Exemplo n.º 1
0
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
Exemplo n.º 2
0
    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")]
Exemplo n.º 3
0
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
Exemplo n.º 4
0
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
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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
Exemplo n.º 9
0
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
Exemplo n.º 10
0
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()
Exemplo n.º 11
0
    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
Exemplo n.º 12
0
  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
Exemplo n.º 13
0
  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
Exemplo n.º 14
0
  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
Exemplo n.º 15
0
    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