2017-09-27 14 views
1

短いバージョン:HTTPリクエストをエンコードしてレスポンスをデコードする簡単なAPIはありますか?PythonでHTTPリクエストをエンコードする

ロングバージョン:paramikoを使ってサーバーとSSHセッションを開く組み込みソフトウェアをいくつか書いています。私はtransport.open_channel('direct-tcpip', <remote address>, <source address>)で開かれたSSHチャンネルを介してHTTPリクエストを行う必要があります。

requestsにはトランスポートアダプタがあり、これにより、自分のトランスポートを代用することができます。しかし、sendによって提供されるインターフェイスは、BaseAdapterは、PreparedRequestというオブジェクトを受け入れます。このオブジェクトは、(a)リモートアドレスを有用な方法で提供しません。ホストとポートを見つけるためにURLを解析する必要があり、(b)リクエストのエンコードされたバージョンを提供せず、ヘッダーの辞書とエンコードされたボディ(存在する場合)のみを提供します。また、レスポンスのデコードに役立たないこともあります。 HTTPAdapterは、リクエストのエンコード、ネットワーク接続の作成、バイトの送信、応答バイトの受信、応答のデコードなど、ロット全体をurllib3に委ねます。

urllib3は、同様に、http.clientおよびhttp.clientHTTPConnectionクラスには、エンコードとネットワーク操作が混乱しています。

「HTTPサーバに送信するバイト数を教えてください。」と「HTTPサーバからバイトがたくさんあり、それらを有用なPythonオブジェクトに変換する」という簡単な方法がありますか?

答えて

2

これは、私が思い付くことができ、この最も簡単な実装である:、

from http.client import HTTPConnection 
import requests 
from requests.structures import CaseInsensitiveDict 
from urllib.parse import urlparse 
from argparse import ArgumentParser 

class TunneledHTTPConnection(HTTPConnection): 
    def __init__(self, transport, *args, **kwargs): 
     self.ssh_transport = transport 
     HTTPConnection.__init__(self, *args, **kwargs) 

    def connect(self): 
     self.sock = self.ssh_transport.open_channel(
      'direct-tcpip', (self.host, self.port), ('localhost', 0) 
     ) 

class TunneledHTTPAdapter(requests.adapters.BaseAdapter): 
    def __init__(self, transport): 
     self.transport = transport 

    def close(self): 
     pass 

    def send(self, request, **kwargs): 
     scheme, location, path, params, query, anchor = urlparse(request.url) 
     if ':' in location: 
      host, port = location.split(':') 
      port = int(port) 
     else: 
      host = location 
      port = 80 

     connection = TunneledHTTPConnection(self.transport, host, port) 
     connection.request(method=request.method, 
          url=request.url, 
          body=request.body, 
          headers=request.headers) 
     r = connection.getresponse() 
     resp = requests.Response() 
     resp.status_code = r.status 
     resp.headers = CaseInsensitiveDict(r.headers) 
     resp.raw = r 
     resp.reason = r.reason 
     resp.url = request.url 
     resp.request = request 
     resp.connection = connection 
     resp.encoding = requests.utils.get_encoding_from_headers(response.headers) 
     requests.cookies.extract_cookies_to_jar(resp.cookies, request, r) 
     return resp 

if __name__ == '__main__': 
    import paramiko 

    parser = ArgumentParser() 
    parser.add_argument('-p', help='Port the SSH server listens on', default=22) 
    parser.add_argument('host', help='SSH server to tunnel through') 
    parser.add_argument('username', help='Username on SSH server') 
    args = parser.parse_args() 

    client = paramiko.SSHClient() 
    client.load_system_host_keys() 
    client.connect(args.host, args.p, username=args.username) 

は、それが処理しないことをBaseAdapter.sendにさまざまなオプションがありますが、それは完全にように、接続プーリングなどの問題を無視して、しかし、それは仕事を完了します。

0

独自のSOCKS4プロキシを作成し、それをlocalhostで実行してから、HTTPリクエストをポイントすることができます。たとえば、https://urllib3.readthedocs.io/en/latest/advanced-usage.htmlは、urllib3でSOCKSプロキシを使用する方法について説明しています。

SOCKS4 is basically生のHTTP/TCPトラフィックに続く簡単なハンドシェイク。ハンドシェイクは、ターゲットIPアドレスとポートを伝えます。したがって、プロキシは、SOCKSサーバーであることをクライアントに認識させるためにハンドシェイクを行うことができ、プロキシはSSHセッションに直接「真の」トラフィックを送信できます(逆方向の応答をプロキシすることもできます)。

このアプローチのクールな点は、それが数多くのクライアントで機能することです.SOCKSは長い間普及しています。

+0

これは興味深い考えですが、私が後にしているよりも複雑です。私は(私はすぐに答えとして追加する)動作する方法を考え出したが、それはまだ非常に複雑すぎるようです。 – Tom

関連する問題