【Python】HTTP通信でGETを直接書く(proxy経由)

# はじめに

前の投稿に続いて、今回はproxy を通すところを試してみました。これがやりたかった。

https://qiita.com/XPT60/items/da1cece630e7548186c6



# 内容

まず、適当なサーバに対してGET を送り、レスポンス（短いのがいいな）が返ってくるの確認します。

```
$ curl http://google.com
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
```

これをproxy経由にしてみます。

### 無料のproxy server 

Google 先生に free proxy サーバと問い合わせてみると、いろいろなサイトを教えてくれます。
私は、以下から日本国内にあるものを見つけ、利用させていただくことにしました。

https://www.freeproxylists.net/ja/



### curl でproxy経由させたときの送受信

前回と同様にcURLを使って調べます。proxy server を指定するには、-x を使います。

```
$ curl -v google.com:80 -x http://116.80.45.7:80
*   Trying 116.80.45.7:80...
* TCP_NODELAY set
* Connected to 116.80.45.7 (116.80.45.7) port 80 (#0)
> GET http://google.com:80/ HTTP/1.1
> Host: google.com
> User-Agent: curl/7.68.0
> Accept: */*
> Proxy-Connection: Keep-Alive
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 301 Moved Permanently
< Location: http://www.google.com/
< Content-Type: text/html; charset=UTF-8
< Date: Fri, 08 Oct 2021 13:32:22 GMT
< Expires: Sun, 07 Nov 2021 13:32:22 GMT
< Cache-Control: public, max-age=2592000
< Server: gws
< Content-Length: 219
< X-XSS-Protection: 0
< X-Frame-Options: SAMEORIGIN
<
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
* Connection #0 to host 116.80.45.7 left intact
```

リクエストの部分で、目的とするhost を書いています。このリクエストをproxy server に送っています。

### python code で通信してみる

なので、同じことをしてみます。コードは前回のを少し変えてこんな感じです。

```Python
import socket
proxy_hostname, proxy_port = "116.80.45.7", 80
hostname, port = "google.com", 80
server_request = \
    f"GET http://{hostname}:{port}/ HTTP/1.1\r\n" \
    + f"Host: {hostname}:{port}\r\n" \
    + "User-Agent: MyPython\r\n" \
    + "Accept: */*\r\n\r\n"
print(f"request:\n{server_request}")
try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.settimeout(5.0)
    s.connect_ex( (proxy_hostname, proxy_port) )
    #s.settimeout(None)
    ret = s.sendall(bytes(server_request, 'utf-8'))
except Exception as e:
    print(f"{e}")
    raise e

try:
    data = s.recv(8192*16)
    print( f"response({len(data)}):")
    print(data.decode('unicode-escape'))
except Exception as e:
    raise e
```


実行してみると、想定通り。

```
$ python3 python_proxy_get.py
request:
GET http://google.com:80/ HTTP/1.1
Host: google.com:80
User-Agent: MyPhthon
Accept: */*


response(528):
HTTP/1.1 301 Moved Permanently
Location: http://www.google.com/
Content-Type: text/html; charset=UTF-8
Date: Fri, 08 Oct 2021 13:28:44 GMT
Expires: Sun, 07 Nov 2021 13:28:44 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 219
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="http://www.google.com/">here</A>.
</BODY></HTML>
```

無事に動いてます。やったー、今日は調子がいいぞ。

### Proxy tunnel を通す(CONNECT メソドの登場)

ここで、curl のオプションに ```--proxytunnel```を付けてみます。

```
$ curl -v google.com -x http://116.80.45.7:80 --proxytunnel
*   Trying 116.80.45.7:80...
* TCP_NODELAY set
* Connected to 116.80.45.7 (116.80.45.7) port 80 (#0)
* allocate connect buffer!
* Establish HTTP proxy tunnel to google.com:80
> CONNECT google.com:80 HTTP/1.1
> Host: google.com:80
> User-Agent: curl/7.68.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 405 Method Not Allowed
< Content-Type: text/html; charset=UTF-8
< Referrer-Policy: no-referrer
< Content-Length: 1592
< Date: Fri, 08 Oct 2021 13:17:05 GMT
< Connection: close
<
* Received HTTP code 405 from proxy after CONNECT
* CONNECT phase completed!
* Closing connection 0
curl: (56) Received HTTP code 405 from proxy after CONNECT

```

先程のpython script でリクエストヘッダを書き換えます。

```Python
server_request = \
    f"CONNECT {hostname}:{port}/ HTTP/1.1\r\n" \
    + f"Host: {hostname}:{port}\r\n" \
    + "User-Agent: MyPython\r\n" \
    + "Accept: */*\r\n\r\n"
```

とりあえず同じものを送れるようにはなり、同じ結果が返ってきました。

# まとめ

HTTP Proxy を理解するために、curl で送受信されるデータを見て、同じ内容をpython で送ってみた。
とりあえず、これでproxy を通すベタな実装はできる気がした。

本当は、おじさんは便利なモジュールを使いたいのだ。そうなのだが誰も教えてくれないので、自分で実装しているのです。
(2021/10/08)