Changeset 738
- Timestamp:
- 07/06/07 20:15:22 (2 years ago)
- Files:
-
- asycamore/trunk/asycamore/asycwsgi.py (modified) (1 diff)
- asycamore/trunk/asycamore/commandline.py (modified) (2 diffs)
- asycamore/trunk/asycamore/httpbench.py (modified) (2 diffs)
- asycamore/trunk/asycamore/httpservice.py (modified) (4 diffs)
- asycamore/trunk/asycamore/httpservicecontext.py (modified) (5 diffs)
- asycamore/trunk/asycamore/wsgienviron.py (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
asycamore/trunk/asycamore/asycwsgi.py
r736 r738 101 101 else: 102 102 default_app = wsgiservicectxcls.default_app 103 def connection_started(model, o, event ):103 def connection_started(model, o, event, threadlocal=None): 104 104 return wsgiservicectxcls.connection_started( 105 model, o, event, default_app) 105 model, o, event, default_app, 106 threadlocal=threadlocal 107 ) 106 108 107 109 connection_factory = httpserviceconn.connection_factory asycamore/trunk/asycamore/commandline.py
r733 r738 5 5 from os.path import abspath, normpath, expanduser, join 6 6 from os.path import isfile, exists, isabs 7 8 # Be useful in the absence of asycamore 9 try: 10 from asycamore.exc_string import exc_string 11 except ImportError: 12 import traceback 13 def exc_string(einfo=None): 14 if not einfo: 15 traceback.print_exc() 16 else: 17 traceback.print_exception(*einfo) 7 18 8 19 import logging … … 86 97 log.critical('stopped') 87 98 return -1 88 99 except: 100 msg = exc_string() 101 if not log.isEnabledFor(logging.CRITICAL): 102 print msg 103 else: 104 log.critical(exc_string()) 105 return -1 106 89 107 #----------------------------------------------------------------------------- 90 108 # logging configuration asycamore/trunk/asycamore/httpbench.py
r736 r738 23 23 from tempfile import mkstemp 24 24 from urlparse import urlsplit 25 from asycamore.exc_string import exc_string 25 26 import asycamore.chunkedtransfer as chunkedtransfer 26 27 import asycamore.httpclient as httpclient … … 567 568 if not opts.pdb: 568 569 raise 569 e xc_info = sys.exc_info()570 einfo = sys.exc_info() 570 571 import pdb, traceback 571 traceback.print_exception(*exc_info)572 print exc_string(einfo) 572 573 sys.stderr.write('\nStarting pdb:\n') 573 574 pdb.post_mortem(exc_info[2]) asycamore/trunk/asycamore/httpservice.py
r736 r738 280 280 def __init__(self, **kw): 281 281 282 self.socket_closed = kw['socket_closed']283 282 self.http_pipeline_limit = kw.pop('http_pipeline_limit', 284 283 self.http_pipeline_limit) 285 284 self.multi_accept = kw.pop('multi_accept', self.multi_accept) 286 285 self.threadteam_sz = kw.pop('threadteam_sz', self.threadteam_sz) 286 self.threadlocal = None 287 self.socket_closed = kw['socket_closed'] 288 287 289 self.stat_report_phase = kw.pop('report_phase', self.stat_report_phase) 288 290 … … 306 308 def start_thread_team(self, teamsize, cancel_io_wait): 307 309 assert teamsize > 0 308 310 import threading 309 311 import asycamore.threadpool as threadpool 310 312 … … 316 318 team.set_team_size(teamsize) 317 319 self.threadteam_sz = teamsize 320 self.threadlocal = threading.local() 318 321 319 322 #_test_thread_delegate(self.thread_delegate) … … 603 606 """ 604 607 conn_started = self.connectionfactories[o.server_addr][1] 605 self.connectioncontexts[o.dispatch_id 606 ] = conn_started(self, o, event) 608 if not self.threadteam_sz: 609 self.connectioncontexts[o.dispatch_id 610 ] = conn_started(self, o, event) 611 else: 612 self.connectioncontexts[o.dispatch_id 613 ] = conn_started( 614 self, o, event, self.threadlocal 615 ) 607 616 608 617 asycamore/trunk/asycamore/httpservicecontext.py
r737 r738 56 56 class HTTPServiceContext(object): 57 57 58 def __init__(self, model, o ):58 def __init__(self, model, o, threadlocal): 59 59 60 60 self.is_paused = False … … 64 64 65 65 self.model = model 66 self.cachevars = wsgienviron.initialise_cachevars(threadlocal) 67 66 68 self.o = o 67 69 self.read_sentinel = object() … … 90 92 91 93 @classmethod 92 def connection_started(cls, servicemodel, o, event ):93 return cls(servicemodel, o, event )94 def connection_started(cls, servicemodel, o, event, threadlocal=None): 95 return cls(servicemodel, o, event, threadlocal) 94 96 95 97 … … 377 379 ) 378 380 379 def __init__(self, servicemodel, o, event, wsgi_app=None): 381 def __init__(self, servicemodel, o, event, 382 wsgi_app=None, threadlocal=None): 380 383 super(WSGIServiceContext, self).__init__( 381 servicemodel, o )384 servicemodel, o, threadlocal) 382 385 self.wsgi_app = wsgi_app or self.default_app 383 386 … … 554 557 555 558 @classmethod 556 def connection_started(cls, servicemodel, o, event, wsgi_app=None): 557 return cls(servicemodel, o, event, wsgi_app) 559 def connection_started(cls, servicemodel, o, event, 560 wsgi_app=None, threadlocal=None): 561 return cls(servicemodel, o, event, 562 wsgi_app=wsgi_app, threadlocal=threadlocal 563 ) 558 564 559 565 def start_request(self, d): asycamore/trunk/asycamore/wsgienviron.py
r737 r738 15 15 from urllib import unquote 16 16 from urlparse import urlparse 17 18 # Weekday and month names for HTTP date/time formatting; always English! 19 _weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 20 _monthname = [None, # Dummy so we can use 1-based month numbers 21 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 22 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 23 24 def format_date_time(timestamp): 25 """This is faster than the rfc822 method by ~20%""" 26 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) 27 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 28 _weekdayname[wd], day, _monthname[month], year, hh, mm, ss 29 ) 30 31 32 COMMA_SEPARATED_HEADERS = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING', 33 'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL', 34 'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT', 35 'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE', 36 'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING', 37 'WWW-AUTHENTICATE'] 38 39 def initialise_cachevars(o): 40 """Populate an instance with fresh cache variables. 41 42 Initialisation is idempotent: Only those attributes which are not 43 present in `o` and are necessary to the api in this module are set. If 44 an attribute exists in `o` it is *not* reset. 45 46 By default, some of the api's in this module cache certain results gobaly 47 accross all requests. This may not be what you want, but more importantly 48 it is *not* thread safe. 49 50 This method should be used to ensure proper initialisation of the various 51 cache variables in cases where you want to control what instances are used 52 by those api's that accept a `cachevars` parameter. 53 54 The expected use is for threaded servers, in which case you should pass an 55 instance of threading.local as the `o` parameter. Having done this you 56 should then pass the pertinent attributes of your thread local instance to 57 the apis that accept `_cache` arguments. 58 59 For your convenience, `o` may be none in which case an instance is created 60 for you using ``type('Bunch', (object,), {})``. 61 62 So to setup conditionaly based on threading being enabled do something 63 like:: 64 65 cachevars = None 66 if threading_enabled: 67 cachevars = threading.local() 68 cachevars = initialise_cachevars(cachevars) 69 70 And to call `populate_server_environ` without having to worry about 71 whether threading is enabled or not do:: 72 73 populate_server_environ(environ, request_line, cachevars) 74 75 NOTE: calling initialise_cachevars a second time, on the same instance, 76 will *not* reset the attributes. 77 78 >>> o = initialise_cachevars() 79 >>> a1 = o.encoded_path 80 >>> a1['/foo']='/bar' 81 >>> o2 = initialise_cachevars(o) 82 >>> assert o is o2 and a1 is o2.encoded_path 83 >>> assert o2.encoded_path['/foo'] == '/bar' 84 85 """ 86 87 if o is None: 88 o = type('Bunch', (object,), {}) 89 90 for dictvar in 'encoded_path resource_path http_k'.split(): 91 if not hasattr(o, dictvar): 92 setattr(o, dictvar, {}) 93 94 if not hasattr(o, 'date'): 95 t = time.time() 96 o.date = (t, format_date_time(t)) 97 if not hasattr(o, 'comma_separated_headers'): 98 o.comma_separated_headers = frozenset([ 99 'HTTP_' + k.replace('-', '_') for k in COMMA_SEPARATED_HEADERS] 100 ) 101 return o 102 103 _cachevars = initialise_cachevars(None) 17 104 18 105 … … 42 129 _resource_path_cache={} 43 130 44 def populate_server_environ(environ, request_line, 45 quoted_slash=quoted_slash, 46 server_software='ASYCAMORE-WSGI/dev', 47 _encoded_path_cache=_encoded_path_cache 48 ): 131 def populate_server_environ(environ, request_line, cachevars=_cachevars, 132 quoted_slash=quoted_slash, server_software='ASYCAMORE-WSGI/dev'): 49 133 """`consider_request_lin` without app selection.""" 50 134 … … 87 171 # before the escaped characters within those components can be 88 172 # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2 89 if path in _encoded_path_cache:90 path = _encoded_path_cache[path]173 if path in cachevars.encoded_path: 174 path = cachevars.encoded_path[path] 91 175 else: 92 176 atoms = [unquote(x) for x in quoted_slash.split(path)] 93 path = _encoded_path_cache[path] = "%2F".join(atoms)177 path = cachevars.encoded_path[path] = "%2F".join(atoms) 94 178 environ["SCRIPT_NAME"] = "" 95 179 environ["PATH_INFO"] = path … … 119 203 120 204 121 # Weekday and month names for HTTP date/time formatting; always English! 122 _weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] 123 _monthname = [None, # Dummy so we can use 1-based month numbers 124 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 125 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 126 127 def format_date_time(timestamp): 128 """This is faster than the rfc822 method by ~20%""" 129 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) 130 return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 131 _weekdayname[wd], day, _monthname[month], year, hh, mm, ss 132 ) 133 134 _cached_date=time.time() 135 _cached_date=(_cached_date, format_date_time(_cached_date)) 136 137 def process_message_headers(environ, headers): 205 def process_message_headers(environ, headers, cachevars=_cachevars): 138 206 139 207 """Populate environ keys that are derived from the request headers. … … 144 212 """ 145 213 146 global _cached_date147 148 214 try: 149 consume_headers(environ, headers) 215 _consume_headers(environ, headers, 216 cachevars.comma_separated_headers, cachevars.http_k) 150 217 except ValueError, e: 151 218 response = build_simple_response( … … 154 221 return response 155 222 223 # Updating the date stamp at a sensible frequency makes a surprising 224 # difference to the over all request rate when the server is under load. 156 225 now=time.time() 157 if now - _cached_date[0] > 0.999:226 if now - cachevars.date[0] > 0.999: 158 227 Date = format_date_time(now) 159 _cached_date=(now, Date) 160 else: 161 Date = _cached_date[1] 228 cachevars.date=(now, Date) 229 else: 230 Date = cachevars.date[1] 231 162 232 response_headers=[ 163 233 ('Server', environ["SERVER_SOFTWARE"]), … … 204 274 205 275 cl = environ.get("CONTENT_LENGTH") 206 if (method == "POST" or method == "PUT") and not read_chunked and cl is None: 276 if ((method == "POST" or method == "PUT") 277 and not read_chunked and cl is None): 207 278 # No Content-Length header supplied. This will hang 208 279 # cgi.FieldStorage, since it cannot determine when to … … 237 308 return response_headers 238 309 239 240 COMMA_SEPARATED_HEADERS = ['ACCEPT', 'ACCEPT-CHARSET', 'ACCEPT-ENCODING', 241 'ACCEPT-LANGUAGE', 'ACCEPT-RANGES', 'ALLOW', 'CACHE-CONTROL', 242 'CONNECTION', 'CONTENT-ENCODING', 'CONTENT-LANGUAGE', 'EXPECT', 243 'IF-MATCH', 'IF-NONE-MATCH', 'PRAGMA', 'PROXY-AUTHENTICATE', 'TE', 244 'TRAILER', 'TRANSFER-ENCODING', 'UPGRADE', 'VARY', 'VIA', 'WARNING', 245 'WWW-AUTHENTICATE'] 246 _http_k_cache=dict() 247 def consume_headers(environ, messageheaders, 248 COMMA_SEPARATED_HEADERS=frozenset([ 249 'HTTP_' + k.replace('-', '_') for k in COMMA_SEPARATED_HEADERS]), 250 _http_k_cache=_http_k_cache): 310 def consume_headers(environ, messageheaders, cachevars=_cachevars): 311 return _consume_headers(environ, messageheaders, 312 cachevars.comma_separated_headers, cachevars.http_k 313 ) 314 315 def _consume_headers(environ, messageheaders, 316 comma_separated_headers, http_k_cache): 251 317 252 318 """Set up the header keys in environ using the provided Message-Headers … … 286 352 # the rest of this function. and this section of code is very 287 353 # high on the profile when stressing request through put. 288 HTTP_K= _http_k_cache.get(k, None)354 HTTP_K=http_k_cache.get(k, None) 289 355 if not HTTP_K: 290 356 HTTP_K = 'HTTP_' + k.strip().upper().replace('-', '_') 291 _http_k_cache[k]=HTTP_K357 http_k_cache[k]=HTTP_K 292 358 293 359 if HTTP_K in COMMA_SEPARATED_HEADERS: … … 325 391 return 'http' 326 392 327 # EOF393 #