1. 程式人生 > >HTTP CONNECT 方法

HTTP CONNECT 方法

HTTP request有常用的5種方法:

HTTP request 方法 描述
GET 從指定的資源請求資料
POST 向指定的資源提交要被處理的資料
PUT 上傳指定的 URI 表示
DELETE 移除指定的資源
HEAD 與 GET 方法相同,但只返回 HTTP 報頭,不返回文件主體

但從HTTP 1.1以來,有一種新的方法無詳細說明,卻被廣泛使用,這就是CONNECT。

它並非使用在網頁開發方面,而是在閘道器這個看似透明的地方。

借用HTTP權威指南中的圖:


如上圖所示,它的原理也很簡單:

通常由客戶端發起到閘道器,閘道器接受後建立對目標伺服器的連線,成功後告知客戶端。

然後客戶端就可以傳送任意資料到目標伺服器了。

這裡我借用一個proxy的程式碼來說明一下CONNECT方法的實現:

import BaseHTTPServer, select, socket, SocketServer, urlparse

class ProxyHandler (BaseHTTPServer.BaseHTTPRequestHandler):

    def _connect_to(self, netloc, soc):
        i = netloc.find(':')
        if i >= 0:
            host_port = netloc[:i], int(netloc[i+1:])
        else:
            host_port = netloc, 80
        print "\t" "connect to %s:%d" % host_port
        try: soc.connect(host_port)
        except socket.error, arg:
            try: msg = arg[1]
            except: msg = arg
            self.send_error(404, msg)
            return 0
        return 1

    def do_CONNECT(self):
        print "In do_CONNECT, self.path: %s" % self.path
        soc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            if self._connect_to(self.path, soc):
                self.log_request(200)
                self.wfile.write(self.protocol_version +
                                 " 200 Connection established\r\n")
                self.wfile.write("Proxy-agent: %s\r\n" % self.version_string())
                self.wfile.write("\r\n")
                self._read_write(soc, 300)
        finally:
            print "\t" "bye"
            soc.close()
            self.connection.close()

    def _read_write(self, soc, max_idling=20):
        iw = [self.connection, soc]
        ow = []
        count = 0
        while 1:
            count += 1
            (ins, _, exs) = select.select(iw, ow, iw, 3)
            if exs: break
            if ins:
                for i in ins:
                    if i is soc:
                        out = self.connection
                    else:
                        out = soc
                    data = i.recv(8192)
                    if data:
                        out.send(data)
                        count = 0
            else:
                print "\t" "idle", count
            if count == max_idling: break

這裡省略了一些其他部分的程式碼,比如正常的HTTP方法的處理函式。

通過BaseHTTPServer的處理,程式執行的時候在收到HTTP CONNECT方法後呼叫do_CONNECT()函式。

do_CONNECT()中呼叫_connect_to()函式建立對目標地址的socket連線,成功後通過self.wfile.write()向客戶端返回建立成功的資訊,然後通過_read_write()方法來實現資料的通訊。

值得說明的是,一個proxy server收到的path並非普通Server收到的URL path資訊,而是帶有URL scheme,URL host的完整URL資訊。所以在_connect_to()函式中會通過判斷冒號的位置來解析URL host和URL port資訊。