HTTPTransport test: Deal with limited-size pipe buffers

HTTPTransport.Upload33k failed on Windows due to WinHTTP timing out. The
test server, http_transport_test_server.py, writes the entire request to
a stdout pipe, to be received by crashpad_util_test. crashpad_util_test
is also the HTTP client, and it does not attempt to read from this pipe
until the HTTP transaction is complete. http_transport_test_server.py
must not write to stdout until the transaction is complete, otherwise,
there is a risk of deadlock if the pipe buffer fills up. The new
Upload33k test sends a large request, which was filling up the pipe
buffer on Windows.

This also adds an Upload33k_LengthUnknown test variant to exercise a
large POST when the length is not known ahead of time. This more closely
matches how Crashpad crash uploads are done on OS X.

TEST=crashpad_util_test HTTPTransport.*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/1286173007 .
This commit is contained in:
Mark Mentovai 2015-08-18 17:52:12 -04:00
parent 397e437e51
commit 14a2241274
2 changed files with 29 additions and 9 deletions

View File

@ -278,7 +278,7 @@ TEST(HTTPTransport, UnchunkedPlainText) {
test.Run();
}
TEST(HTTPTransport, Upload33k) {
void RunUpload33k(bool has_content_length) {
// On OS X, NSMutableURLRequest winds up calling into a CFReadStreams Read()
// callback with a 32kB buffer. Make sure that its able to get everything
// when enough is available to fill this buffer, requiring more than one
@ -290,8 +290,10 @@ TEST(HTTPTransport, Upload33k) {
HTTPHeaders headers;
headers[kContentType] = "application/octet-stream";
headers[kContentLength] =
base::StringPrintf("%" PRIuS, request_string.size());
if (has_content_length) {
headers[kContentLength] =
base::StringPrintf("%" PRIuS, request_string.size());
}
HTTPTransportTestFixture test(headers, body_stream.Pass(), 200,
[](HTTPTransportTestFixture* fixture, const std::string& request) {
size_t body_start = request.rfind("\r\n");
@ -300,6 +302,15 @@ TEST(HTTPTransport, Upload33k) {
test.Run();
}
TEST(HTTPTransport, Upload33k) {
RunUpload33k(true);
}
TEST(HTTPTransport, Upload33k_LengthUnknown) {
// The same as Upload33k, but without declaring Content-Length ahead of time.
RunUpload33k(false);
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -59,6 +59,14 @@ class BufferedReadFile(object):
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
# Everything to be written to stdout is collected into this string. It cant
# be written to stdout until after the HTTP transaction is complete, because
# stdout is a pipe being read by a test program thats also the HTTP client.
# The test program expects to complete the entire HTTP transaction before it
# even starts reading this scripts stdout. If the stdout pipe buffer fills up
# during an HTTP transaction, deadlock would result.
raw_request = ''
response_code = 500
response_body = ''
@ -69,9 +77,7 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
BaseHTTPServer.BaseHTTPRequestHandler.handle_one_request(self)
def do_POST(self):
writer = sys.stdout
writer.write(self.rfile.buffer)
RequestHandler.raw_request = self.rfile.buffer
self.rfile.buffer = ''
if self.headers.get('Transfer-Encoding', '') == 'Chunked':
@ -80,15 +86,14 @@ class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
length = int(self.headers.get('Content-Length', -1))
body = self.rfile.read(length)
RequestHandler.raw_request += body
self.send_response(self.response_code)
self.end_headers()
if self.response_code == 200:
self.wfile.write(self.response_body)
self.wfile.write('\r\n')
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.
@ -146,5 +151,9 @@ def Main():
# Handle the request.
server.handle_request()
# Share the entire request with the test program, which will validate it.
sys.stdout.write(RequestHandler.raw_request)
sys.stdout.flush()
if __name__ == '__main__':
Main()