httplib-HTTPConnection

api的使用:
html

>>> import httplib, urllib

>>> params = urllib.urlencode({'@number': 12524, '@type': 'issue', '@action': 'show'})
>>> headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

>>> conn = httplib.HTTPConnection("bugs.python.org")
>>> conn.request("POST", "", params, headers)

>>> response = conn.getresponse()
>>> print response.status, response.reason
302 Found

>>> data = response.read()
>>> data
'Redirecting to <a href=" 

>>> conn.close()


HTTPConnection必須以server location來初始化,意圖就是一個HTTPConnection表示,只能對一個location請求。python

用戶調用conn.request指定method,path,body,headers,發起請求。api

調用conn.getresponse返回HTTPResponse響應。app


再來看看它有什麼其餘的接口,socket

connect:更新self.sock屬性。this

putrequest:構建起始行和HOST和Accept-Encoding頭部,由於這兩個和http的version有關。url

putheader:構建頭部行spa

endheaders:發送起始行,headers和bodydebug

close:關閉鏈接設計

set_tunnel:設置隧道


能夠看出HTTPConnection的接口,是從業務流程來設計的。

首先創建socket鏈接,

而後構建起始行,

構建headers,

發送request的請求,

而後返回http響應。


而後看看HTTPSConnection是基於HTTPConnection,怎麼實現的:

def connect(self):
    "Connect to a host on a given (SSL) port."
     sock = socket.create_connection((self.host, self.port),
                                     self.timeout, self.source_address)
     if self._tunnel_host:
         self.sock = sock
         self._tunnel()
     self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file)

它複寫了connect方法,https須要key_file, cert_file來創建鏈接。但沒有使用connect的參數傳遞,而是經過類的__init__方法傳遞,經過屬性。

這種形式比connect參數傳遞會好,由於接口的設計,若是兼顧到不少功能,會有許多默認參數。並且對之後的擴展,也很差。但這種__init__方法,也須要考慮到許多默認參數,並且參數的做用相比沒那麼直接。


再接着看看它是如何發送數據的。

def _output(self, s):
        """Add a line of output to the current request buffer.
        Assumes that the line does *not* end with \\r\\n.
        """
        self._buffer.append(s)

self._buffer = [],它的元素是http頭部的每一行。在_send_output方法中,會被格式化成標準http格式。

def _send_output(self, message_body=None):
        """Send the currently buffered request and clear the buffer.
        Appends an extra \\r\\n to the buffer.
        A message_body may be specified, to be appended to the request.
        """
        self._buffer.extend(("", ""))
        msg = "\r\n".join(self._buffer)
        del self._buffer[:]
        # If msg and message_body are sent in a single send() call,
        # it will avoid performance problems caused by the interaction
        # between delayed ack and the Nagle algorithm.
        if isinstance(message_body, str):
            msg += message_body
            message_body = None
        self.send(msg)
        if message_body is not None:
            #message_body was not a string (i.e. it is a file) and
            #we must run the risk of Nagle
            self.send(message_body)

能夠看到msg變量是由self._buffer經過\r\n來鏈接起來的,格式化成標準的http頭部。而後調用send方法,把http頭部和http實體發送出去。

def send(self, data):
        """Send `data' to the server."""
        if self.sock is None:
            if self.auto_open:
                self.connect()
            else:
                raise NotConnected()
        if self.debuglevel > 0:
            print "send:", repr(data)
        blocksize = 8192
        if hasattr(data,'read') and not isinstance(data, array):
            if self.debuglevel > 0: print "sendIng a read()able"
            datablock = data.read(blocksize)
            while datablock:
                self.sock.sendall(datablock)
                datablock = data.read(blocksize)
        else:
            self.sock.sendall(data)

send方法,只是負責向socket發送數據。它支持data的read屬性,會不斷的從data中獲取數據,而後發送出去。

    def putheader(self, header, *values):
        """Send a request header line to the server.
        For example: h.putheader('Accept', 'text/html')
        """
        if self.__state != _CS_REQ_STARTED:
            raise CannotSendHeader()
        hdr = '%s: %s' % (header, '\r\n\t'.join([str(v) for v in values]))
        self._output(hdr)

putheader方法很簡單,只是簡單的構建頭部。

    def request(self, method, url, body=None, headers={}):
        """Send a complete request to the server."""
        self._send_request(method, url, body, headers)

_send_request方法的定義:

def _send_request(self, method, url, body, headers):
        # Honor explicitly requested Host: and Accept-Encoding: headers.
        header_names = dict.fromkeys([k.lower() for k in headers])
        skips = {}
        if 'host' in header_names:
            skips['skip_host'] = 1
        if 'accept-encoding' in header_names:
            skips['skip_accept_encoding'] = 1
        self.putrequest(method, url, **skips)
        if body is not None and 'content-length' not in header_names:
            self._set_content_length(body)
        for hdr, value in headers.iteritems():
            self.putheader(hdr, value)
        self.endheaders(body)


首先是調用putrequest構建起始行

而後調用putheader構建頭部

最後調用endheaders構建實體,而且發送。

    def getresponse(self, buffering=False):
        "Get the response from the server."
        if self.__state != _CS_REQ_SENT or self.__response:
            raise ResponseNotReady()
        args = (self.sock,)
        kwds = {"strict":self.strict, "method":self._method}
        if self.debuglevel > 0:
            args += (self.debuglevel,)
        if buffering:
            #only add this keyword if non-default, for compatibility with
            #other response_classes.
            kwds["buffering"] = True;
        response = self.response_class(*args, **kwds)
        response.begin()
        assert response.will_close != _UNKNOWN
        self.__state = _CS_IDLE
        if response.will_close:
            # this effectively passes the connection to the response
            self.close()
        else:
            # remember this, so we can tell when it is complete
            self.__response = response
        return response

getresponse方法,使用self.sock實例化HTTPResponse對象,而後調用HTTPResponse的begin方法。HTTPResponse主要負責基於socket,對http響應的解析。在後面有講解。

相關文章
相關標籤/搜索