2016-08-21 15 views
1

私は、CONNECT要求に応答して送信されたヘッダーに有用な情報を入れるプロキシサービス(proxymesh)を使用しています。何らかの理由で、Python's httplib doesn't parse themについて:CONNECTヘッダーを読む

> CONNECT example.com:443 HTTP/1.1 
> Host: example.com:443 
> 
< HTTP/1.1 200 Connection established 
< X-Useful-Header: value # completely ignored 
< 

requestsモジュールは、内部httplibを使用していますので、同様にそれらを無視します。 CONNECTリクエストからヘッダーを抽出するにはどうすればよいですか?

+0

この画像にはいくつか問題があります。まず、CONNECTへの応答にヘッダーを付けるのは大丈夫です。第2に、ペイロードを持ってはいけません。なぜなら、次に起こるのはクライアントが開始したTLSハンドシェイクであるからです。 – Adrien

+0

@Adrien:私が言ったように、 'httplib'は' CONNECT'の後に送られたヘッダを破棄します。それはそうではないが、そうである。ペイロード部分に関しては、私は同意する、それは間違っている。私はそれを削除しました。 – Blender

答えて

1

Pythonのhttplibは、実際にトンネルを作成するときにこれらのヘッダを無視します。それはハックですが、あなたはそれらを傍受し、実際のHTTPレスポンスのヘッダに「ヘッダ」行をマージすることができます:

import socket 
import httplib 
import requests 

from requests.packages.urllib3.connection import HTTPSConnection 
from requests.packages.urllib3.connectionpool import HTTPSConnectionPool 
from requests.packages.urllib3.poolmanager import ProxyManager 

from requests.adapters import HTTPAdapter 


class ProxyHeaderHTTPSConnection(HTTPSConnection): 
    def __init__(self, *args, **kwargs): 
     super(ProxyHeaderHTTPSConnection, self).__init__(*args, **kwargs) 
     self._proxy_headers = [] 

    def _tunnel(self): 
     self.send("CONNECT %s:%d HTTP/1.0\r\n" % (self._tunnel_host, self._tunnel_port)) 

     for header, value in self._tunnel_headers.iteritems(): 
      self.send("%s: %s\r\n" % (header, value)) 

     self.send("\r\n") 

     response = self.response_class(self.sock, strict=self.strict, method=self._method) 
     version, code, message = response._read_status() 

     if version == "HTTP/0.9": 
      # HTTP/0.9 doesn't support the CONNECT verb, so if httplib has 
      # concluded HTTP/0.9 is being used something has gone wrong. 
      self.close() 
      raise socket.error("Invalid response from tunnel request") 

     if code != 200: 
      self.close() 
      raise socket.error("Tunnel connection failed: %d %s" % (code, message.strip())) 

     self._proxy_headers = [] 

     while True: 
      line = response.fp.readline(httplib._MAXLINE + 1) 

      if len(line) > httplib._MAXLINE: 
       raise LineTooLong("header line") 

      if not line or line == '\r\n': 
       break 

      # The line is a header, save it 
      if ':' in line: 
       self._proxy_headers.append(line) 

    def getresponse(self, buffering=False): 
     response = super(ProxyHeaderHTTPSConnection, self).getresponse(buffering) 
     response.msg.headers.extend(self._proxy_headers) 

     return response 


class ProxyHeaderHTTPSConnectionPool(HTTPSConnectionPool): 
    ConnectionCls = ProxyHeaderHTTPSConnection 


class ProxyHeaderProxyManager(ProxyManager): 
    def _new_pool(self, scheme, host, port): 
     assert scheme == 'https' 

     return ProxyHeaderHTTPSConnectionPool(host, port, **self.connection_pool_kw) 


class ProxyHeaderHTTPAdapter(HTTPAdapter): 
    def proxy_manager_for(self, proxy, **proxy_kwargs): 
     if proxy in self.proxy_manager: 
      manager = self.proxy_manager[proxy] 
     else: 
      proxy_headers = self.proxy_headers(proxy) 
      manager = self.proxy_manager[proxy] = ProxyHeaderProxyManager(
       proxy_url=proxy, 
       proxy_headers=proxy_headers, 
       num_pools=self._pool_connections, 
       maxsize=self._pool_maxsize, 
       block=self._pool_block, 
       **proxy_kwargs) 

     return manager 

あなたは、セッション上にアダプターをインストールすることができます。

session = requests.Session() 
session.mount('https://', ProxyHeaderHTTPAdapter()) 

response = session.get('https://example.com', proxies={...}) 

プロキシのヘッダう応答ヘッダーとマージされるので、プロキシが応答ヘッダーを直接変更したかのように動作するはずです。

関連する問題