Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # 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.
| |
| 3 # | |
| 4 # Licensed under the Apache License, Version 2.0 (the "License"); | |
| 5 # you may not use this file except in compliance with the License. | |
| 6 # You may obtain a copy of the License at | |
| 7 # | |
| 8 # http://www.apache.org/licenses/LICENSE-2.0 | |
| 9 # | |
| 10 # Unless required by applicable law or agreed to in writing, software | |
| 11 # distributed under the License is distributed on an "AS IS" BASIS, | |
| 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| 13 # See the License for the specific language governing permissions and | |
| 14 # limitations under the License. | |
| 15 | |
| 16 """A one-shot testing webserver. | |
| 17 | |
| 18 When invoked, this server will write an integer to stdout, indiciating on which | |
| 19 port the server is listening. It will then read one integer from stdin, | |
| 20 indiciating the response code to set for a request. The server will process | |
| 21 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.
| |
| 22 """ | |
| 23 | |
| 24 import BaseHTTPServer | |
| 25 import struct | |
| 26 import sys | |
| 27 | |
| 28 class BufferedReadFile(object): | |
| 29 """A File-like object that stores all read contents into a buffer.""" | |
| 30 | |
| 31 def __init__(self, real_file): | |
| 32 self.file = real_file | |
| 33 self.buffer = "" | |
| 34 | |
| 35 def read(self, size=-1): | |
| 36 buf = self.file.read(size) | |
| 37 self.buffer += buf | |
| 38 return buf | |
| 39 | |
| 40 def readline(self, size=-1): | |
| 41 buf = self.file.readline(size) | |
| 42 self.buffer += buf | |
| 43 return buf | |
| 44 | |
| 45 def flush(self): | |
| 46 self.file.flush() | |
| 47 | |
| 48 def close(self): | |
| 49 self.file.close() | |
| 50 | |
| 51 | |
| 52 class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | |
| 53 response_code = 500 | |
| 54 | |
| 55 def handle_one_request(self): | |
| 56 # Wrap the rfile in the buffering file object so that the raw header block | |
| 57 # can be written to stdout after it is parsed. | |
| 58 self.rfile = BufferedReadFile(self.rfile) | |
| 59 BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self) | |
| 60 | |
| 61 def do_POST(self): | |
| 62 writer = sys.stdout | |
| 63 | |
| 64 writer.write(self.rfile.buffer) | |
| 65 self.rfile.buffer = '' | |
| 66 | |
| 67 if self.headers.get('Transfer-Encoding', '') == 'Chunked': | |
| 68 body = self.handle_chunked_encoding() | |
| 69 else: | |
| 70 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.
| |
| 71 body = self.rfile.read(length) | |
| 72 | |
| 73 self.send_response(self.response_code) | |
| 74 writer.write(body) | |
| 75 writer.flush() | |
| 76 | |
| 77 def handle_chunked_encoding(self): | |
| 78 """This parses a "Transfer-Encoding: Chunked" body in accordance with | |
| 79 <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.
| |
| 80 as a string. | |
| 81 """ | |
| 82 body = '' | |
| 83 chunk_size = self.read_chunk_size() | |
| 84 while chunk_size > 0: | |
| 85 # Read the body. | |
| 86 data = self.rfile.read(chunk_size) | |
| 87 chunk_size -= len(data) | |
| 88 body += data | |
| 89 | |
| 90 # Finished reading this chunk. | |
| 91 if chunk_size == 0: | |
| 92 # Read through any trailer fields. | |
| 93 trailer_line = self.rfile.readline() | |
| 94 while trailer_line.strip() != '': | |
| 95 trailer_line = self.rfile.readline() | |
| 96 | |
| 97 # Read the chunk size. | |
| 98 chunk_size = self.read_chunk_size() | |
| 99 return body | |
| 100 | |
| 101 def read_chunk_size(self): | |
| 102 # Read the whole line, including the \r\n. | |
| 103 chunk_size_and_ext_line = self.rfile.readline() | |
| 104 # Look for a chunk extension. | |
| 105 chunk_size_end = chunk_size_and_ext_line.find(';') | |
| 106 if chunk_size_end == -1: | |
| 107 # No chunk extensions; just encounter the end of line. | |
| 108 chunk_size_end = chunk_size_and_ext_line.find('\r') | |
| 109 if chunk_size_end == -1: | |
| 110 self.send_response(400) # Bad request. | |
| 111 return -1 | |
| 112 return int(chunk_size_and_ext_line[:chunk_size_end], base=16) | |
| 113 | |
| 114 | |
| 115 def Main(): | |
| 116 # Start the server. | |
| 117 server = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), RequestHandler) | |
| 118 | |
| 119 # Write the port as an int to the parent process. | |
| 120 sys.stdout.write(struct.pack('i', server.server_address[1])) | |
| 121 sys.stdout.flush() | |
| 122 | |
| 123 # Read the desired test response code from the parent process. | |
| 124 RequestHandler.response_code = \ | |
| 125 struct.unpack('i', sys.stdin.read(struct.calcsize('i')))[0] | |
| 126 | |
| 127 # Handle the request. | |
| 128 server.handle_request() | |
| 129 | |
| 130 if __name__ == '__main__': | |
| 131 Main() | |
| OLD | NEW |