def __init__(self, url, data=None, method=None, timeout=30, **kwargs): self.last_read = time.time() self.timeout = timeout self.set_terminator('\r\n\r\n') # parse the url and set everything up self.url = url self.parsed = parsed = urlparse.urlparse(url) if parsed.scheme not in ('http', 'https'): raise httplib.UnknownProtocol("Can only access http urls") self.method = None self.established = parsed.scheme == 'http' if method and method.upper() in ('GET', 'POST'): self.method = method.upper() else: self.method = 'POST' if data is not None else 'GET' self.callbacks = kwargs # prepare the http request itself post_body = urllib.urlencode(data) if data is not None else None host, _, port = parsed.netloc.partition(':') http = httplib.HTTPConnection(host) http.sock = StringBuffer() path = parsed.path if parsed.params: path += ';' + parsed.params if parsed.query: path += '?' + parsed.query http.request(self.method, path, post_body) # connect to the host asynchronously asynchat.async_chat.__init__(self) self.push(http.sock.getvalue()) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) port = int(port, 10) if port else PORTS[parsed.scheme] self.connect((host, port))
def __init__(self, request_handler=TestingHTTPRequestHandler, protocol_version=None): """Constructor. :param request_handler: a class that will be instantiated to handle an http connection (one or several requests). :param protocol_version: if specified, will override the protocol version of the request handler. """ # Depending on the protocol version, we will create the approriate # server if protocol_version is None: # Use the request handler one proto_vers = request_handler.protocol_version else: # Use our own, it will be used to override the request handler # one too. proto_vers = protocol_version # Get the appropriate server class for the required protocol serv_cls = self.http_server_class.get(proto_vers, None) if serv_cls is None: raise httplib.UnknownProtocol(proto_vers) self.host = 'localhost' self.port = 0 super(HttpServer, self).__init__((self.host, self.port), serv_cls, request_handler) self.protocol_version = proto_vers # Allows tests to verify number of GET requests issued self.GET_request_nb = 0 self._http_base_url = None self.logs = []
def _read_status(self, line): version = None status = None reason = None # Initialize with Simple-Response defaults if not line: # Presumably, the server closed the connection before # sending a valid response. raise httplib.BadStatusLine(line) try: [version, status, reason] = line.split(None, 2) except ValueError: try: [version, status] = line.split(None, 1) reason = "" except ValueError: # empty version will cause next test to fail and status # will be treated as 0.9 response. version = "" if not version.startswith('HTTP/'): print repr(line) errinfo = "error http status" raise socket.error(errinfo) # The status code is a three-digit number try: status = int(status) if status < 100 or status > 999: raise httplib.BadStatusLine(line) except ValueError: raise httplib.BadStatusLine(line) self.status = status self.reason = reason.strip() if version == 'HTTP/1.0': self.version = 10 elif version.startswith('HTTP/1.'): self.version = 11 elif version == 'HTTP/0.9': self.version = 9 else: raise httplib.UnknownProtocol(version)
def _generic_proxytunnel(self): proxyheaders = dict([(x, self.headers[x]) for x in self.headers if x.lower().startswith('proxy-')]) self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport) for header in proxyheaders.iteritems(): self.send('%s: %s\r\n' % header) self.send('\r\n') # majority of the following code is duplicated from # httplib.HTTPConnection as there are no adequate places to # override functions to provide the needed functionality res = self.response_class(self.sock, strict=self.strict, method=self._method) while True: version, status, reason = res._read_status() if status != httplib.CONTINUE: break while True: skip = res.fp.readline().strip() if not skip: break res.status = status res.reason = reason.strip() if res.status == 200: while True: line = res.fp.readline() if line == '\r\n': break return True if version == 'HTTP/1.0': res.version = 10 elif version.startswith('HTTP/1.'): res.version = 11 elif version == 'HTTP/0.9': res.version = 9 else: raise httplib.UnknownProtocol(version) if res.version == 9: res.length = None res.chunked = 0 res.will_close = 1 res.msg = httplib.HTTPMessage(cStringIO.StringIO()) return False res.msg = httplib.HTTPMessage(res.fp) res.msg.fp = None # are we using the chunked-style of transfer encoding? trenc = res.msg.getheader('transfer-encoding') if trenc and trenc.lower() == "chunked": res.chunked = 1 res.chunk_left = None else: res.chunked = 0 # will the connection close at the end of the response? res.will_close = res._check_close() # do we have a Content-Length? # NOTE: RFC 2616, section 4.4, #3 says we ignore this if # transfer-encoding is "chunked" length = res.msg.getheader('content-length') if length and not res.chunked: try: res.length = int(length) except ValueError: res.length = None else: if res.length < 0: # ignore nonsensical negative lengths res.length = None else: res.length = None # does the body have a fixed length? (of zero) if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or 100 <= status < 200 or # 1xx codes res._method == 'HEAD'): res.length = 0 # if the connection remains open, and we aren't using chunked, and # a content-length was not provided, then assume that the connection # WILL close. if (not res.will_close and not res.chunked and res.length is None): res.will_close = 1 self.proxyres = res return False
def begin(self): if self.msg is not None: # we've already started reading the response return # read until we get a non-100 response while True: version, status, reason = self._read_status() if status != httplib.CONTINUE: break # skip the header from the 100 response while True: skip = self.fp.readline(httplib._MAXLINE + 1) if len(skip) > httplib._MAXLINE: raise httplib.LineTooLong("header line") skip = skip.strip() if not skip: break if self.debuglevel > 0: print "header:", skip self.status = status self.reason = reason.strip() if version == 'HTTP/1.0': self.version = 10 elif version.startswith('HTTP/1.'): self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 elif version == 'HTTP/0.9': self.version = 9 else: raise httplib.UnknownProtocol(version) if self.version == 9: self.length = None self.chunked = 0 self.will_close = 1 self.msg = httplib.HTTPMessage(StringIO()) return self.msg = httplib.HTTPMessage(self.fp, 0) if self.debuglevel > 0: for hdr in self.msg.headers: print "header:", hdr, # don't let the msg keep an fp self.msg.fp = None # are we using the chunked-style of transfer encoding? tr_enc = self.msg.getheader('transfer-encoding') if tr_enc and tr_enc.lower() == "chunked": self.chunked = 1 self.chunk_left = None else: self.chunked = 0 # will the connection close at the end of the response? self.will_close = self._check_close() # do we have a Content-Length? # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" length = self._get_content_length() if length and not self.chunked: try: self.length = int(length) except (ValueError, TypeError): self.length = None else: if self.length < 0: # ignore nonsensical negative lengths self.length = None else: self.length = None # does the body have a fixed length? (of zero) if (status == NO_CONTENT or status == httplib.NOT_MODIFIED or 100 <= status < 200 or # 1xx codes self._method == 'HEAD'): self.length = 0 # if the connection remains open, and we aren't using chunked, and # a content-length was not provided, then assume that the connection # WILL close. if not self.will_close and \ not self.chunked and \ self.length is None: self.will_close = 1