| 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..ac6c834cdba33907783f41ecd004675981951afc
|
| --- /dev/null
|
| +++ b/util/net/http_transport_test_server.py
|
| @@ -0,0 +1,137 @@
|
| +#!/usr/bin/env python
|
| +# coding: utf-8
|
| +
|
| +# Copyright 2014 The Crashpad Authors. All rights reserved.
|
| +#
|
| +# 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.
|
| +
|
| +This server is written in Python since it provides a simple HTTP stack, and
|
| +because parsing Chunked encoding is safer and easier in a memory-safe language.
|
| +This could easily have been written in C++ instead.
|
| +"""
|
| +
|
| +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.get('Content-Length', -1))
|
| + 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
|
| + RFC 7230 ยง4.1. This returns the result 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 unsigned short to the parent process.
|
| + sys.stdout.write(struct.pack('=H', server.server_address[1]))
|
| + sys.stdout.flush()
|
| +
|
| + # Read the desired test response code as an unsigned short from the parent
|
| + # process.
|
| + RequestHandler.response_code = \
|
| + struct.unpack('=H', sys.stdin.read(struct.calcsize('=H')))[0]
|
| +
|
| + # Handle the request.
|
| + server.handle_request()
|
| +
|
| +if __name__ == '__main__':
|
| + Main()
|
|
|