thirdparty/google_appengine/google/appengine/dist/httplib.py
changeset 1278 a7766286a7be
child 2413 d0b7dac5325c
equal deleted inserted replaced
1277:5c931bd3dc1e 1278:a7766286a7be
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Copyright 2007 Google Inc.
       
     4 #
       
     5 # Licensed under the Apache License, Version 2.0 (the "License");
       
     6 # you may not use this file except in compliance with the License.
       
     7 # You may obtain a copy of the License at
       
     8 #
       
     9 #     http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    11 # Unless required by applicable law or agreed to in writing, software
       
    12 # distributed under the License is distributed on an "AS IS" BASIS,
       
    13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
       
    14 # See the License for the specific language governing permissions and
       
    15 # limitations under the License.
       
    16 #
       
    17 """Copyright 2008 Python Software Foundation, Ian Bicking, and Google."""
       
    18 
       
    19 import mimetools
       
    20 import StringIO
       
    21 import sys
       
    22 
       
    23 
       
    24 CONTINUE = 100
       
    25 SWITCHING_PROTOCOLS = 101
       
    26 PROCESSING  = 102
       
    27 OK = 200
       
    28 CREATED = 201
       
    29 ACCEPTED = 202
       
    30 NON_AUTHORITATIVE_INFORMATION = 203
       
    31 NO_CONTENT = 204
       
    32 RESET_CONTENT = 205
       
    33 PARTIAL_CONTENT = 206
       
    34 MULTI_STATUS = 207
       
    35 IM_USED = 226
       
    36 MULTIPLE_CHOICES = 300
       
    37 MOVED_PERMANENTLY = 301
       
    38 FOUND = 302
       
    39 SEE_OTHER = 303
       
    40 NOT_MODIFIED = 304
       
    41 USE_PROXY = 305
       
    42 TEMPORARY_REDIRECT = 307
       
    43 BAD_REQUEST = 400
       
    44 UNAUTHORIZED = 401
       
    45 PAYMENT_REQUIRED = 402
       
    46 FORBIDDEN = 403
       
    47 NOT_FOUND = 404
       
    48 METHOD_NOT_ALLOWED = 405
       
    49 NOT_ACCEPTABLE = 406
       
    50 PROXY_AUTHENTICATION_REQUIRED = 407
       
    51 REQUEST_TIMEOUT = 408
       
    52 CONFLICT = 409
       
    53 GONE = 410
       
    54 LENGTH_REQUIRED = 411
       
    55 PRECONDITION_FAILED = 412
       
    56 REQUEST_ENTITY_TOO_LARGE = 413
       
    57 REQUEST_URI_TOO_LONG = 414
       
    58 UNSUPPORTED_MEDIA_TYPE = 415
       
    59 REQUESTED_RANGE_NOT_SATISFIABLE = 416
       
    60 EXPECTATION_FAILED = 417
       
    61 UNPROCESSABLE_ENTITY = 422
       
    62 LOCKED = 423
       
    63 FAILED_DEPENDENCY = 424
       
    64 UPGRADE_REQUIRED = 426
       
    65 INTERNAL_SERVER_ERROR = 500
       
    66 NOT_IMPLEMENTED = 501
       
    67 BAD_GATEWAY = 502
       
    68 SERVICE_UNAVAILABLE = 503
       
    69 GATEWAY_TIMEOUT = 504
       
    70 HTTP_VERSION_NOT_SUPPORTED = 505
       
    71 INSUFFICIENT_STORAGE = 507
       
    72 NOT_EXTENDED = 510
       
    73 
       
    74 responses = {
       
    75   100: 'Continue',
       
    76   101: 'Switching Protocols',
       
    77 
       
    78   200: 'OK',
       
    79   201: 'Created',
       
    80   202: 'Accepted',
       
    81   203: 'Non-Authoritative Information',
       
    82   204: 'No Content',
       
    83   205: 'Reset Content',
       
    84   206: 'Partial Content',
       
    85 
       
    86   300: 'Multiple Choices',
       
    87   301: 'Moved Permanently',
       
    88   302: 'Found',
       
    89   303: 'See Other',
       
    90   304: 'Not Modified',
       
    91   305: 'Use Proxy',
       
    92   306: '(Unused)',
       
    93   307: 'Temporary Redirect',
       
    94 
       
    95   400: 'Bad Request',
       
    96   401: 'Unauthorized',
       
    97   402: 'Payment Required',
       
    98   403: 'Forbidden',
       
    99   404: 'Not Found',
       
   100   405: 'Method Not Allowed',
       
   101   406: 'Not Acceptable',
       
   102   407: 'Proxy Authentication Required',
       
   103   408: 'Request Timeout',
       
   104   409: 'Conflict',
       
   105   410: 'Gone',
       
   106   411: 'Length Required',
       
   107   412: 'Precondition Failed',
       
   108   413: 'Request Entity Too Large',
       
   109   414: 'Request-URI Too Long',
       
   110   415: 'Unsupported Media Type',
       
   111   416: 'Requested Range Not Satisfiable',
       
   112   417: 'Expectation Failed',
       
   113 
       
   114   500: 'Internal Server Error',
       
   115   501: 'Not Implemented',
       
   116   502: 'Bad Gateway',
       
   117   503: 'Service Unavailable',
       
   118   504: 'Gateway Timeout',
       
   119   505: 'HTTP Version Not Supported',
       
   120 }
       
   121 
       
   122 HTTP_PORT = 80
       
   123 HTTPS_PORT = 443
       
   124 
       
   125 
       
   126 
       
   127 
       
   128 
       
   129 class HTTPConnection:
       
   130 
       
   131 
       
   132   protocol = 'http'
       
   133   default_port = HTTP_PORT
       
   134   _allow_truncated = True
       
   135   _follow_redirects = False
       
   136 
       
   137   def __init__(self, host, port=None, strict=False, timeout=None):
       
   138     from google.appengine.api import urlfetch
       
   139     self._fetch = urlfetch.fetch
       
   140     self._method_map = {
       
   141       'GET': urlfetch.GET,
       
   142       'POST': urlfetch.POST,
       
   143       'HEAD': urlfetch.HEAD,
       
   144       'PUT': urlfetch.PUT,
       
   145       'DELETE': urlfetch.DELETE,
       
   146     }
       
   147     self.host = host
       
   148     self.port = port
       
   149     self._method = self._url = None
       
   150     self._body = ''
       
   151     self.headers = []
       
   152 
       
   153   def connect(self):
       
   154     pass
       
   155 
       
   156   def request(self, method, url, body=None, headers=None):
       
   157     self._method = method
       
   158     self._url = url
       
   159     try:
       
   160       self._body = body.read()
       
   161     except AttributeError:
       
   162       self._body = body
       
   163     if headers is None:
       
   164       headers = []
       
   165     elif hasattr(headers, 'items'):
       
   166       headers = headers.items()
       
   167     self.headers = headers
       
   168 
       
   169   def putrequest(self, request, selector, skip_host=False, skip_accept_encoding=False):
       
   170     self._method = request
       
   171     self._url = selector
       
   172 
       
   173   def putheader(self, header, *lines):
       
   174     line = '\r\n\t'.join(lines)
       
   175     self.headers.append((header, line))
       
   176 
       
   177   def endheaders(self):
       
   178     pass
       
   179 
       
   180   def set_debuglevel(self, level=None):
       
   181     pass
       
   182 
       
   183   def send(self, data):
       
   184     self._body += data
       
   185 
       
   186   def getresponse(self):
       
   187     if self.port and self.port != self.default_port:
       
   188         host = '%s:%s' % (self.host, self.port)
       
   189     else:
       
   190         host = self.host
       
   191     url = '%s://%s%s' % (self.protocol, host, self._url)
       
   192     headers = dict(self.headers)
       
   193 
       
   194     try:
       
   195       method = self._method_map[self._method.upper()]
       
   196     except KeyError:
       
   197       raise ValueError("%r is an unrecognized HTTP method" % self._method)
       
   198 
       
   199     response = self._fetch(url, self._body, method, headers,
       
   200                            self._allow_truncated, self._follow_redirects)
       
   201     return HTTPResponse(response)
       
   202 
       
   203   def close(self):
       
   204       pass
       
   205 
       
   206 
       
   207 class HTTPSConnection(HTTPConnection):
       
   208 
       
   209     protocol = 'https'
       
   210     default_port = HTTPS_PORT
       
   211 
       
   212     def __init__(self, host, port=None, key_file=None, cert_file=None,
       
   213                  strict=False, timeout=None):
       
   214         if key_file is not None or cert_file is not None:
       
   215             raise NotImplementedError(
       
   216                 "key_file and cert_file arguments are not implemented")
       
   217         HTTPConnection.__init__(self, host, port=port, strict=strict,
       
   218                                 timeout=timeout)
       
   219 
       
   220 
       
   221 class HTTPResponse(object):
       
   222 
       
   223   def __init__(self, fetch_response):
       
   224     self._fetch_response = fetch_response
       
   225     self.fp = StringIO.StringIO(fetch_response.content)
       
   226 
       
   227   def __getattr__(self, attr):
       
   228     return getattr(self.fp, attr)
       
   229 
       
   230   def getheader(self, name, default=None):
       
   231     return self._fetch_response.headers.get(name, default)
       
   232 
       
   233   def getheaders(self):
       
   234     return self._fetch_response.headers.items()
       
   235 
       
   236   @property
       
   237   def msg(self):
       
   238     msg = mimetools.Message(StringIO.StringIO(''))
       
   239     for name, value in self._fetch_response.headers.items():
       
   240       msg[name] = value
       
   241     return msg
       
   242 
       
   243   version = 11
       
   244 
       
   245   @property
       
   246   def status(self):
       
   247     return self._fetch_response.status_code
       
   248 
       
   249   @property
       
   250   def reason(self):
       
   251     return responses.get(self._fetch_response.status_code, 'Unknown')
       
   252 
       
   253 
       
   254 class HTTP:
       
   255   "Compatibility class with httplib.py from 1.5."
       
   256 
       
   257   _http_vsn = 11
       
   258   _http_vsn_str = 'HTTP/1.1'
       
   259 
       
   260   debuglevel = 0
       
   261 
       
   262   _connection_class = HTTPConnection
       
   263 
       
   264   def __init__(self, host='', port=None, strict=None):
       
   265     "Provide a default host, since the superclass requires one."
       
   266 
       
   267     if port == 0:
       
   268       port = None
       
   269 
       
   270     self._setup(self._connection_class(host, port, strict))
       
   271 
       
   272   def _setup(self, conn):
       
   273     self._conn = conn
       
   274 
       
   275     self.send = conn.send
       
   276     self.putrequest = conn.putrequest
       
   277     self.endheaders = conn.endheaders
       
   278     self.set_debuglevel = conn.set_debuglevel
       
   279 
       
   280     conn._http_vsn = self._http_vsn
       
   281     conn._http_vsn_str = self._http_vsn_str
       
   282 
       
   283     self.file = None
       
   284 
       
   285   def connect(self, host=None, port=None):
       
   286     "Accept arguments to set the host/port, since the superclass doesn't."
       
   287     self.__init__(host, port)
       
   288 
       
   289   def getfile(self):
       
   290     "Provide a getfile, since the superclass' does not use this concept."
       
   291     return self.file
       
   292 
       
   293   def putheader(self, header, *values):
       
   294     "The superclass allows only one value argument."
       
   295     self._conn.putheader(header, '\r\n\t'.join(values))
       
   296 
       
   297   def getreply(self):
       
   298     """Compat definition since superclass does not define it.
       
   299 
       
   300     Returns a tuple consisting of:
       
   301     - server status code (e.g. '200' if all goes well)
       
   302     - server "reason" corresponding to status code
       
   303     - any RFC822 headers in the response from the server
       
   304     """
       
   305     response = self._conn.getresponse()
       
   306 
       
   307     self.headers = response.msg
       
   308     self.file = response.fp
       
   309     return response.status, response.reason, response.msg
       
   310 
       
   311   def close(self):
       
   312     self._conn.close()
       
   313 
       
   314     self.file = None
       
   315 
       
   316 
       
   317 class HTTPS(HTTP):
       
   318   """Compatibility with 1.5 httplib interface
       
   319 
       
   320   Python 1.5.2 did not have an HTTPS class, but it defined an
       
   321   interface for sending http requests that is also useful for
       
   322   https.
       
   323   """
       
   324 
       
   325   _connection_class = HTTPSConnection
       
   326 
       
   327   def __init__(self, host='', port=None, key_file=None, cert_file=None,
       
   328                strict=None):
       
   329     if key_file is not None or cert_file is not None:
       
   330       raise NotImplementedError(
       
   331           "key_file and cert_file arguments are not implemented")
       
   332 
       
   333 
       
   334     if port == 0:
       
   335       port = None
       
   336     self._setup(self._connection_class(host, port, key_file,
       
   337                                        cert_file, strict))
       
   338 
       
   339     self.key_file = key_file
       
   340     self.cert_file = cert_file
       
   341 
       
   342 
       
   343 class HTTPException(Exception):
       
   344   pass
       
   345 
       
   346 class NotConnected(HTTPException):
       
   347   pass
       
   348 
       
   349 class InvalidURL(HTTPException):
       
   350   pass
       
   351 
       
   352 class UnknownProtocol(HTTPException):
       
   353   def __init__(self, version):
       
   354     self.version = version
       
   355     HTTPException.__init__(self, version)
       
   356 
       
   357 class UnknownTransferEncoding(HTTPException):
       
   358   pass
       
   359 
       
   360 class UnimplementedFileMode(HTTPException):
       
   361   pass
       
   362 
       
   363 class IncompleteRead(HTTPException):
       
   364   def __init__(self, partial):
       
   365     self.partial = partial
       
   366     HTTPException.__init__(self, partial)
       
   367 
       
   368 class ImproperConnectionState(HTTPException):
       
   369   pass
       
   370 
       
   371 class CannotSendRequest(ImproperConnectionState):
       
   372   pass
       
   373 
       
   374 class CannotSendHeader(ImproperConnectionState):
       
   375   pass
       
   376 
       
   377 class ResponseNotReady(ImproperConnectionState):
       
   378   pass
       
   379 
       
   380 class BadStatusLine(HTTPException):
       
   381   def __init__(self, line):
       
   382     self.line = line
       
   383     HTTPException.__init__(self, line)
       
   384 
       
   385 error = HTTPException