def httplib_normalize_headers(response_headers, skip_headers=[]): """return (headers, content_encoding, transfer_encoding)""" headers = [] for keyword, value in response_headers: keyword = keyword.title() if keyword in skip_headers: continue if keyword == 'Connection': headers.append(('Connection', 'close')) elif keyword != 'Set-Cookie': headers.append((keyword, value)) else: scs = value.split(', ') cookies = [] i = -1 for sc in scs: if re.match(r'[^ =]+ ', sc): try: cookies[i] = '%s, %s' % (cookies[i], sc) except IndexError: pass else: cookies.append(sc) i += 1 headers += [('Set-Cookie', x) for x in cookies] return headers
def write(self): """ Begins the transmission of the response. The lightly-internal `start_response` attribute **MUST** be manually set on the object **BEFORE** calling this method! This callable is called during execution to set the status line & headers of the response. Returns: iterable: An iterable of the content Raises: ResponseFailed: If no `start_response` was set before calling. """ if not self.start_response: raise ResponseFailed( "{}.write called before being provided a callable".format( self.__class__.__name__)) status = "{} {}".format( self.status_code, RESPONSE_CODES.get(self.status_code, RESPONSE_CODES[500]), ) headers = [(k, v) for k, v in self.headers.items()] possible_cookies = self._cookies.output() # Update the headers to include the cookies. if possible_cookies: for line in possible_cookies.splitlines(): headers.append(tuple(line.split(": ", 1))) self.start_response(status, headers) return [self.body.encode("utf-8")]
def send_wsgi_response(status, headers, content, start_response, cors_handler=None): """Dump reformatted response to CGI start_response. This calls start_response and returns the response body. Args: status: A string containing the HTTP status code to send. headers: A list of (header, value) tuples, the headers to send in the response. content: A string containing the body content to write. start_response: A function with semantics defined in PEP-333. cors_handler: A handler to process CORS request headers and update the headers in the response. Or this can be None, to bypass CORS checks. Returns: A string containing the response body. """ if cors_handler: cors_handler.update_headers(headers) # Update content length. content_len = len(content) if content else 0 headers = [(header, value) for header, value in headers if header.lower() != 'content-length'] headers.append(('Content-Length', '%s' % content_len)) start_response(status, headers) return content
def httplib_normalize_headers(response_headers, skip_headers=[]): """return (headers, content_encoding, transfer_encoding)""" headers = [] for keyword, value in response_headers: keyword = keyword.title() if keyword in skip_headers: continue if keyword == "Connection": headers.append(("Connection", "close")) elif keyword != "Set-Cookie": headers.append((keyword, value)) else: scs = value.split(", ") cookies = [] i = -1 for sc in scs: if re.match(r"[^ =]+ ", sc): try: cookies[i] = "%s, %s" % (cookies[i], sc) except IndexError: pass else: cookies.append(sc) i += 1 headers += [("Set-Cookie", x) for x in cookies] return headers
def print_response(response): """Parses and prints the Analytics Reporting API V4 response""" for report in response.get('reports', []): columnHeader = report.get('columnHeader', {}) dimensionHeaders = columnHeader.get('dimensions', []) metricHeaders = columnHeader.get('metricHeader', {}).get('metricHeaderEntries', []) rows = report.get('data', {}).get('rows', []) #print(rows) for row in rows: dimensions = row.get('dimensions', []) dateRangeValues = row.get('metrics', []) #print(row) headers = [] for header, dimension in zip(dimensionHeaders, dimensions): headers.append(header) #print ('数组:'+headers[1]) for i, values in enumerate(dateRangeValues): #print ('Date range (' + str(i) + ')') for metricHeader, value in zip(metricHeaders, values.get('values')): #print (value) print(metricHeader.get('name') + '\t' + value) return headers, dimension, metricHeader.get('name'), value
def decode_request(request): data = zlib.decompress(base64.b64decode(request)) headers = [] kwargs = {} for line in data.splitlines(): keyword, _, value = line.partition(':') if keyword.startswith('X-Goa-'): kwargs[keyword[6:].lower()] = value.strip() else: headers.append((keyword.title(), value.strip())) return headers, kwargs
def forward(request, hostname, port, target, tls=None): hconn = h11.Connection(our_role=h11.CLIENT) if tls is None: tls = (port == 443) headers = _forward_headers(request.raw_headers, request.http_version, also_exclude=['Host']) # RFC 7230 recommends that ``Host`` be the first header. headers.insert(0, ('Host', _generate_host_header(hostname, port, tls))) headers.append(('Connection', 'close')) sock = socket.create_connection((hostname, port)) try: if tls: # We intentionally ignore server certificates. In this context, # they are more likely to be a nuisance than a boon. ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) sock = ssl_context.wrap_socket(sock, server_hostname=hostname) sock.sendall(hconn.send(h11.Request(method=request.method, target=target, headers=_encode_headers(headers)))) sock.sendall(hconn.send(h11.Data(data=request.body))) sock.sendall(hconn.send(h11.EndOfMessage())) response = Response() while True: # pylint: disable=no-member event = hconn.next_event() if event is h11.NEED_DATA: hconn.receive_data(sock.recv(4096)) elif isinstance(event, h11.Response): response.http_version = event.http_version.decode() response.status_code = event.status_code # Reason phrases can contain arbitrary bytes. # See above regarding ISO-8859-1. response.reason = event.reason.decode('iso-8859-1') response.raw_headers[:] = _forward_headers( _decode_headers(event.headers), response.http_version) elif isinstance(event, h11.Data): response.body += event.data elif isinstance(event, h11.EndOfMessage): return response except h11.RemoteProtocolError as exc: # https://github.com/njsmith/h11/issues/41 raise RuntimeError(str(exc)) from exc finally: sock.close()
def handle(self, environ, start_response, url_map, match, request_id, request_type): """Serves this request by forwarding it to the runtime process. Args: environ: An environ dict for the request as defined in PEP-333. start_response: A function with semantics defined in PEP-333. url_map: An appinfo.URLMap instance containing the configuration for the handler matching this request. match: A re.MatchObject containing the result of the matched URL pattern. request_id: A unique string id associated with the request. request_type: The type of the request. See instance.*_REQUEST module constants. Yields: A sequence of strings containing the body of the HTTP response. """ if self._prior_error: yield self._handle_error(self._prior_error, start_response) return environ[http_runtime_constants.SCRIPT_HEADER] = match.expand( url_map.script) if request_type == instance.BACKGROUND_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'background' elif request_type == instance.SHUTDOWN_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'shutdown' elif request_type == instance.INTERACTIVE_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'interactive' for name in http_runtime_constants.ENVIRONS_TO_PROPAGATE: if http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name not in environ: value = environ.get(name, None) if value is not None: environ[http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name] = value headers = util.get_headers_from_environ(environ) if environ.get('QUERY_STRING'): url = '%s?%s' % (urllib.quote( environ['PATH_INFO']), environ['QUERY_STRING']) else: url = urllib.quote(environ['PATH_INFO']) if 'CONTENT_LENGTH' in environ: headers['CONTENT-LENGTH'] = environ['CONTENT_LENGTH'] data = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) else: data = '' cookies = environ.get('HTTP_COOKIE') user_email, admin, user_id = login.get_user_info(cookies) if user_email: nickname, organization = user_email.split('@', 1) else: nickname = '' organization = '' headers[http_runtime_constants.REQUEST_ID_HEADER] = request_id headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Id'] = (user_id) headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Email'] = (user_email) headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Is-Admin'] = (str(int(admin))) headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Nickname'] = (nickname) headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Organization'] = (organization) headers['X-AppEngine-Country'] = 'ZZ' connection = httplib.HTTPConnection(self._host, self._port) with contextlib.closing(connection): try: connection.connect() connection.request(environ.get('REQUEST_METHOD', 'GET'), url, data, dict(headers.items())) try: response = connection.getresponse() except httplib.HTTPException as e: # The runtime process has written a bad HTTP response. For example, # a Go runtime process may have crashed in app-specific code. yield self._handle_error( 'the runtime process gave a bad HTTP response: %s' % e, start_response) return # Ensures that we avoid merging repeat headers into a single header, # allowing use of multiple Set-Cookie headers. headers = [] for name in response.msg: for value in response.msg.getheaders(name): headers.append((name, value)) response_headers = wsgiref.headers.Headers(headers) error_file = self._get_error_file() if (error_file and http_runtime_constants.ERROR_CODE_HEADER in response_headers): try: with open(error_file) as f: content = f.read() except IOError: content = 'Failed to load error handler' logging.exception('failed to load error file: %s', error_file) start_response('500 Internal Server Error', [('Content-Type', 'text/html'), ('Content-Length', str(len(content)))]) yield content return del response_headers[http_runtime_constants.ERROR_CODE_HEADER] start_response('%s %s' % (response.status, response.reason), response_headers.items()) # Yield the response body in small blocks. while True: try: block = response.read(512) if not block: break yield block except httplib.HTTPException: # The runtime process has encountered a problem, but has not # necessarily crashed. For example, a Go runtime process' HTTP # handler may have panicked in app-specific code (which the http # package will recover from, so the process as a whole doesn't # crash). At this point, we have already proxied onwards the HTTP # header, so we cannot retroactively serve a 500 Internal Server # Error. We silently break here; the runtime process has presumably # already written to stderr (via the Tee). break except Exception: with self._process_lock: if self._process and self._process.poll() is not None: # The development server is in a bad state. Log and return an error # message. self._prior_error = ( 'the runtime process for the instance running ' 'on port %d has unexpectedly quit' % (self._port)) yield self._handle_error(self._prior_error, start_response) else: raise
def handle(self, environ, start_response, url_map, match, request_id, request_type): """Serves this request by forwarding it to the runtime process. Args: environ: An environ dict for the request as defined in PEP-333. start_response: A function with semantics defined in PEP-333. url_map: An appinfo.URLMap instance containing the configuration for the handler matching this request. match: A re.MatchObject containing the result of the matched URL pattern. request_id: A unique string id associated with the request. request_type: The type of the request. See instance.*_REQUEST module constants. Yields: A sequence of strings containing the body of the HTTP response. """ environ[http_runtime_constants.SCRIPT_HEADER] = match.expand(url_map.script) if request_type == instance.BACKGROUND_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'background' elif request_type == instance.SHUTDOWN_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'shutdown' elif request_type == instance.INTERACTIVE_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'interactive' for name in http_runtime_constants.ENVIRONS_TO_PROPAGATE: if http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name not in environ: value = environ.get(name, None) if value is not None: environ[ http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name] = value headers = util.get_headers_from_environ(environ) if environ.get('QUERY_STRING'): url = '%s?%s' % (urllib.quote(environ['PATH_INFO']), environ['QUERY_STRING']) else: url = urllib.quote(environ['PATH_INFO']) if 'CONTENT_LENGTH' in environ: headers['CONTENT-LENGTH'] = environ['CONTENT_LENGTH'] data = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) else: data = '' cookies = environ.get('HTTP_COOKIE') user_email, admin, user_id = login.get_user_info(cookies) if user_email: nickname, organization = user_email.split('@', 1) else: nickname = '' organization = '' headers[http_runtime_constants.REQUEST_ID_HEADER] = request_id headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Id'] = ( user_id) headers[http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Email'] = ( user_email) headers[ http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Is-Admin'] = ( str(int(admin))) headers[ http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Nickname'] = ( nickname) headers[ http_runtime_constants.INTERNAL_HEADER_PREFIX + 'User-Organization'] = ( organization) headers['X-AppEngine-Country'] = 'ZZ' connection = httplib.HTTPConnection(self._host, self._port) with contextlib.closing(connection): try: connection.connect() connection.request(environ.get('REQUEST_METHOD', 'GET'), url, data, dict(headers.items())) response = connection.getresponse() # Ensures that we avoid merging repeat headers into a single header, # allowing use of multiple Set-Cookie headers. headers = [] for name in response.msg: for value in response.msg.getheaders(name): headers.append((name, value)) response_headers = wsgiref.headers.Headers(headers) error_file = self._get_error_file() if (error_file and http_runtime_constants.ERROR_CODE_HEADER in response_headers): try: with open(error_file) as f: content = f.read() except IOError: content = 'Failed to load error handler' logging.exception('failed to load error file: %s', error_file) start_response('500 Internal Server Error', [('Content-Type', 'text/html'), ('Content-Length', str(len(content)))]) yield content return del response_headers[http_runtime_constants.ERROR_CODE_HEADER] start_response('%s %s' % (response.status, response.reason), response_headers.items()) # Yield the response body in small blocks. block = response.read(512) while block: yield block block = response.read(512) except Exception: with self._process_lock: if self._process and self._process.poll() is not None: # The development server is in a bad state. Log and return an error # message and quit. message = ('the runtime process for the instance running on port ' '%d has unexpectedly quit; exiting the development ' 'server' % ( self._port)) logging.error(message) start_response('500 Internal Server Error', [('Content-Type', 'text/plain'), ('Content-Length', str(len(message)))]) shutdown.async_quit() yield message else: raise
def handle(self, environ, start_response, url_map, match, request_id, request_type): """Serves this request by forwarding it to the runtime process. Args: environ: An environ dict for the request as defined in PEP-333. start_response: A function with semantics defined in PEP-333. url_map: An appinfo.URLMap instance containing the configuration for the handler matching this request. match: A re.MatchObject containing the result of the matched URL pattern. request_id: A unique string id associated with the request. request_type: The type of the request. See instance.*_REQUEST module constants. Yields: A sequence of strings containing the body of the HTTP response. """ if self._prior_error: logging.error(self._prior_error) yield self._respond_with_error(self._prior_error, start_response) return environ[http_runtime_constants.SCRIPT_HEADER] = match.expand(url_map.script) if request_type == instance.BACKGROUND_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'background' elif request_type == instance.SHUTDOWN_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'shutdown' elif request_type == instance.INTERACTIVE_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'interactive' for name in http_runtime_constants.ENVIRONS_TO_PROPAGATE: if http_runtime_constants.APPENGINE_ENVIRON_PREFIX + name not in environ: value = environ.get(name, None) if value is not None: environ[ http_runtime_constants.APPENGINE_ENVIRON_PREFIX + name] = value headers = util.get_headers_from_environ(environ) if environ.get('QUERY_STRING'): url = '%s?%s' % (urllib.quote(environ['PATH_INFO']), environ['QUERY_STRING']) else: url = urllib.quote(environ['PATH_INFO']) if 'CONTENT_LENGTH' in environ: headers['CONTENT-LENGTH'] = environ['CONTENT_LENGTH'] data = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) else: data = None cookies = environ.get('HTTP_COOKIE') user_email, admin, user_id = login.get_user_info(cookies) if user_email: nickname, organization = user_email.split('@', 1) else: nickname = '' organization = '' headers[self.request_id_header_name] = request_id headers[http_runtime_constants.APPENGINE_HEADER_PREFIX + 'User-Id'] = ( user_id) headers[http_runtime_constants.APPENGINE_HEADER_PREFIX + 'User-Email'] = ( user_email) headers[ http_runtime_constants.APPENGINE_HEADER_PREFIX + 'User-Is-Admin'] = ( str(int(admin))) headers[ http_runtime_constants.APPENGINE_HEADER_PREFIX + 'User-Nickname'] = ( nickname) headers[http_runtime_constants.APPENGINE_HEADER_PREFIX + 'User-Organization'] = organization headers['X-AppEngine-Country'] = 'ZZ' connection = httplib.HTTPConnection(self._host, self._port) with contextlib.closing(connection): try: connection.connect() connection.request(environ.get('REQUEST_METHOD', 'GET'), url, data, dict(headers.items())) try: response = connection.getresponse() except httplib.HTTPException as e: # The runtime process has written a bad HTTP response. For example, # a Go runtime process may have crashed in app-specific code. yield self._respond_with_error( 'the runtime process gave a bad HTTP response: %s' % e, start_response) return # Ensures that we avoid merging repeat headers into a single header, # allowing use of multiple Set-Cookie headers. headers = [] for name in response.msg: for value in response.msg.getheaders(name): headers.append((name, value)) response_headers = wsgiref.headers.Headers(headers) if self._error_handler_file and ( http_runtime_constants.ERROR_CODE_HEADER in response_headers): try: with open(self._error_handler_file) as f: content = f.read() except IOError: content = 'Failed to load error handler' logging.exception('failed to load error file: %s', self._error_handler_file) start_response('500 Internal Server Error', [('Content-Type', 'text/html'), ('Content-Length', str(len(content)))]) yield content return del response_headers[http_runtime_constants.ERROR_CODE_HEADER] start_response('%s %s' % (response.status, response.reason), response_headers.items()) # Yield the response body in small blocks. while True: try: block = response.read(512) if not block: break yield block except httplib.HTTPException: # The runtime process has encountered a problem, but has not # necessarily crashed. For example, a Go runtime process' HTTP # handler may have panicked in app-specific code (which the http # package will recover from, so the process as a whole doesn't # crash). At this point, we have already proxied onwards the HTTP # header, so we cannot retroactively serve a 500 Internal Server # Error. We silently break here; the runtime process has presumably # already written to stderr (via the Tee). break except Exception: if self._instance_died_unexpectedly(): yield self._respond_with_error( 'the runtime process for the instance running on port %d has ' 'unexpectedly quit' % self._port, start_response) else: raise
def fetch_url(self, server, method, relative_url, body=None, headers=None, instance=None): """Makes an HTTP request to the given server and returns the result. Args: server: The name of the server that the request should be sent to e.g. "default". method: The HTTP method to use when fetching the URL e.g. "GET". relative_url: The URL to access on the server e.g. "/foo?bar=baz". body: The body to use in the HTTP request. headers: A dict containing the headers that should be sent. instance: A str containing the instance ID of the instance to send the request to. Defaults to using the load-balancer. Returns: A 3-tuple of: status: An int representing the received HTTP status code e.g. 200. content: A string containing the content of the HTTP reply. headers: A wsgiref.headers.Headers instance containing the received HTTP headers. """ host = self.server.server_to_address(server, instance=instance) headers = headers or {} if not relative_url.startswith('/'): relative_url = '/' + relative_url logging.info('Connecting to %s', host) try: connection = httplib.HTTPConnection(host) logging.info('Sending request "%s %s"', method, relative_url) try: connection.putrequest(method, relative_url) if body is None: content_length = 0 else: content_length = len(body) if method not in ('GET', 'TRACE') or content_length: connection.putheader('Content-length', content_length) for key, value in headers.iteritems(): connection.putheader(str(key), str(value)) connection.endheaders() if body is not None: connection.send(body) response = connection.getresponse() status = response.status content = response.read() # Ensures that we avoid merging repeat headers into a single header, # allowing use of multiple Set-Cookie headers. headers = [] for name in response.msg: for value in response.msg.getheaders(name): headers.append((name, value)) headers = wsgiref.headers.Headers(headers) logging.info('Received response %s with content:\n%s\n', status, content[:1024 * 16]) return status, content, headers finally: connection.close() except (IOError, httplib.HTTPException, socket.error), e: logging.info('Encountered exception accessing HTTP server: %s', e) raise
def handle(self, environ, start_response, url_map, match, request_id, request_type): """Serves this request by forwarding it to the runtime process. Args: environ: An environ dict for the request as defined in PEP-333. start_response: A function with semantics defined in PEP-333. url_map: An appinfo.URLMap instance containing the configuration for the handler matching this request. match: A re.MatchObject containing the result of the matched URL pattern. request_id: A unique string id associated with the request. request_type: The type of the request. See instance.*_REQUEST module constants. Yields: A sequence of strings containing the body of the HTTP response. """ environ[http_runtime_constants.SCRIPT_HEADER] = match.expand( url_map.script) if request_type == instance.BACKGROUND_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'background' elif request_type == instance.SHUTDOWN_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'shutdown' elif request_type == instance.INTERACTIVE_REQUEST: environ[http_runtime_constants.REQUEST_TYPE_HEADER] = 'interactive' for name in http_runtime_constants.ENVIRONS_TO_PROPAGATE: if http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name not in environ: value = environ.get(name, None) if value is not None: environ[http_runtime_constants.INTERNAL_ENVIRON_PREFIX + name] = value headers = util.get_headers_from_environ(environ) if environ.get('QUERY_STRING'): url = '%s?%s' % (urllib.quote( environ['PATH_INFO']), environ['QUERY_STRING']) else: url = urllib.quote(environ['PATH_INFO']) if 'CONTENT_LENGTH' in environ: headers['CONTENT-LENGTH'] = environ['CONTENT_LENGTH'] data = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH'])) else: data = '' cookies = environ.get('HTTP_COOKIE') user_email, admin, user_id = login.get_user_info(cookies) if user_email: nickname, organization = user_email.split('@', 1) else: nickname = '' organization = '' headers[http_runtime_constants.REQUEST_ID_HEADER] = request_id prefix = http_runtime_constants.INTERNAL_HEADER_PREFIX # The Go runtime from the 1.9.48 SDK looks for different headers. if self._module_configuration.runtime == 'go': headers['X-Appengine-Dev-Request-Id'] = request_id prefix = 'X-Appengine-' headers[prefix + 'User-Id'] = (user_id) headers[prefix + 'User-Email'] = (user_email) headers[prefix + 'User-Is-Admin'] = (str(int(admin))) headers[prefix + 'User-Nickname'] = (nickname) headers[prefix + 'User-Organization'] = (organization) headers['X-AppEngine-Country'] = 'ZZ' connection = httplib.HTTPConnection(self._host, self._port) with contextlib.closing(connection): try: connection.connect() connection.request(environ.get('REQUEST_METHOD', 'GET'), url, data, dict(headers.items())) response = connection.getresponse() # Ensures that we avoid merging repeat headers into a single header, # allowing use of multiple Set-Cookie headers. headers = [] for name in response.msg: for value in response.msg.getheaders(name): headers.append((name, value)) response_headers = wsgiref.headers.Headers(headers) error_file = self._get_error_file() if (error_file and http_runtime_constants.ERROR_CODE_HEADER in response_headers): try: with open(error_file) as f: content = f.read() except IOError: content = 'Failed to load error handler' logging.exception('failed to load error file: %s', error_file) start_response('500 Internal Server Error', [('Content-Type', 'text/html'), ('Content-Length', str(len(content)))]) yield content return del response_headers[http_runtime_constants.ERROR_CODE_HEADER] start_response('%s %s' % (response.status, response.reason), response_headers.items()) # Yield the response body in small blocks. block = response.read(512) while block: yield block block = response.read(512) except Exception: with self._process_lock: if self._process and self._process.poll() is not None: # The development server is in a bad state. Log and return an error # message and quit. message = ( 'the runtime process for the instance running on port ' '%d has unexpectedly quit; exiting the development ' 'server' % (self._port)) logging.error(message) start_response('500 Internal Server Error', [('Content-Type', 'text/plain'), ('Content-Length', str(len(message)))]) shutdown.async_quit() yield message else: raise