author | Pawel Solyga <Pawel.Solyga@gmail.com> |
Mon, 01 Jun 2009 20:31:38 +0200 | |
changeset 2373 | 05ab9393303d |
parent 1278 | a7766286a7be |
child 2413 | d0b7dac5325c |
permissions | -rwxr-xr-x |
#!/usr/bin/env python # # Copyright 2007 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # """Copyright 2008 Python Software Foundation, Ian Bicking, and Google.""" import mimetools import StringIO import sys CONTINUE = 100 SWITCHING_PROTOCOLS = 101 PROCESSING = 102 OK = 200 CREATED = 201 ACCEPTED = 202 NON_AUTHORITATIVE_INFORMATION = 203 NO_CONTENT = 204 RESET_CONTENT = 205 PARTIAL_CONTENT = 206 MULTI_STATUS = 207 IM_USED = 226 MULTIPLE_CHOICES = 300 MOVED_PERMANENTLY = 301 FOUND = 302 SEE_OTHER = 303 NOT_MODIFIED = 304 USE_PROXY = 305 TEMPORARY_REDIRECT = 307 BAD_REQUEST = 400 UNAUTHORIZED = 401 PAYMENT_REQUIRED = 402 FORBIDDEN = 403 NOT_FOUND = 404 METHOD_NOT_ALLOWED = 405 NOT_ACCEPTABLE = 406 PROXY_AUTHENTICATION_REQUIRED = 407 REQUEST_TIMEOUT = 408 CONFLICT = 409 GONE = 410 LENGTH_REQUIRED = 411 PRECONDITION_FAILED = 412 REQUEST_ENTITY_TOO_LARGE = 413 REQUEST_URI_TOO_LONG = 414 UNSUPPORTED_MEDIA_TYPE = 415 REQUESTED_RANGE_NOT_SATISFIABLE = 416 EXPECTATION_FAILED = 417 UNPROCESSABLE_ENTITY = 422 LOCKED = 423 FAILED_DEPENDENCY = 424 UPGRADE_REQUIRED = 426 INTERNAL_SERVER_ERROR = 500 NOT_IMPLEMENTED = 501 BAD_GATEWAY = 502 SERVICE_UNAVAILABLE = 503 GATEWAY_TIMEOUT = 504 HTTP_VERSION_NOT_SUPPORTED = 505 INSUFFICIENT_STORAGE = 507 NOT_EXTENDED = 510 responses = { 100: 'Continue', 101: 'Switching Protocols', 200: 'OK', 201: 'Created', 202: 'Accepted', 203: 'Non-Authoritative Information', 204: 'No Content', 205: 'Reset Content', 206: 'Partial Content', 300: 'Multiple Choices', 301: 'Moved Permanently', 302: 'Found', 303: 'See Other', 304: 'Not Modified', 305: 'Use Proxy', 306: '(Unused)', 307: 'Temporary Redirect', 400: 'Bad Request', 401: 'Unauthorized', 402: 'Payment Required', 403: 'Forbidden', 404: 'Not Found', 405: 'Method Not Allowed', 406: 'Not Acceptable', 407: 'Proxy Authentication Required', 408: 'Request Timeout', 409: 'Conflict', 410: 'Gone', 411: 'Length Required', 412: 'Precondition Failed', 413: 'Request Entity Too Large', 414: 'Request-URI Too Long', 415: 'Unsupported Media Type', 416: 'Requested Range Not Satisfiable', 417: 'Expectation Failed', 500: 'Internal Server Error', 501: 'Not Implemented', 502: 'Bad Gateway', 503: 'Service Unavailable', 504: 'Gateway Timeout', 505: 'HTTP Version Not Supported', } HTTP_PORT = 80 HTTPS_PORT = 443 class HTTPConnection: protocol = 'http' default_port = HTTP_PORT _allow_truncated = True _follow_redirects = False def __init__(self, host, port=None, strict=False, timeout=None): from google.appengine.api import urlfetch self._fetch = urlfetch.fetch self._method_map = { 'GET': urlfetch.GET, 'POST': urlfetch.POST, 'HEAD': urlfetch.HEAD, 'PUT': urlfetch.PUT, 'DELETE': urlfetch.DELETE, } self.host = host self.port = port self._method = self._url = None self._body = '' self.headers = [] def connect(self): pass def request(self, method, url, body=None, headers=None): self._method = method self._url = url try: self._body = body.read() except AttributeError: self._body = body if headers is None: headers = [] elif hasattr(headers, 'items'): headers = headers.items() self.headers = headers def putrequest(self, request, selector, skip_host=False, skip_accept_encoding=False): self._method = request self._url = selector def putheader(self, header, *lines): line = '\r\n\t'.join(lines) self.headers.append((header, line)) def endheaders(self): pass def set_debuglevel(self, level=None): pass def send(self, data): self._body += data def getresponse(self): if self.port and self.port != self.default_port: host = '%s:%s' % (self.host, self.port) else: host = self.host url = '%s://%s%s' % (self.protocol, host, self._url) headers = dict(self.headers) try: method = self._method_map[self._method.upper()] except KeyError: raise ValueError("%r is an unrecognized HTTP method" % self._method) response = self._fetch(url, self._body, method, headers, self._allow_truncated, self._follow_redirects) return HTTPResponse(response) def close(self): pass class HTTPSConnection(HTTPConnection): protocol = 'https' default_port = HTTPS_PORT def __init__(self, host, port=None, key_file=None, cert_file=None, strict=False, timeout=None): if key_file is not None or cert_file is not None: raise NotImplementedError( "key_file and cert_file arguments are not implemented") HTTPConnection.__init__(self, host, port=port, strict=strict, timeout=timeout) class HTTPResponse(object): def __init__(self, fetch_response): self._fetch_response = fetch_response self.fp = StringIO.StringIO(fetch_response.content) def __getattr__(self, attr): return getattr(self.fp, attr) def getheader(self, name, default=None): return self._fetch_response.headers.get(name, default) def getheaders(self): return self._fetch_response.headers.items() @property def msg(self): msg = mimetools.Message(StringIO.StringIO('')) for name, value in self._fetch_response.headers.items(): msg[name] = value return msg version = 11 @property def status(self): return self._fetch_response.status_code @property def reason(self): return responses.get(self._fetch_response.status_code, 'Unknown') class HTTP: "Compatibility class with httplib.py from 1.5." _http_vsn = 11 _http_vsn_str = 'HTTP/1.1' debuglevel = 0 _connection_class = HTTPConnection def __init__(self, host='', port=None, strict=None): "Provide a default host, since the superclass requires one." if port == 0: port = None self._setup(self._connection_class(host, port, strict)) def _setup(self, conn): self._conn = conn self.send = conn.send self.putrequest = conn.putrequest self.endheaders = conn.endheaders self.set_debuglevel = conn.set_debuglevel conn._http_vsn = self._http_vsn conn._http_vsn_str = self._http_vsn_str self.file = None def connect(self, host=None, port=None): "Accept arguments to set the host/port, since the superclass doesn't." self.__init__(host, port) def getfile(self): "Provide a getfile, since the superclass' does not use this concept." return self.file def putheader(self, header, *values): "The superclass allows only one value argument." self._conn.putheader(header, '\r\n\t'.join(values)) def getreply(self): """Compat definition since superclass does not define it. Returns a tuple consisting of: - server status code (e.g. '200' if all goes well) - server "reason" corresponding to status code - any RFC822 headers in the response from the server """ response = self._conn.getresponse() self.headers = response.msg self.file = response.fp return response.status, response.reason, response.msg def close(self): self._conn.close() self.file = None class HTTPS(HTTP): """Compatibility with 1.5 httplib interface Python 1.5.2 did not have an HTTPS class, but it defined an interface for sending http requests that is also useful for https. """ _connection_class = HTTPSConnection def __init__(self, host='', port=None, key_file=None, cert_file=None, strict=None): if key_file is not None or cert_file is not None: raise NotImplementedError( "key_file and cert_file arguments are not implemented") if port == 0: port = None self._setup(self._connection_class(host, port, key_file, cert_file, strict)) self.key_file = key_file self.cert_file = cert_file class HTTPException(Exception): pass class NotConnected(HTTPException): pass class InvalidURL(HTTPException): pass class UnknownProtocol(HTTPException): def __init__(self, version): self.version = version HTTPException.__init__(self, version) class UnknownTransferEncoding(HTTPException): pass class UnimplementedFileMode(HTTPException): pass class IncompleteRead(HTTPException): def __init__(self, partial): self.partial = partial HTTPException.__init__(self, partial) class ImproperConnectionState(HTTPException): pass class CannotSendRequest(ImproperConnectionState): pass class CannotSendHeader(ImproperConnectionState): pass class ResponseNotReady(ImproperConnectionState): pass class BadStatusLine(HTTPException): def __init__(self, line): self.line = line HTTPException.__init__(self, line) error = HTTPException