Chromium Code Reviews| Index: util/net/http_transport_test_server.py |
| diff --git a/util/net/http_transport_test_server.py b/util/net/http_transport_test_server.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..b20ef9ee0da5032fd4c66d0a4dffe8d6ea1a6393 |
| --- /dev/null |
| +++ b/util/net/http_transport_test_server.py |
| @@ -0,0 +1,131 @@ |
| +#!/usr/bin/env python |
| +# Copyright 2014 The Crashpad Authors. All rights reserved. |
|
Mark Mentovai
2014/10/30 21:13:55
Gimme a blank line before this.
Robert Sesek
2014/10/31 14:48:15
Done.
|
| +# |
| +# Licensed under the Apache License, Version 2.0 (the "License"); |
| +# you may not use this file except in compliance with the License. |
| +# You may obtain a copy of the License at |
| +# |
| +# http://www.apache.org/licenses/LICENSE-2.0 |
| +# |
| +# Unless required by applicable law or agreed to in writing, software |
| +# distributed under the License is distributed on an "AS IS" BASIS, |
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| +# See the License for the specific language governing permissions and |
| +# limitations under the License. |
| + |
| +"""A one-shot testing webserver. |
| + |
| +When invoked, this server will write an integer to stdout, indiciating on which |
| +port the server is listening. It will then read one integer from stdin, |
| +indiciating the response code to set for a request. The server will process |
| +one HTTP request, writing it out entirely to stdout, and will then terminate. |
|
Mark Mentovai
2014/10/30 21:13:55
Do you want to say up here that the main motivatio
Robert Sesek
2014/10/31 14:48:15
Done.
|
| +""" |
| + |
| +import BaseHTTPServer |
| +import struct |
| +import sys |
| + |
| +class BufferedReadFile(object): |
| + """A File-like object that stores all read contents into a buffer.""" |
| + |
| + def __init__(self, real_file): |
| + self.file = real_file |
| + self.buffer = "" |
| + |
| + def read(self, size=-1): |
| + buf = self.file.read(size) |
| + self.buffer += buf |
| + return buf |
| + |
| + def readline(self, size=-1): |
| + buf = self.file.readline(size) |
| + self.buffer += buf |
| + return buf |
| + |
| + def flush(self): |
| + self.file.flush() |
| + |
| + def close(self): |
| + self.file.close() |
| + |
| + |
| +class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): |
| + response_code = 500 |
| + |
| + def handle_one_request(self): |
| + # Wrap the rfile in the buffering file object so that the raw header block |
| + # can be written to stdout after it is parsed. |
| + self.rfile = BufferedReadFile(self.rfile) |
| + BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self) |
| + |
| + def do_POST(self): |
| + writer = sys.stdout |
| + |
| + writer.write(self.rfile.buffer) |
| + self.rfile.buffer = '' |
| + |
| + if self.headers.get('Transfer-Encoding', '') == 'Chunked': |
| + body = self.handle_chunked_encoding() |
| + else: |
| + length = int(self.headers['Content-Length']) |
|
Mark Mentovai
2014/10/30 21:13:55
This field may not be present. If it’s not, you sh
Robert Sesek
2014/10/31 14:48:15
Done.
|
| + body = self.rfile.read(length) |
| + |
| + self.send_response(self.response_code) |
| + writer.write(body) |
| + writer.flush() |
| + |
| + def handle_chunked_encoding(self): |
| + """This parses a "Transfer-Encoding: Chunked" body in accordance with |
| + <http://tools.ietf.org/html/rfc7230#section-4.1>. This returns the result |
|
Mark Mentovai
2014/10/30 21:13:55
RFCs are pretty canonical, you don’t need a link,
Robert Sesek
2014/10/31 14:48:15
Done.
|
| + as a string. |
| + """ |
| + body = '' |
| + chunk_size = self.read_chunk_size() |
| + while chunk_size > 0: |
| + # Read the body. |
| + data = self.rfile.read(chunk_size) |
| + chunk_size -= len(data) |
| + body += data |
| + |
| + # Finished reading this chunk. |
| + if chunk_size == 0: |
| + # Read through any trailer fields. |
| + trailer_line = self.rfile.readline() |
| + while trailer_line.strip() != '': |
| + trailer_line = self.rfile.readline() |
| + |
| + # Read the chunk size. |
| + chunk_size = self.read_chunk_size() |
| + return body |
| + |
| + def read_chunk_size(self): |
| + # Read the whole line, including the \r\n. |
| + chunk_size_and_ext_line = self.rfile.readline() |
| + # Look for a chunk extension. |
| + chunk_size_end = chunk_size_and_ext_line.find(';') |
| + if chunk_size_end == -1: |
| + # No chunk extensions; just encounter the end of line. |
| + chunk_size_end = chunk_size_and_ext_line.find('\r') |
| + if chunk_size_end == -1: |
| + self.send_response(400) # Bad request. |
| + return -1 |
| + return int(chunk_size_and_ext_line[:chunk_size_end], base=16) |
| + |
| + |
| +def Main(): |
| + # Start the server. |
| + server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler) |
| + |
| + # Write the port as an int to the parent process. |
| + sys.stdout.write(struct.pack('i', server.server_address[1])) |
| + sys.stdout.flush() |
| + |
| + # Read the desired test response code from the parent process. |
| + RequestHandler.response_code = \ |
| + struct.unpack('i', sys.stdin.read(struct.calcsize('i')))[0] |
| + |
| + # Handle the request. |
| + server.handle_request() |
| + |
| +if __name__ == '__main__': |
| + Main() |