]> review.fuel-infra Code Review - packages/trusty/python-eventlet.git/blob - eventlet/eventlet/wsgi.py
Added python-eventlet 0.15.2 for Ubuntu 14.04
[packages/trusty/python-eventlet.git] / eventlet / eventlet / wsgi.py
1 import errno
2 import os
3 import sys
4 import time
5 import traceback
6 import types
7 import warnings
8
9 from eventlet.green import BaseHTTPServer
10 from eventlet.green import socket
11 from eventlet.green import urllib
12 from eventlet import greenio
13 from eventlet import greenpool
14 from eventlet import support
15 from eventlet.support import six
16
17
18 DEFAULT_MAX_SIMULTANEOUS_REQUESTS = 1024
19 DEFAULT_MAX_HTTP_VERSION = 'HTTP/1.1'
20 MAX_REQUEST_LINE = 8192
21 MAX_HEADER_LINE = 8192
22 MAX_TOTAL_HEADER_SIZE = 65536
23 MINIMUM_CHUNK_SIZE = 4096
24 # %(client_port)s is also available
25 DEFAULT_LOG_FORMAT = ('%(client_ip)s - - [%(date_time)s] "%(request_line)s"'
26                       ' %(status_code)s %(body_length)s %(wall_seconds).6f')
27
28 __all__ = ['server', 'format_date_time']
29
30 # Weekday and month names for HTTP date/time formatting; always English!
31 _weekdayname = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
32 _monthname = [None,  # Dummy so we can use 1-based month numbers
33               "Jan", "Feb", "Mar", "Apr", "May", "Jun",
34               "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
35
36
37 def format_date_time(timestamp):
38     """Formats a unix timestamp into an HTTP standard string."""
39     year, month, day, hh, mm, ss, wd, _y, _z = time.gmtime(timestamp)
40     return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % (
41         _weekdayname[wd], day, _monthname[month], year, hh, mm, ss
42     )
43
44
45 # Collections of error codes to compare against.  Not all attributes are set
46 # on errno module on all platforms, so some are literals :(
47 BAD_SOCK = set((errno.EBADF, 10053))
48 BROKEN_SOCK = set((errno.EPIPE, errno.ECONNRESET))
49
50
51 # special flag return value for apps
52 class _AlreadyHandled(object):
53
54     def __iter__(self):
55         return self
56
57     def next(self):
58         raise StopIteration
59
60     __next__ = next
61
62
63 ALREADY_HANDLED = _AlreadyHandled()
64
65
66 class Input(object):
67
68     def __init__(self,
69                  rfile,
70                  content_length,
71                  wfile=None,
72                  wfile_line=None,
73                  chunked_input=False):
74
75         self.rfile = rfile
76         if content_length is not None:
77             content_length = int(content_length)
78         self.content_length = content_length
79
80         self.wfile = wfile
81         self.wfile_line = wfile_line
82
83         self.position = 0
84         self.chunked_input = chunked_input
85         self.chunk_length = -1
86
87         # (optional) headers to send with a "100 Continue" response. Set by
88         # calling set_hundred_continue_respose_headers() on env['wsgi.input']
89         self.hundred_continue_headers = None
90
91     def _send_hundred_continue_response(self):
92         towrite = []
93
94         # 100 Continue status line
95         towrite.append(self.wfile_line)
96
97         # Optional headers
98         if self.hundred_continue_headers is not None:
99             # 100 Continue headers
100             for header in self.hundred_continue_headers:
101                 towrite.append('%s: %s\r\n' % header)
102
103         # Blank line
104         towrite.append('\r\n')
105
106         self.wfile.writelines(towrite)
107         self.wfile = None
108         self.wfile_line = None
109
110     def _do_read(self, reader, length=None):
111         if self.wfile is not None:
112             # 100 Continue response
113             self._send_hundred_continue_response()
114         if length is None and self.content_length is not None:
115             length = self.content_length - self.position
116         if length and length > self.content_length - self.position:
117             length = self.content_length - self.position
118         if not length:
119             return ''
120         try:
121             read = reader(length)
122         except greenio.SSL.ZeroReturnError:
123             read = ''
124         self.position += len(read)
125         return read
126
127     def _chunked_read(self, rfile, length=None, use_readline=False):
128         if self.wfile is not None:
129             # 100 Continue response
130             self._send_hundred_continue_response()
131         try:
132             if length == 0:
133                 return ""
134
135             if length < 0:
136                 length = None
137
138             if use_readline:
139                 reader = self.rfile.readline
140             else:
141                 reader = self.rfile.read
142
143             response = []
144             while self.chunk_length != 0:
145                 maxreadlen = self.chunk_length - self.position
146                 if length is not None and length < maxreadlen:
147                     maxreadlen = length
148
149                 if maxreadlen > 0:
150                     data = reader(maxreadlen)
151                     if not data:
152                         self.chunk_length = 0
153                         raise IOError("unexpected end of file while parsing chunked data")
154
155                     datalen = len(data)
156                     response.append(data)
157
158                     self.position += datalen
159                     if self.chunk_length == self.position:
160                         rfile.readline()
161
162                     if length is not None:
163                         length -= datalen
164                         if length == 0:
165                             break
166                     if use_readline and data[-1] == "\n":
167                         break
168                 else:
169                     self.chunk_length = int(rfile.readline().split(b";", 1)[0], 16)
170                     self.position = 0
171                     if self.chunk_length == 0:
172                         rfile.readline()
173         except greenio.SSL.ZeroReturnError:
174             pass
175         return b''.join(response)
176
177     def read(self, length=None):
178         if self.chunked_input:
179             return self._chunked_read(self.rfile, length)
180         return self._do_read(self.rfile.read, length)
181
182     def readline(self, size=None):
183         if self.chunked_input:
184             return self._chunked_read(self.rfile, size, True)
185         else:
186             return self._do_read(self.rfile.readline, size)
187
188     def readlines(self, hint=None):
189         return self._do_read(self.rfile.readlines, hint)
190
191     def __iter__(self):
192         return iter(self.read, '')
193
194     def get_socket(self):
195         return self.rfile._sock
196
197     def set_hundred_continue_response_headers(self, headers,
198                                               capitalize_response_headers=True):
199         # Response headers capitalization (default)
200         # CONTent-TYpe: TExt/PlaiN -> Content-Type: TExt/PlaiN
201         # Per HTTP RFC standard, header name is case-insensitive.
202         # Please, fix your client to ignore header case if possible.
203         if capitalize_response_headers:
204             headers = [
205                 ('-'.join([x.capitalize() for x in key.split('-')]), value)
206                 for key, value in headers]
207         self.hundred_continue_headers = headers
208
209
210 class HeaderLineTooLong(Exception):
211     pass
212
213
214 class HeadersTooLarge(Exception):
215     pass
216
217
218 class FileObjectForHeaders(object):
219
220     def __init__(self, fp):
221         self.fp = fp
222         self.total_header_size = 0
223
224     def readline(self, size=-1):
225         sz = size
226         if size < 0:
227             sz = MAX_HEADER_LINE
228         rv = self.fp.readline(sz)
229         if size < 0 and len(rv) >= MAX_HEADER_LINE:
230             raise HeaderLineTooLong()
231         self.total_header_size += len(rv)
232         if self.total_header_size > MAX_TOTAL_HEADER_SIZE:
233             raise HeadersTooLarge()
234         return rv
235
236
237 class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
238     protocol_version = 'HTTP/1.1'
239     minimum_chunk_size = MINIMUM_CHUNK_SIZE
240     capitalize_response_headers = True
241
242     def setup(self):
243         # overriding SocketServer.setup to correctly handle SSL.Connection objects
244         conn = self.connection = self.request
245         try:
246             self.rfile = conn.makefile('rb', self.rbufsize)
247             self.wfile = conn.makefile('wb', self.wbufsize)
248         except (AttributeError, NotImplementedError):
249             if hasattr(conn, 'send') and hasattr(conn, 'recv'):
250                 # it's an SSL.Connection
251                 self.rfile = socket._fileobject(conn, "rb", self.rbufsize)
252                 self.wfile = socket._fileobject(conn, "wb", self.wbufsize)
253             else:
254                 # it's a SSLObject, or a martian
255                 raise NotImplementedError("wsgi.py doesn't support sockets "
256                                           "of type %s" % type(conn))
257
258     def handle_one_request(self):
259         if self.server.max_http_version:
260             self.protocol_version = self.server.max_http_version
261
262         if self.rfile.closed:
263             self.close_connection = 1
264             return
265
266         try:
267             self.raw_requestline = self.rfile.readline(self.server.url_length_limit)
268             if len(self.raw_requestline) == self.server.url_length_limit:
269                 self.wfile.write(
270                     "HTTP/1.0 414 Request URI Too Long\r\n"
271                     "Connection: close\r\nContent-length: 0\r\n\r\n")
272                 self.close_connection = 1
273                 return
274         except greenio.SSL.ZeroReturnError:
275             self.raw_requestline = ''
276         except socket.error as e:
277             if support.get_errno(e) not in BAD_SOCK:
278                 raise
279             self.raw_requestline = ''
280
281         if not self.raw_requestline:
282             self.close_connection = 1
283             return
284
285         orig_rfile = self.rfile
286         try:
287             self.rfile = FileObjectForHeaders(self.rfile)
288             if not self.parse_request():
289                 return
290         except HeaderLineTooLong:
291             self.wfile.write(
292                 "HTTP/1.0 400 Header Line Too Long\r\n"
293                 "Connection: close\r\nContent-length: 0\r\n\r\n")
294             self.close_connection = 1
295             return
296         except HeadersTooLarge:
297             self.wfile.write(
298                 "HTTP/1.0 400 Headers Too Large\r\n"
299                 "Connection: close\r\nContent-length: 0\r\n\r\n")
300             self.close_connection = 1
301             return
302         finally:
303             self.rfile = orig_rfile
304
305         content_length = self.headers.get('content-length')
306         if content_length:
307             try:
308                 int(content_length)
309             except ValueError:
310                 self.wfile.write(
311                     "HTTP/1.0 400 Bad Request\r\n"
312                     "Connection: close\r\nContent-length: 0\r\n\r\n")
313                 self.close_connection = 1
314                 return
315
316         self.environ = self.get_environ()
317         self.application = self.server.app
318         try:
319             self.server.outstanding_requests += 1
320             try:
321                 self.handle_one_response()
322             except socket.error as e:
323                 # Broken pipe, connection reset by peer
324                 if support.get_errno(e) not in BROKEN_SOCK:
325                     raise
326         finally:
327             self.server.outstanding_requests -= 1
328
329     def handle_one_response(self):
330         start = time.time()
331         headers_set = []
332         headers_sent = []
333
334         wfile = self.wfile
335         result = None
336         use_chunked = [False]
337         length = [0]
338         status_code = [200]
339
340         def write(data, _writelines=wfile.writelines):
341             towrite = []
342             if not headers_set:
343                 raise AssertionError("write() before start_response()")
344             elif not headers_sent:
345                 status, response_headers = headers_set
346                 headers_sent.append(1)
347                 header_list = [header[0].lower() for header in response_headers]
348                 towrite.append('%s %s\r\n' % (self.protocol_version, status))
349                 for header in response_headers:
350                     towrite.append('%s: %s\r\n' % header)
351
352                 # send Date header?
353                 if 'date' not in header_list:
354                     towrite.append('Date: %s\r\n' % (format_date_time(time.time()),))
355
356                 client_conn = self.headers.get('Connection', '').lower()
357                 send_keep_alive = False
358                 if self.close_connection == 0 and \
359                    self.server.keepalive and (client_conn == 'keep-alive' or
360                                               (self.request_version == 'HTTP/1.1' and
361                                                not client_conn == 'close')):
362                         # only send keep-alives back to clients that sent them,
363                         # it's redundant for 1.1 connections
364                     send_keep_alive = (client_conn == 'keep-alive')
365                     self.close_connection = 0
366                 else:
367                     self.close_connection = 1
368
369                 if 'content-length' not in header_list:
370                     if self.request_version == 'HTTP/1.1':
371                         use_chunked[0] = True
372                         towrite.append('Transfer-Encoding: chunked\r\n')
373                     elif 'content-length' not in header_list:
374                         # client is 1.0 and therefore must read to EOF
375                         self.close_connection = 1
376
377                 if self.close_connection:
378                     towrite.append('Connection: close\r\n')
379                 elif send_keep_alive:
380                     towrite.append('Connection: keep-alive\r\n')
381                 towrite.append('\r\n')
382                 # end of header writing
383
384             if use_chunked[0]:
385                 # Write the chunked encoding
386                 towrite.append("%x\r\n%s\r\n" % (len(data), data))
387             else:
388                 towrite.append(data)
389             try:
390                 _writelines(towrite)
391                 length[0] = length[0] + sum(map(len, towrite))
392             except UnicodeEncodeError:
393                 self.server.log_message(
394                     "Encountered non-ascii unicode while attempting to write"
395                     "wsgi response: %r" %
396                     [x for x in towrite if isinstance(x, six.text_type)])
397                 self.server.log_message(traceback.format_exc())
398                 _writelines(
399                     ["HTTP/1.1 500 Internal Server Error\r\n",
400                      "Connection: close\r\n",
401                      "Content-type: text/plain\r\n",
402                      "Content-length: 98\r\n",
403                      "Date: %s\r\n" % format_date_time(time.time()),
404                      "\r\n",
405                      ("Internal Server Error: wsgi application passed "
406                       "a unicode object to the server instead of a string.")])
407
408         def start_response(status, response_headers, exc_info=None):
409             status_code[0] = status.split()[0]
410             if exc_info:
411                 try:
412                     if headers_sent:
413                         # Re-raise original exception if headers sent
414                         six.reraise(exc_info[0], exc_info[1], exc_info[2])
415                 finally:
416                     # Avoid dangling circular ref
417                     exc_info = None
418
419             # Response headers capitalization
420             # CONTent-TYpe: TExt/PlaiN -> Content-Type: TExt/PlaiN
421             # Per HTTP RFC standard, header name is case-insensitive.
422             # Please, fix your client to ignore header case if possible.
423             if self.capitalize_response_headers:
424                 response_headers = [
425                     ('-'.join([x.capitalize() for x in key.split('-')]), value)
426                     for key, value in response_headers]
427
428             headers_set[:] = [status, response_headers]
429             return write
430
431         try:
432             try:
433                 result = self.application(self.environ, start_response)
434                 if (isinstance(result, _AlreadyHandled)
435                         or isinstance(getattr(result, '_obj', None), _AlreadyHandled)):
436                     self.close_connection = 1
437                     return
438
439                 # Set content-length if possible
440                 if not headers_sent and hasattr(result, '__len__') and \
441                         'Content-Length' not in [h for h, _v in headers_set[1]]:
442                     headers_set[1].append(('Content-Length', str(sum(map(len, result)))))
443
444                 towrite = []
445                 towrite_size = 0
446                 just_written_size = 0
447                 minimum_write_chunk_size = int(self.environ.get(
448                     'eventlet.minimum_write_chunk_size', self.minimum_chunk_size))
449                 for data in result:
450                     towrite.append(data)
451                     towrite_size += len(data)
452                     if towrite_size >= minimum_write_chunk_size:
453                         write(''.join(towrite))
454                         towrite = []
455                         just_written_size = towrite_size
456                         towrite_size = 0
457                 if towrite:
458                     just_written_size = towrite_size
459                     write(''.join(towrite))
460                 if not headers_sent or (use_chunked[0] and just_written_size):
461                     write('')
462             except Exception:
463                 self.close_connection = 1
464                 tb = traceback.format_exc()
465                 self.server.log_message(tb)
466                 if not headers_set:
467                     err_body = ""
468                     if(self.server.debug):
469                         err_body = tb
470                     start_response("500 Internal Server Error",
471                                    [('Content-type', 'text/plain'),
472                                     ('Content-length', len(err_body))])
473                     write(err_body)
474         finally:
475             if hasattr(result, 'close'):
476                 result.close()
477             if (self.environ['eventlet.input'].chunked_input or
478                     self.environ['eventlet.input'].position
479                     < self.environ['eventlet.input'].content_length):
480                 # Read and discard body if there was no pending 100-continue
481                 if not self.environ['eventlet.input'].wfile:
482                     # NOTE: MINIMUM_CHUNK_SIZE is used here for purpose different than chunking.
483                     # We use it only cause it's at hand and has reasonable value in terms of
484                     # emptying the buffer.
485                     while self.environ['eventlet.input'].read(MINIMUM_CHUNK_SIZE):
486                         pass
487             finish = time.time()
488
489             for hook, args, kwargs in self.environ['eventlet.posthooks']:
490                 hook(self.environ, *args, **kwargs)
491
492             if self.server.log_output:
493                 self.server.log_message(self.server.log_format % {
494                     'client_ip': self.get_client_ip(),
495                     'client_port': self.client_address[1],
496                     'date_time': self.log_date_time_string(),
497                     'request_line': self.requestline,
498                     'status_code': status_code[0],
499                     'body_length': length[0],
500                     'wall_seconds': finish - start,
501                 })
502
503     def get_client_ip(self):
504         client_ip = self.client_address[0]
505         if self.server.log_x_forwarded_for:
506             forward = self.headers.get('X-Forwarded-For', '').replace(' ', '')
507             if forward:
508                 client_ip = "%s,%s" % (forward, client_ip)
509         return client_ip
510
511     def get_environ(self):
512         env = self.server.get_environ()
513         env['REQUEST_METHOD'] = self.command
514         env['SCRIPT_NAME'] = ''
515
516         pq = self.path.split('?', 1)
517         env['RAW_PATH_INFO'] = pq[0]
518         env['PATH_INFO'] = urllib.unquote(pq[0])
519         if len(pq) > 1:
520             env['QUERY_STRING'] = pq[1]
521
522         ct = self.headers.get('content-type')
523         if ct is None:
524             try:
525                 ct = self.headers.type
526             except AttributeError:
527                 ct = self.headers.get_content_type()
528         env['CONTENT_TYPE'] = ct
529
530         length = self.headers.get('content-length')
531         if length:
532             env['CONTENT_LENGTH'] = length
533         env['SERVER_PROTOCOL'] = 'HTTP/1.0'
534
535         host, port = self.request.getsockname()[:2]
536         env['SERVER_NAME'] = host
537         env['SERVER_PORT'] = str(port)
538         env['REMOTE_ADDR'] = self.client_address[0]
539         env['REMOTE_PORT'] = str(self.client_address[1])
540         env['GATEWAY_INTERFACE'] = 'CGI/1.1'
541
542         try:
543             headers = self.headers.headers
544         except AttributeError:
545             headers = self.headers._headers
546         else:
547             headers = [h.split(':', 1) for h in headers]
548
549         for k, v in headers:
550             k = k.replace('-', '_').upper()
551             v = v.strip()
552             if k in env:
553                 continue
554             envk = 'HTTP_' + k
555             if envk in env:
556                 env[envk] += ',' + v
557             else:
558                 env[envk] = v
559
560         if env.get('HTTP_EXPECT') == '100-continue':
561             wfile = self.wfile
562             wfile_line = 'HTTP/1.1 100 Continue\r\n'
563         else:
564             wfile = None
565             wfile_line = None
566         chunked = env.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked'
567         env['wsgi.input'] = env['eventlet.input'] = Input(
568             self.rfile, length, wfile=wfile, wfile_line=wfile_line,
569             chunked_input=chunked)
570         env['eventlet.posthooks'] = []
571
572         return env
573
574     def finish(self):
575         try:
576             BaseHTTPServer.BaseHTTPRequestHandler.finish(self)
577         except socket.error as e:
578             # Broken pipe, connection reset by peer
579             if support.get_errno(e) not in BROKEN_SOCK:
580                 raise
581         greenio.shutdown_safe(self.connection)
582         self.connection.close()
583
584
585 class Server(BaseHTTPServer.HTTPServer):
586
587     def __init__(self,
588                  socket,
589                  address,
590                  app,
591                  log=None,
592                  environ=None,
593                  max_http_version=None,
594                  protocol=HttpProtocol,
595                  minimum_chunk_size=None,
596                  log_x_forwarded_for=True,
597                  keepalive=True,
598                  log_output=True,
599                  log_format=DEFAULT_LOG_FORMAT,
600                  url_length_limit=MAX_REQUEST_LINE,
601                  debug=True,
602                  socket_timeout=None,
603                  capitalize_response_headers=True):
604
605         self.outstanding_requests = 0
606         self.socket = socket
607         self.address = address
608         if log:
609             self.log = log
610         else:
611             self.log = sys.stderr
612         self.app = app
613         self.keepalive = keepalive
614         self.environ = environ
615         self.max_http_version = max_http_version
616         self.protocol = protocol
617         self.pid = os.getpid()
618         self.minimum_chunk_size = minimum_chunk_size
619         self.log_x_forwarded_for = log_x_forwarded_for
620         self.log_output = log_output
621         self.log_format = log_format
622         self.url_length_limit = url_length_limit
623         self.debug = debug
624         self.socket_timeout = socket_timeout
625         self.capitalize_response_headers = capitalize_response_headers
626
627         if not self.capitalize_response_headers:
628             warnings.warn("""capitalize_response_headers is disabled.
629  Please, make sure you know what you are doing.
630  HTTP headers names are case-insensitive per RFC standard.
631  Most likely, you need to fix HTTP parsing in your client software.""",
632                           DeprecationWarning, stacklevel=3)
633
634     def get_environ(self):
635         d = {
636             'wsgi.errors': sys.stderr,
637             'wsgi.version': (1, 0),
638             'wsgi.multithread': True,
639             'wsgi.multiprocess': False,
640             'wsgi.run_once': False,
641             'wsgi.url_scheme': 'http',
642         }
643         # detect secure socket
644         if hasattr(self.socket, 'do_handshake'):
645             d['wsgi.url_scheme'] = 'https'
646             d['HTTPS'] = 'on'
647         if self.environ is not None:
648             d.update(self.environ)
649         return d
650
651     def process_request(self, sock_params):
652         # The actual request handling takes place in __init__, so we need to
653         # set minimum_chunk_size before __init__ executes and we don't want to modify
654         # class variable
655         sock, address = sock_params
656         proto = new(self.protocol)
657         if self.minimum_chunk_size is not None:
658             proto.minimum_chunk_size = self.minimum_chunk_size
659         proto.capitalize_response_headers = self.capitalize_response_headers
660         try:
661             proto.__init__(sock, address, self)
662         except socket.timeout:
663             # Expected exceptions are not exceptional
664             sock.close()
665             if self.debug:
666                 # similar to logging "accepted" in server()
667                 self.log_message('(%s) timed out %r' % (self.pid, address))
668
669     def log_message(self, message):
670         self.log.write(message + '\n')
671
672
673 try:
674     new = types.InstanceType
675 except AttributeError:
676     new = lambda cls: cls.__new__(cls)
677
678
679 try:
680     import ssl
681     ACCEPT_EXCEPTIONS = (socket.error, ssl.SSLError)
682     ACCEPT_ERRNO = set((errno.EPIPE, errno.EBADF, errno.ECONNRESET,
683                         ssl.SSL_ERROR_EOF, ssl.SSL_ERROR_SSL))
684 except ImportError:
685     ACCEPT_EXCEPTIONS = (socket.error,)
686     ACCEPT_ERRNO = set((errno.EPIPE, errno.EBADF, errno.ECONNRESET))
687
688
689 def server(sock, site,
690            log=None,
691            environ=None,
692            max_size=None,
693            max_http_version=DEFAULT_MAX_HTTP_VERSION,
694            protocol=HttpProtocol,
695            server_event=None,
696            minimum_chunk_size=None,
697            log_x_forwarded_for=True,
698            custom_pool=None,
699            keepalive=True,
700            log_output=True,
701            log_format=DEFAULT_LOG_FORMAT,
702            url_length_limit=MAX_REQUEST_LINE,
703            debug=True,
704            socket_timeout=None,
705            capitalize_response_headers=True):
706     """Start up a WSGI server handling requests from the supplied server
707     socket.  This function loops forever.  The *sock* object will be
708     closed after server exits, but the underlying file descriptor will
709     remain open, so if you have a dup() of *sock*, it will remain usable.
710
711     :param sock: Server socket, must be already bound to a port and listening.
712     :param site: WSGI application function.
713     :param log: File-like object that logs should be written to.
714                 If not specified, sys.stderr is used.
715     :param environ: Additional parameters that go into the environ dictionary of every request.
716     :param max_size: Maximum number of client connections opened at any time by this server.
717     :param max_http_version: Set to "HTTP/1.0" to make the server pretend it only supports HTTP 1.0.
718                 This can help with applications or clients that don't behave properly using HTTP 1.1.
719     :param protocol: Protocol class.  Deprecated.
720     :param server_event: Used to collect the Server object.  Deprecated.
721     :param minimum_chunk_size: Minimum size in bytes for http chunks.  This can be used to improve
722                 performance of applications which yield many small strings, though
723                 using it technically violates the WSGI spec. This can be overridden
724                 on a per request basis by setting environ['eventlet.minimum_write_chunk_size'].
725     :param log_x_forwarded_for: If True (the default), logs the contents of the x-forwarded-for
726                 header in addition to the actual client ip address in the 'client_ip' field of the
727                 log line.
728     :param custom_pool: A custom GreenPool instance which is used to spawn client green threads.
729                 If this is supplied, max_size is ignored.
730     :param keepalive: If set to False, disables keepalives on the server; all connections will be
731                 closed after serving one request.
732     :param log_output: A Boolean indicating if the server will log data or not.
733     :param log_format: A python format string that is used as the template to generate log lines.
734                 The following values can be formatted into it: client_ip, date_time, request_line,
735                 status_code, body_length, wall_seconds.  The default is a good example of how to
736                 use it.
737     :param url_length_limit: A maximum allowed length of the request url. If exceeded, 414 error
738                 is returned.
739     :param debug: True if the server should send exception tracebacks to the clients on 500 errors.
740                 If False, the server will respond with empty bodies.
741     :param socket_timeout: Timeout for client connections' socket operations. Default None means
742                 wait forever.
743     :param capitalize_response_headers: Normalize response headers' names to Foo-Bar.
744                 Default is True.
745     """
746     serv = Server(sock, sock.getsockname(),
747                   site, log,
748                   environ=environ,
749                   max_http_version=max_http_version,
750                   protocol=protocol,
751                   minimum_chunk_size=minimum_chunk_size,
752                   log_x_forwarded_for=log_x_forwarded_for,
753                   keepalive=keepalive,
754                   log_output=log_output,
755                   log_format=log_format,
756                   url_length_limit=url_length_limit,
757                   debug=debug,
758                   socket_timeout=socket_timeout,
759                   capitalize_response_headers=capitalize_response_headers,
760                   )
761     if server_event is not None:
762         server_event.send(serv)
763     if max_size is None:
764         max_size = DEFAULT_MAX_SIMULTANEOUS_REQUESTS
765     if custom_pool is not None:
766         pool = custom_pool
767     else:
768         pool = greenpool.GreenPool(max_size)
769     try:
770         host, port = sock.getsockname()[:2]
771         port = ':%s' % (port, )
772         if hasattr(sock, 'do_handshake'):
773             scheme = 'https'
774             if port == ':443':
775                 port = ''
776         else:
777             scheme = 'http'
778             if port == ':80':
779                 port = ''
780
781         serv.log.write("(%s) wsgi starting up on %s://%s%s/\n" % (
782             serv.pid, scheme, host, port))
783         while True:
784             try:
785                 client_socket = sock.accept()
786                 client_socket[0].settimeout(serv.socket_timeout)
787                 if debug:
788                     serv.log.write("(%s) accepted %r\n" % (
789                         serv.pid, client_socket[1]))
790                 try:
791                     pool.spawn_n(serv.process_request, client_socket)
792                 except AttributeError:
793                     warnings.warn("wsgi's pool should be an instance of "
794                                   "eventlet.greenpool.GreenPool, is %s. Please convert your"
795                                   " call site to use GreenPool instead" % type(pool),
796                                   DeprecationWarning, stacklevel=2)
797                     pool.execute_async(serv.process_request, client_socket)
798             except ACCEPT_EXCEPTIONS as e:
799                 if support.get_errno(e) not in ACCEPT_ERRNO:
800                     raise
801             except (KeyboardInterrupt, SystemExit):
802                 serv.log.write("wsgi exiting\n")
803                 break
804     finally:
805         try:
806             # NOTE: It's not clear whether we want this to leave the
807             # socket open or close it.  Use cases like Spawning want
808             # the underlying fd to remain open, but if we're going
809             # that far we might as well not bother closing sock at
810             # all.
811             sock.close()
812         except socket.error as e:
813             if support.get_errno(e) not in BROKEN_SOCK:
814                 traceback.print_exc()