HTTPリクエストのモックを作る
SimpleHTTPRequestHandlerを利用してHTTPリクエストのモック(MockConnection)を作りました。というのはたぶん奇妙な言い方で、SimpleHTTPRequestHandlerの動作を確認してみました。
#!/usr/bin/env python2.6 import unittest from test import test_support from SimpleHTTPServer import SimpleHTTPRequestHandler import os import StringIO import re class MockConnection(object): def __init__(self, path): self.path = path def makefile(self, mode='r', bufsize=-1): if mode == 'wb': return StringIO.StringIO() else: return open(self.path, mode, bufsize) class TestRequestHandler(SimpleHTTPRequestHandler): def __init__(self, request, client_address, server): SimpleHTTPRequestHandler.__init__(self, request, client_address, server) def setup(self): SimpleHTTPRequestHandler.setup(self) self.rfile.seek(0) def finish(self): self.response = self.wfile.getvalue() SimpleHTTPRequestHandler.finish(self) def getresponse(self): return self.response WEEK_REG = "(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)" MONTH_REG = "(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)" DATETIME_REG = " %s, \d{2} %s \d{4} \d{2}:\d{2}:\d{2} GMT" % (WEEK_REG, MONTH_REG) HEADER_REG = "HTTP/1\.0 %d %s\r\n"\ "Server: SimpleHTTP/0\.6 Python/2\.6\.5\r\n"\ "Date:%s\r\n"\ "Content-(?:T|t)ype: %s\r\n" ERROR_REG = "<head>\n"\ "<title>Error response</title>\n"\ "</head>\n"\ "<body>\n"\ "<h1>Error response</h1>\n"\ "<p>Error code %d\.\n"\ "<p>Message: %s\.\n"\ "<p>Error code explanation: %d = %s\.\n"\ "</body>\n" class HandlerTests(unittest.TestCase): @staticmethod def make_handler(request): path = "tmp.txt" f = open(path, 'aw') f.write(request) f.close() con = MockConnection(path) return TestRequestHandler(con, ('', 2323), None) def tearDown(self): path = "tmp.txt" if os.path.exists(path): os.remove(path) class HTTPRequestHandlerTests(HandlerTests): def test_mock_connection_09(self): request = "GET /_files/hello.txt HTTP/0.9\r\n"\ "Connection: close\r\n"\ "\r\n" self.assertEqual("Hello World\n", self.make_handler(request).getresponse()) @staticmethod def is_match(buf, values, request): exp = ''.join(buf) return re.match(exp % values, HTTPRequestHandlerTests.make_handler(request).getresponse()) is not None def test_mock_connection_GET_200(self): request = "GET /_files/hello.txt HTTP/1.0\r\n"\ "Connection: close\r\n"\ "\r\n" buf = [HEADER_REG, "Content-Length: \d+\r\n" "Last-Modified:%s\r\n" "\r\n" "Hello World\n" ] self.assertTrue(self.is_match(buf, (200, "OK", DATETIME_REG, "text/plain", DATETIME_REG), request)) def test_mock_connection_HEAD_200(self): request = "HEAD /_files/hello.txt HTTP/1.0\r\n"\ "Connection: close\r\n"\ "\r\n" buf = [HEADER_REG, "Content-Length: \d+\r\n" "Last-Modified:%s\r\n" "\r\n" ] self.assertTrue(self.is_match(buf, (200, "OK", DATETIME_REG, "text/plain", DATETIME_REG), request)) def test_mock_connection_dir(self): request = "GET /_files/ HTTP/1.0\r\n"\ "Connection: close\r\n"\ "\r\n" buf = [HEADER_REG, "Content-Length: \d+\r\n" "\r\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\"><html>\n" "<title>Directory listing for /_files/</title>\n" "<body>\n<h2>Directory listing for /_files/</h2>\n" "<hr>\n" "<ul>\n" "<li><a href=\"hello.txt\">hello.txt</a>\n" "</ul>\n" "<hr>\n" "</body>\n" "</html>\n" ] self.assertTrue(self.is_match(buf, (200, "OK", DATETIME_REG, "text/html"), request)) def test_mock_connection_501(self): request = "SPAM /_files/hello.txt HTTP/1.0\r\n"\ "Connection: close\r\n"\ "\r\n" code = 501 shortmessage = "Unsupported method \('SPAM'\)" longmessage = "Server does not support this operation" self.assertTrue(self.is_match([HEADER_REG, "Connection: close\r\n\r\n", ERROR_REG], (code, shortmessage, DATETIME_REG,\ "text/html", code, shortmessage, code, longmessage), request)) def test_mock_connection_404(self): request = "GET /no_such_file.txt HTTP/1.0\r\n"\ "Connection: close\r\n"\ "\r\n" code = 404 shortmessage = "File not found" longmessage = "Nothing matches the given URI" self.assertTrue(self.is_match([HEADER_REG, "Connection: close\r\n\r\n", ERROR_REG], (code, shortmessage, DATETIME_REG,\ "text/html", code, shortmessage, code, longmessage), request)) class HTTPRequestHandlerMethodTests(HandlerTests): def test_parse_request_valid(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET /_files/hello.txt HTTP/1.1\r\n" "Connection: close\r\n" "\r\n" ) handler.raw_requestline = handler.rfile.readline() self.assertTrue(handler.parse_request()) self.assertEqual("GET", handler.command) self.assertEqual("/_files/hello.txt", handler.path) self.assertEqual("HTTP/1.1", handler.request_version) def test_parse_request_bad_request_version(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET /_files/hello.txt BAD_REQUEST_VERSION\r\n" "Connection: close\r\n" "\r\n" ) handler.wfile = StringIO.StringIO() handler.raw_requestline = handler.rfile.readline() self.assertFalse(handler.parse_request()) code = 400 shortmessage = "Bad request version \('BAD_REQUEST_VERSION'\)" longmessage = "Bad request syntax or unsupported method" source = ERROR_REG % (code, shortmessage, code, longmessage) self.assertTrue(re.match(source, handler.wfile.getvalue()) is not None) handler.finish() def test_parse_request_invalid_http_version(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET /_files/hello.txt HTTP/2.0\r\n" "Connection: close\r\n" "\r\n" ) handler.wfile = StringIO.StringIO() handler.raw_requestline = handler.rfile.readline() self.assertFalse(handler.parse_request()) code = 505 shortmessage = "Invalid HTTP Version \(2.0\)" longmessage = "Cannot fulfill request\." source = ERROR_REG % (code, shortmessage, code, longmessage) self.assertTrue(re.match(source, handler.wfile.getvalue()) is not None) handler.finish() def test_parse_bad_http_0_9_request_type(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("HEAD /_files/hello.txt \r\n" "Connection: close\r\n" "\r\n" ) handler.wfile = StringIO.StringIO() handler.raw_requestline = handler.rfile.readline() self.assertFalse(handler.parse_request()) code = 400 shortmessage = "Bad HTTP/0.9 request type \('HEAD'\)" longmessage = "Bad request syntax or unsupported method" source = ERROR_REG % (code, shortmessage, code, longmessage) self.assertTrue(re.match(source, handler.wfile.getvalue()) is not None) handler.finish() def test_parse_bad_request_syntax(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("ONE_WORD_CASE\r\n" "Connection: close\r\n" "\r\n" ) handler.wfile = StringIO.StringIO() handler.raw_requestline = handler.rfile.readline() self.assertFalse(handler.parse_request()) code = 400 shortmessage = "Bad request syntax \('ONE_WORD_CASE'\)" longmessage = "Bad request syntax or unsupported method" source = ERROR_REG % (code, shortmessage, code, longmessage) self.assertTrue(re.match(source, handler.wfile.getvalue()) is not None) handler.finish() def test_send_message(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET PATH HTTP/1.0\r\n" "Connection: close\r\n" "\r\n" ) handler.raw_requestline = handler.rfile.readline() self.assertTrue(handler.parse_request()) handler.wfile = StringIO.StringIO() handler.close_connection = 0 handler.send_header("keyword", "value") self.assertEqual( "keyword: value\r\n" , handler.wfile.getvalue()) self.assertEqual(0, handler.close_connection) handler.finish() def test_send_message_close(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET PATH HTTP/1.0\r\n" "Connection: close\r\n" "\r\n" ) handler.raw_requestline = handler.rfile.readline() self.assertTrue(handler.parse_request()) handler.wfile = StringIO.StringIO() handler.close_connection = 0 handler.send_header("Connection", "close") self.assertEqual("Connection: close\r\n" , handler.wfile.getvalue()) self.assertEqual(1, handler.close_connection) handler.finish() def test_send_response(self): handler = self.make_handler("") handler.rfile = StringIO.StringIO("GET PATH HTTP/1.0\r\n" "Connection: close\r\n" "\r\n" ) handler.raw_requestline = handler.rfile.readline() self.assertTrue(handler.parse_request()) handler.wfile = StringIO.StringIO() handler.send_response(1024, "message") source = "HTTP/1.0 1024 message\r\n"\ "Server: SimpleHTTP/0.6 Python/2.6.5\r\n"\ "Date:%s\r\n" % DATETIME_REG self.assertTrue(re.match(source, handler.wfile.getvalue()) is not None) handler.finish() if __name__ == '__main__': test_support.run_unittest(HTTPRequestHandlerTests, HTTPRequestHandlerMethodTests)