pythonでsocketを使ってHTTP GETしてみる。

「Webを支える技術」を読んでいたら、HTTP GETしてみたくなったのでpythonのsocketを使ってやってみました。
サーバー側はsocketじゃなくてflaskです。apacheとか、まぁ、ふつうにgoogleとかにアクセスしてもいいよね。

http_request_tester.py
[python]
# vim: fileencoding=utf-8
import socket
import urllib

class Response(object):
HEADER_BODY_SPLITTER = "rnrn"

def __init__(self, response):
split_index = response.index(self.HEADER_BODY_SPLITTER)
self.header = response[:split_index]
self.status = self._get_status(self.header)
self.headers = self._header2dict(self.header)
self.body = response[split_index+len(self.HEADER_BODY_SPLITTER):]

def _get_status(self, header):
return header[:header.index("rn")]

def _header2dict(self, header):
headers = {}
for line in header.splitlines()[1:]:
headers[line[:line.index(":")]] = line[line.index(":")+1:].strip()
return headers

class HttpRequestTester(object):
sock = None

def __init__(self, host=’localhost’, port=80):
self.host = host
self.ip = socket.gethostbyname(host)
self.port = port

def __enter__(self):
return self

def __exit__(self, exc_type, exc_val, exc_tb):
pass

def _request(self, request_message):
try:
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect((self.ip, self.port))

self.sock.sendall(request_message)

response = ""
while True:
data = self.sock.recv(1024)
if not data:
break
response += data

finally:
if self.sock:
self.sock.close()
self.sock = None

return Response(response)

def get(self, path, params={}):
template = "GET {path} HTTP/1.1n"
template += "Host: {host}n"
template += "Connection: {connection}n"
template += "User-Agent: {user_agent}n"
template += "Accept-Language: {accept_language}n"
template += "Referer: {referer}n"
template += "n"
request_message = template.format(path=path, host=self.host,
connection=’close’,
user_agent=’HttpRequestTester/0.0.1′,
accept_language=’ja’,
referer=”)

print "REQUEST: {request}".format(request=request_message)

return self._request(request_message)

def post(self, path, params={}):
template = "POST {path} HTTP/1.1n"
template += "Host: {host}{port}n"
template += "Content-Type: {content_type}n"
#template += "Connection: {connection}n"
#template += "User-Agent: {user_agent}n"
#template += "Accept-Language: {accept_language}n"
#template += "Referer: {referer}n"
template += "n"
template += "{param_string}n"
template += "n"
request_message = template.format(path=path, host=self.host,
connection=’close’,
content_type=’text/plain; charset=utf-8′,
user_agent=’HttpRequestTester/0.0.1′,
accept_language=’ja’,
referer=”,
port=":"+str(self.port) if self.port!=80 else "",
param_string=urllib.urlencode(params))

print "REQUEST: {request}".format(request=request_message)

return self._request(request_message)

if __name__ == "__main__":
HOST = ‘localhost’
PORT = 5000

http = HttpRequestTester(HOST, PORT)
res = http.get("/")
print res.status
print res.headers
print res.body
raw_input()

res = http.post("/", params={"hoge":"hogehoge"})
print res.status
print res.headers
print res.body
raw_input()
[/python]

flaskで作ったテストサーバー
[python]
from flask import Flask, request

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def post():
if request.method == "GET":
return "GET DESU"

return "POST DESU"

if __name__ == ‘__main__’:
app.run(debug=True)
[/python]

GETやPOSTのConnectionとかは適当。
「Webを支える技術」の8章とか巻末あたりに書いてそうだけど、まだ読んでないのでまた今度。

参考