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