diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_network_transaction_unittest.cc | 34 | ||||
-rw-r--r-- | net/net.gyp | 1 | ||||
-rw-r--r-- | net/spdy/spdy_network_transaction_unittest.cc | 541 | ||||
-rwxr-xr-x | net/spdy/spdy_test_util.cc | 367 | ||||
-rw-r--r-- | net/spdy/spdy_test_util.h | 163 |
5 files changed, 602 insertions, 504 deletions
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc index 37d6f60..e0a679b 100644 --- a/net/http/http_network_transaction_unittest.cc +++ b/net/http/http_network_transaction_unittest.cc @@ -5150,14 +5150,12 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForNpnSpdy) { ssl.was_npn_negotiated = true; session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); - MockWrite spdy_writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite spdy_writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead spdy_reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0), @@ -5281,20 +5279,20 @@ TEST_F(HttpNetworkTransactionTest, UseAlternateProtocolForTunneledNpnSpdy) { ssl.was_npn_negotiated = true; session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite spdy_writes[] = { MockWrite("CONNECT www.google.com:443 HTTP/1.1\r\n" "Host: www.google.com\r\n" "Proxy-Connection: keep-alive\r\n\r\n"), // 0 - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), // 3 + CreateMockWrite(req.get()) // 3 }; const char kCONNECTResponse[] = "HTTP/1.1 200 Connected\r\n\r\n"; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead spdy_reads[] = { MockRead(true, kCONNECTResponse, arraysize(kCONNECTResponse) - 1, 1), // 1 - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), // 2, 4 - arraysize(kGetSynReply), 4), + CreateMockRead(resp.get(), 4), // 2, 4 MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), // 5 arraysize(kGetBodyFrame), 4), MockRead(true, 0, 0, 4), // 6 @@ -5382,14 +5380,12 @@ TEST_F(HttpNetworkTransactionTest, // Make sure we use ssl for spdy here. SpdySession::SetSSLMode(true); - MockWrite spdy_writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite spdy_writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead spdy_reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0), @@ -6123,10 +6119,8 @@ TEST_F(HttpNetworkTransactionTest, SpdyPostNPNServerHangup) { ssl.was_npn_negotiated = true; session_deps.socket_factory.AddSSLSocketDataProvider(&ssl); - MockWrite spdy_writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite spdy_writes[] = { CreateMockWrite(req.get()) }; MockRead spdy_reads[] = { MockRead(false, 0, 0) // Not async - return 0 immediately. diff --git a/net/net.gyp b/net/net.gyp index ec5e187..b5cfd93 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -751,6 +751,7 @@ 'spdy/spdy_network_transaction_unittest.cc', 'spdy/spdy_protocol_test.cc', 'spdy/spdy_session_unittest.cc', + 'spdy/spdy_test_util.cc', 'spdy/spdy_test_util.h', 'tools/dump_cache/url_to_filename_encoder.cc', 'tools/dump_cache/url_to_filename_encoder.h', diff --git a/net/spdy/spdy_network_transaction_unittest.cc b/net/spdy/spdy_network_transaction_unittest.cc index e912b78..8c3198a 100644 --- a/net/spdy/spdy_network_transaction_unittest.cc +++ b/net/spdy/spdy_network_transaction_unittest.cc @@ -28,21 +28,6 @@ namespace net { -// NOTE: In GCC, on a Mac, this can't be in an anonymous namespace! -// This struct holds information used to construct spdy control and data frames. -struct SpdyHeaderInfo { - int kind; - spdy::SpdyStreamId id; - spdy::SpdyStreamId assoc_id; - int priority; - spdy::SpdyControlFlags control_flags; - bool compressed; - int status; - const char* data; - uint32 data_length; - spdy::SpdyDataFlags data_flags; -}; - namespace { // Helper to manage the lifetimes of the dependencies for a @@ -92,322 +77,6 @@ HttpNetworkSession* CreateSession(SessionDependencies* session_deps) { NULL); } -// Chop a frame into an array of MockWrites. -// |data| is the frame to chop. -// |length| is the length of the frame to chop. -// |num_chunks| is the number of chunks to create. -MockWrite* ChopFrame(const char* data, int length, int num_chunks) { - MockWrite* chunks = new MockWrite[num_chunks]; - int chunk_size = length / num_chunks; - for (int index = 0; index < num_chunks; index++) { - const char* ptr = data + (index * chunk_size); - if (index == num_chunks - 1) - chunk_size += length % chunk_size; // The last chunk takes the remainder. - chunks[index] = MockWrite(true, ptr, chunk_size); - } - return chunks; -} - -// ---------------------------------------------------------------------------- - -// Adds headers and values to a map. -// |extra_headers| is an array of { name, value } pairs, arranged as strings -// where the even entries are the header names, and the odd entries are the -// header values. -// |headers| gets filled in from |extra_headers|. -void AppendHeadersToSpdyFrame(const char* const extra_headers[], - int extra_header_count, - spdy::SpdyHeaderBlock* headers) { - std::string this_header; - std::string this_value; - - if (!extra_header_count) - return; - - // Sanity check: Non-NULL header list. - DCHECK(NULL != extra_headers) << "NULL header value pair list"; - // Sanity check: Non-NULL header map. - DCHECK(NULL != headers) << "NULL header map"; - // Copy in the headers. - for (int i = 0; i < extra_header_count; i++) { - // Sanity check: Non-empty header. - DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair"; - this_header = extra_headers[i * 2]; - std::string::size_type header_len = this_header.length(); - if (!header_len) - continue; - this_value = extra_headers[1 + (i * 2)]; - std::string new_value; - if (headers->find(this_header) != headers->end()) { - // More than one entry in the header. - // Don't add the header again, just the append to the value, - // separated by a NULL character. - - // Adjust the value. - new_value = (*headers)[this_header]; - // Put in a NULL separator. - new_value.append(1, '\0'); - // Append the new value. - new_value += this_value; - } else { - // Not a duplicate, just write the value. - new_value = this_value; - } - (*headers)[this_header] = new_value; - } -} - -// Writes |str| of the given |len| to the buffer pointed to by |buffer_handle|. -// Uses a template so buffer_handle can be a char* or an unsigned char*. -// Updates the |*buffer_handle| pointer by |len| -// Returns the number of bytes written into *|buffer_handle| -template<class T> -int AppendToBuffer(const void* str, - int len, - T** buffer_handle, - int* buffer_len_remaining) { - if (len <= 0) - return 0; - DCHECK(NULL != buffer_handle) << "NULL buffer handle"; - DCHECK(NULL != *buffer_handle) << "NULL pointer"; - DCHECK(NULL != buffer_len_remaining) - << "NULL buffer remainder length pointer"; - DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size"; - memcpy(*buffer_handle, str, len); - *buffer_handle += len; - *buffer_len_remaining -= len; - return len; -} - -// Writes |val| to a location of size |len|, in big-endian format. -// in the buffer pointed to by |buffer_handle|. -// Updates the |*buffer_handle| pointer by |len| -// Returns the number of bytes written -int AppendToBuffer(int val, - int len, - unsigned char** buffer_handle, - int* buffer_len_remaining) { - if (len <= 0) - return 0; - DCHECK((size_t) len <= sizeof(len)) << "Data length too long for data type"; - DCHECK(NULL != buffer_handle) << "NULL buffer handle"; - DCHECK(NULL != *buffer_handle) << "NULL pointer"; - DCHECK(NULL != buffer_len_remaining) - << "NULL buffer remainder length pointer"; - DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size"; - for (int i = 0; i < len; i++) { - int shift = (8 * (len - (i + 1))); - unsigned char val_chunk = (val >> shift) & 0x0FF; - *(*buffer_handle)++ = val_chunk; - *buffer_len_remaining += 1; - } - return len; -} - -// Construct a SPDY packet. -// |head| is the start of the packet, up to but not including -// the header value pairs. -// |extra_headers| are the extra header-value pairs, which typically -// will vary the most between calls. -// |tail| is any (relatively constant) header-value pairs to add. -// |buffer| is the buffer we're filling in. -// Returns a SpdFrame. -spdy::SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo* header_info, - const char* const extra_headers[], - int extra_header_count, - const char* const tail[], - int tail_header_count) { - spdy::SpdyFramer framer; - spdy::SpdyHeaderBlock headers; - // Copy in the extra headers to our map. - AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers); - // Copy in the tail headers to our map. - if (tail && tail_header_count) - AppendHeadersToSpdyFrame(tail, tail_header_count, &headers); - spdy::SpdyFrame* frame = NULL; - switch (header_info->kind) { - case spdy::SYN_STREAM: - frame = framer.CreateSynStream(header_info->id, header_info->assoc_id, - header_info->priority, - header_info->control_flags, - header_info->compressed, &headers); - break; - case spdy::SYN_REPLY: - frame = framer.CreateSynReply(header_info->id, header_info->control_flags, - header_info->compressed, &headers); - break; - case spdy::RST_STREAM: - frame = framer.CreateRstStream(header_info->id, header_info->status); - break; - default: - frame = framer.CreateDataFrame(header_info->id, header_info->data, - header_info->data_length, - header_info->data_flags); - break; - } - return frame; -} - -// Construct an expected SPDY reply string. -// |extra_headers| are the extra header-value pairs, which typically -// will vary the most between calls. -// |buffer| is the buffer we're filling in. -// Returns the number of bytes written into |buffer|. -int ConstructSpdyReply(const char* const extra_headers[], - int extra_header_count, - char* buffer, - int buffer_length) { - int packet_size = 0; - int header_count = 0; - char* buffer_write = buffer; - int buffer_left = buffer_length; - spdy::SpdyHeaderBlock headers; - if (!buffer || !buffer_length) - return 0; - // Copy in the extra headers. - AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers); - header_count = headers.size(); - // The iterator gets us the list of header/value pairs in sorted order. - spdy::SpdyHeaderBlock::iterator next = headers.begin(); - spdy::SpdyHeaderBlock::iterator last = headers.end(); - for ( ; next != last; ++next) { - // Write the header. - int value_len, current_len, offset; - const char* header_string = next->first.c_str(); - packet_size += AppendToBuffer(header_string, - next->first.length(), - &buffer_write, - &buffer_left); - packet_size += AppendToBuffer(": ", - strlen(": "), - &buffer_write, - &buffer_left); - // Write the value(s). - const char* value_string = next->second.c_str(); - // Check if it's split among two or more values. - value_len = next->second.length(); - current_len = strlen(value_string); - offset = 0; - // Handle the first N-1 values. - while (current_len < value_len) { - // Finish this line -- write the current value. - packet_size += AppendToBuffer(value_string + offset, - current_len - offset, - &buffer_write, - &buffer_left); - packet_size += AppendToBuffer("\n", - strlen("\n"), - &buffer_write, - &buffer_left); - // Advance to next value. - offset = current_len + 1; - current_len += 1 + strlen(value_string + offset); - // Start another line -- add the header again. - packet_size += AppendToBuffer(header_string, - next->first.length(), - &buffer_write, - &buffer_left); - packet_size += AppendToBuffer(": ", - strlen(": "), - &buffer_write, - &buffer_left); - } - EXPECT_EQ(value_len, current_len); - // Copy the last (or only) value. - packet_size += AppendToBuffer(value_string + offset, - value_len - offset, - &buffer_write, - &buffer_left); - packet_size += AppendToBuffer("\n", - strlen("\n"), - &buffer_write, - &buffer_left); - } - return packet_size; -} - -// Construct an expected SPDY SETTINGS frame. -// |settings| are the settings to set. -// Returns the constructed frame. The caller takes ownership of the frame. -spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings) { - spdy::SpdyFramer framer; - return framer.CreateSettings(settings); -} - -// Construct a single SPDY header entry, for validation. -// |extra_headers| are the extra header-value pairs. -// |buffer| is the buffer we're filling in. -// |index| is the index of the header we want. -// Returns the number of bytes written into |buffer|. -int ConstructSpdyHeader(const char* const extra_headers[], - int extra_header_count, - char* buffer, - int buffer_length, - int index) { - const char* this_header = NULL; - const char* this_value = NULL; - if (!buffer || !buffer_length) - return 0; - *buffer = '\0'; - // Sanity check: Non-empty header list. - DCHECK(NULL != extra_headers) << "NULL extra headers pointer"; - // Sanity check: Index out of range. - DCHECK((index >= 0) && (index < extra_header_count)) - << "Index " << index - << " out of range [0, " << extra_header_count << ")"; - this_header = extra_headers[index * 2]; - // Sanity check: Non-empty header. - if (!*this_header) - return 0; - std::string::size_type header_len = strlen(this_header); - if (!header_len) - return 0; - this_value = extra_headers[1 + (index * 2)]; - // Sanity check: Non-empty value. - if (!*this_value) - this_value = ""; - int n = base::snprintf(buffer, - buffer_length, - "%s: %s\r\n", - this_header, - this_value); - return n; -} - -// Constructs a standard SPDY GET packet. -// |extra_headers| are the extra header-value pairs, which typically -// will vary the most between calls. -// Returns a SpdFrame. -spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], - int extra_header_count) { - SpdyHeaderInfo SynStartHeader = { - spdy::SYN_STREAM, // Kind = Syn - 1, // Stream ID - 0, // Associated stream ID - 3, // Priority - spdy::CONTROL_FLAG_FIN, // Control Flags - false, // Compressed - 200, // Status - NULL, // Data - 0, // Length - spdy::DATA_FLAG_NONE // Data Flags - }; - static const char* const kStandardGetHeaders[] = { - "method", - "GET", - "url", - "http://www.google.com/", - "version", - "HTTP/1.1" - }; - return ConstructSpdyPacket( - &SynStartHeader, - extra_headers, - extra_header_count, - kStandardGetHeaders, - arraysize(kStandardGetHeaders) / 2); -} - } // namespace class SpdyNetworkTransactionTest : public PlatformTest { @@ -523,14 +192,13 @@ TEST_F(SpdyNetworkTransactionTest, Constructor) { } TEST_F(SpdyNetworkTransactionTest, Get) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -590,7 +258,7 @@ TEST_F(SpdyNetworkTransactionTest, EmptyPost) { 0x01, 0x00, 0x00, 0x4a, // flags, len 0x00, 0x00, 0x00, 0x01, // stream id 0x00, 0x00, 0x00, 0x00, // associated - 0xc0, 0x00, 0x00, 0x03, // 4 headers + 0xc0, 0x00, 0x00, 0x03, // 3 headers 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', 0x00, 0x04, 'P', 'O', 'S', 'T', 0x00, 0x03, 'u', 'r', 'l', @@ -690,16 +358,13 @@ TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { // We disable SSL for this test. SpdySession::SetSSLMode(false); - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -732,15 +397,16 @@ TEST_F(SpdyNetworkTransactionTest, ResponseWithTwoSynReplies) { } TEST_F(SpdyNetworkTransactionTest, CancelledTransaction) { + // Construct the request. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), + CreateMockWrite(req.get()), MockRead(true, 0, 0) // EOF }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), // This following read isn't used by the test, except during the // RunAllPending() call at the end since the SpdySession survives the // SpdyNetworkTransaction and still tries to continue Read()'ing. Any @@ -790,14 +456,12 @@ class DeleteSessionCallback : public CallbackRunner< Tuple1<int> > { // transaction. Failures will usually be valgrind errors. See // http://crbug.com/46925 TEST_F(SpdyNetworkTransactionTest, DeleteSessionOnReadCallback) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn), 1), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply), 2), + CreateMockRead(resp.get(), 2), MockRead(true, ERR_IO_PENDING, 3), // Force a pause MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame), 4), @@ -920,10 +584,8 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeaders) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; MockRead reads[] = { MockRead(true, reinterpret_cast<const char*>(test_cases[i].syn_reply), @@ -964,10 +626,10 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { spdy::SYN_REPLY, // Syn Reply 1, // Stream ID 0, // Associated Stream ID - 3, // Priority + SPDY_PRIORITY_LOWEST, // Priority spdy::CONTROL_FLAG_NONE, // Control Flags false, // Compressed - 200, // Status + spdy::INVALID, // Status NULL, // Data 0, // Data Length spdy::DATA_FLAG_NONE // Data Flags @@ -1050,10 +712,7 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { test_cases[i].num_headers[0])); MockWrite writes[] = { - MockWrite( - true, - frame_req->data(), - frame_req->length() + spdy::SpdyFrame::size()), + CreateMockWrite(frame_req.get()), }; // Construct the reply. @@ -1065,9 +724,7 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { 0)); MockRead reads[] = { - MockRead(true, - frame_reply->data(), - frame_reply->length() + spdy::SpdyFrame::size()), + CreateMockRead(frame_reply.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), @@ -1120,10 +777,10 @@ TEST_F(SpdyNetworkTransactionTest, SynReplyHeadersVary) { // Construct the expected header reply string. char reply_buffer[256] = ""; - ConstructSpdyReply(test_cases[i].extra_headers[1], - test_cases[i].num_headers[1], - reply_buffer, - 256); + ConstructSpdyReplyString(test_cases[i].extra_headers[1], + test_cases[i].num_headers[1], + reply_buffer, + 256); EXPECT_EQ(std::string(reply_buffer), lines) << i; } @@ -1165,9 +822,9 @@ TEST_F(SpdyNetworkTransactionTest, InvalidSynReply) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), + CreateMockWrite(req.get()), MockWrite(true, 0, 0) // EOF }; @@ -1210,9 +867,9 @@ TEST_F(SpdyNetworkTransactionTest, CorruptFrameSessionError) { }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), + CreateMockWrite(req.get()), MockWrite(true, 0, 0) // EOF }; @@ -1397,10 +1054,8 @@ TEST_F(SpdyNetworkTransactionTest, ServerPush) { const char syn_body_data2[] = "hello my darling hello my baby"; const char* syn_body_data = NULL; - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; // This array is for request before and after push is received. The push // body is only one 'packet', to allow the initial transaction to read all @@ -1551,17 +1206,18 @@ TEST_F(SpdyNetworkTransactionTest, ServerPush) { // Test that we shutdown correctly on write errors. TEST_F(SpdyNetworkTransactionTest, WriteError) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { // We'll write 10 bytes successfully - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), 10), + MockWrite(true, req->data(), 10), // Followed by ERROR! MockWrite(true, ERR_FAILED), MockWrite(true, 0, 0) // EOF }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get(), 2), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -1580,13 +1236,13 @@ TEST_F(SpdyNetworkTransactionTest, WriteError) { // Test that partial writes work. TEST_F(SpdyNetworkTransactionTest, PartialWrite) { // Chop the SYN_STREAM frame into 5 chunks. + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); const int kChunks = 5; - scoped_array<MockWrite> writes(ChopFrame( - reinterpret_cast<const char*>(kGetSyn), arraysize(kGetSyn), kChunks)); + scoped_array<MockWrite> writes(ChopFrame(req.get(), kChunks)); + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -1612,15 +1268,15 @@ TEST_F(SpdyNetworkTransactionTest, ConnectFailure) { }; for (size_t index = 0; index < arraysize(connects); ++index) { + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), + CreateMockWrite(req.get()), MockWrite(true, 0, 0) // EOF }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -1645,9 +1301,9 @@ TEST_F(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { MockWrite(true, 0, 0) // EOF }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -1670,14 +1326,12 @@ TEST_F(SpdyNetworkTransactionTest, DecompressFailureOnSynReply) { // Test that the NetLog contains good data for a simple GET request. TEST_F(SpdyNetworkTransactionTest, NetLog) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -1733,10 +1387,8 @@ TEST_F(SpdyNetworkTransactionTest, NetLog) { // on the network, but issued a Read for only 5 of those bytes) that the data // flow still works correctly. TEST_F(SpdyNetworkTransactionTest, BufferFull) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; static const unsigned char kCombinedDataFrames[] = { 0x00, 0x00, 0x00, 0x01, // header @@ -1753,9 +1405,9 @@ TEST_F(SpdyNetworkTransactionTest, BufferFull) { 'd', }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, ERR_IO_PENDING), // Force a pause MockRead(true, reinterpret_cast<const char*>(kCombinedDataFrames), arraysize(kCombinedDataFrames)), @@ -1837,10 +1489,8 @@ TEST_F(SpdyNetworkTransactionTest, BufferFull) { // at the same time, ensure that we don't notify a read completion for // each data frame individually. TEST_F(SpdyNetworkTransactionTest, Buffering) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; // 4 data frames in a single read. static const unsigned char kCombinedDataFrames[] = { @@ -1858,9 +1508,9 @@ TEST_F(SpdyNetworkTransactionTest, Buffering) { 'm', 'e', 's', 's', 'a', 'g', 'e', }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, ERR_IO_PENDING), // Force a pause MockRead(true, reinterpret_cast<const char*>(kCombinedDataFrames), arraysize(kCombinedDataFrames)), @@ -1941,10 +1591,8 @@ TEST_F(SpdyNetworkTransactionTest, Buffering) { // Verify the case where we buffer data but read it after it has been buffered. TEST_F(SpdyNetworkTransactionTest, BufferedAll) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; // The Syn Reply and all data frames in a single read. static const unsigned char kCombinedFrames[] = { @@ -2050,10 +1698,8 @@ TEST_F(SpdyNetworkTransactionTest, BufferedAll) { // Verify the case where we buffer data and close the connection. TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; // All data frames in a single read. static const unsigned char kCombinedFrames[] = { @@ -2072,9 +1718,9 @@ TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { // NOTE: We didn't FIN the stream. }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, ERR_IO_PENDING), // Force a wait MockRead(true, reinterpret_cast<const char*>(kCombinedFrames), arraysize(kCombinedFrames)), @@ -2152,10 +1798,8 @@ TEST_F(SpdyNetworkTransactionTest, BufferedClosed) { // Verify the case where we buffer data and cancel the transaction. TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; static const unsigned char kDataFrame[] = { 0x00, 0x00, 0x00, 0x01, // header @@ -2164,9 +1808,9 @@ TEST_F(SpdyNetworkTransactionTest, BufferedCancelled) { // NOTE: We didn't FIN the stream. }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(true, ERR_IO_PENDING), // Force a wait MockRead(true, reinterpret_cast<const char*>(kDataFrame), arraysize(kDataFrame)), @@ -2235,10 +1879,10 @@ TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { spdy::SYN_REPLY, // Syn Reply 1, // Stream ID 0, // Associated Stream ID - 3, // Priority + SPDY_PRIORITY_LOWEST, // Priority spdy::CONTROL_FLAG_NONE, // Control Flags false, // Compressed - 200, // Status + spdy::INVALID, // Status NULL, // Data 0, // Data Length spdy::DATA_FLAG_NONE // Data Flags @@ -2257,10 +1901,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { // Construct the request. scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); - - MockWrite writes[] = { - MockWrite(true, req->data(), req->length() + spdy::SpdyFrame::size()), - }; + MockWrite writes[] = { CreateMockWrite(req.get()) }; // Construct the reply. scoped_ptr<spdy::SpdyFrame> reply( @@ -2297,11 +1938,10 @@ TEST_F(SpdyNetworkTransactionTest, SettingsSaved) { } MockRead reads[] = { - MockRead(true, reply->data(), reply->length() + spdy::SpdyFrame::size()), + CreateMockRead(reply.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), - MockRead(true, settings_frame->data(), - settings_frame->length() + spdy::SpdyFrame::size()), + CreateMockRead(settings_frame.get()), MockRead(true, 0, 0) // EOF }; @@ -2344,10 +1984,10 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { spdy::SYN_REPLY, // Syn Reply 1, // Stream ID 0, // Associated Stream ID - 3, // Priority + SPDY_PRIORITY_LOWEST, // Priority spdy::CONTROL_FLAG_NONE, // Control Flags false, // Compressed - 200, // Status + spdy::INVALID, // Status NULL, // Data 0, // Data Length spdy::DATA_FLAG_NONE // Data Flags @@ -2395,9 +2035,8 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); MockWrite writes[] = { - MockWrite(true, settings_frame->data(), - settings_frame->length() + spdy::SpdyFrame::size()), - MockWrite(true, req->data(), req->length() + spdy::SpdyFrame::size()), + CreateMockWrite(settings_frame.get()), + CreateMockWrite(req.get()), }; // Construct the reply. @@ -2409,7 +2048,7 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { 0)); MockRead reads[] = { - MockRead(true, reply->data(), reply->length() + spdy::SpdyFrame::size()), + CreateMockRead(reply.get()), MockRead(true, reinterpret_cast<const char*>(kGetBodyFrame), arraysize(kGetBodyFrame)), MockRead(true, 0, 0) // EOF @@ -2448,10 +2087,8 @@ TEST_F(SpdyNetworkTransactionTest, SettingsPlayback) { } TEST_F(SpdyNetworkTransactionTest, GoAwayWithActiveStream) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; MockRead reads[] = { MockRead(true, reinterpret_cast<const char*>(kGoAway), @@ -2469,14 +2106,12 @@ TEST_F(SpdyNetworkTransactionTest, GoAwayWithActiveStream) { } TEST_F(SpdyNetworkTransactionTest, CloseWithActiveStream) { - MockWrite writes[] = { - MockWrite(true, reinterpret_cast<const char*>(kGetSyn), - arraysize(kGetSyn)), - }; + scoped_ptr<spdy::SpdyFrame> req(ConstructSpdyGet(NULL, 0)); + MockWrite writes[] = { CreateMockWrite(req.get()) }; + scoped_ptr<spdy::SpdyFrame> resp(ConstructSpdyGetSynReply(NULL, 0)); MockRead reads[] = { - MockRead(true, reinterpret_cast<const char*>(kGetSynReply), - arraysize(kGetSynReply)), + CreateMockRead(resp.get()), MockRead(false, 0, 0) // EOF }; diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc new file mode 100755 index 0000000..bed7031 --- /dev/null +++ b/net/spdy/spdy_test_util.cc @@ -0,0 +1,367 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/spdy/spdy_test_util.h" + +#include "base/basictypes.h" +#include "base/string_util.h" + +namespace net { + +// Chop a frame into an array of MockWrites. +// |data| is the frame to chop. +// |length| is the length of the frame to chop. +// |num_chunks| is the number of chunks to create. +MockWrite* ChopFrame(const char* data, int length, int num_chunks) { + MockWrite* chunks = new MockWrite[num_chunks]; + int chunk_size = length / num_chunks; + for (int index = 0; index < num_chunks; index++) { + const char* ptr = data + (index * chunk_size); + if (index == num_chunks - 1) + chunk_size += length % chunk_size; // The last chunk takes the remainder. + chunks[index] = MockWrite(true, ptr, chunk_size); + } + return chunks; +} + +// Chop a SpdyFrame into an array of MockWrites. +// |frame| is the frame to chop. +// |num_chunks| is the number of chunks to create. +MockWrite* ChopFrame(const spdy::SpdyFrame* frame, int num_chunks) { + return ChopFrame(frame->data(), + frame->length() + spdy::SpdyFrame::size(), + num_chunks); +} + +// Adds headers and values to a map. +// |extra_headers| is an array of { name, value } pairs, arranged as strings +// where the even entries are the header names, and the odd entries are the +// header values. +// |headers| gets filled in from |extra_headers|. +void AppendHeadersToSpdyFrame(const char* const extra_headers[], + int extra_header_count, + spdy::SpdyHeaderBlock* headers) { + std::string this_header; + std::string this_value; + + if (!extra_header_count) + return; + + // Sanity check: Non-NULL header list. + DCHECK(NULL != extra_headers) << "NULL header value pair list"; + // Sanity check: Non-NULL header map. + DCHECK(NULL != headers) << "NULL header map"; + // Copy in the headers. + for (int i = 0; i < extra_header_count; i++) { + // Sanity check: Non-empty header. + DCHECK_NE('\0', *extra_headers[i * 2]) << "Empty header value pair"; + this_header = extra_headers[i * 2]; + std::string::size_type header_len = this_header.length(); + if (!header_len) + continue; + this_value = extra_headers[1 + (i * 2)]; + std::string new_value; + if (headers->find(this_header) != headers->end()) { + // More than one entry in the header. + // Don't add the header again, just the append to the value, + // separated by a NULL character. + + // Adjust the value. + new_value = (*headers)[this_header]; + // Put in a NULL separator. + new_value.append(1, '\0'); + // Append the new value. + new_value += this_value; + } else { + // Not a duplicate, just write the value. + new_value = this_value; + } + (*headers)[this_header] = new_value; + } +} + +// Writes |val| to a location of size |len|, in big-endian format. +// in the buffer pointed to by |buffer_handle|. +// Updates the |*buffer_handle| pointer by |len| +// Returns the number of bytes written +int AppendToBuffer(int val, + int len, + unsigned char** buffer_handle, + int* buffer_len_remaining) { + if (len <= 0) + return 0; + DCHECK((size_t) len <= sizeof(len)) << "Data length too long for data type"; + DCHECK(NULL != buffer_handle) << "NULL buffer handle"; + DCHECK(NULL != *buffer_handle) << "NULL pointer"; + DCHECK(NULL != buffer_len_remaining) + << "NULL buffer remainder length pointer"; + DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size"; + for (int i = 0; i < len; i++) { + int shift = (8 * (len - (i + 1))); + unsigned char val_chunk = (val >> shift) & 0x0FF; + *(*buffer_handle)++ = val_chunk; + *buffer_len_remaining += 1; + } + return len; +} + +// Construct a SPDY packet. +// |head| is the start of the packet, up to but not including +// the header value pairs. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// |tail| is any (relatively constant) header-value pairs to add. +// |buffer| is the buffer we're filling in. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo* header_info, + const char* const extra_headers[], + int extra_header_count, + const char* const tail[], + int tail_header_count) { + spdy::SpdyFramer framer; + spdy::SpdyHeaderBlock headers; + // Copy in the extra headers to our map. + AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers); + // Copy in the tail headers to our map. + if (tail && tail_header_count) + AppendHeadersToSpdyFrame(tail, tail_header_count, &headers); + spdy::SpdyFrame* frame = NULL; + switch (header_info->kind) { + case spdy::SYN_STREAM: + frame = framer.CreateSynStream(header_info->id, header_info->assoc_id, + header_info->priority, + header_info->control_flags, + header_info->compressed, &headers); + break; + case spdy::SYN_REPLY: + frame = framer.CreateSynReply(header_info->id, header_info->control_flags, + header_info->compressed, &headers); + break; + case spdy::RST_STREAM: + frame = framer.CreateRstStream(header_info->id, header_info->status); + break; + default: + frame = framer.CreateDataFrame(header_info->id, header_info->data, + header_info->data_length, + header_info->data_flags); + break; + } + return frame; +} + +// Construct an expected SPDY SETTINGS frame. +// |settings| are the settings to set. +// Returns the constructed frame. The caller takes ownership of the frame. +spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings) { + spdy::SpdyFramer framer; + return framer.CreateSettings(settings); +} + +// Construct a single SPDY header entry, for validation. +// |extra_headers| are the extra header-value pairs. +// |buffer| is the buffer we're filling in. +// |index| is the index of the header we want. +// Returns the number of bytes written into |buffer|. +int ConstructSpdyHeader(const char* const extra_headers[], + int extra_header_count, + char* buffer, + int buffer_length, + int index) { + const char* this_header = NULL; + const char* this_value = NULL; + if (!buffer || !buffer_length) + return 0; + *buffer = '\0'; + // Sanity check: Non-empty header list. + DCHECK(NULL != extra_headers) << "NULL extra headers pointer"; + // Sanity check: Index out of range. + DCHECK((index >= 0) && (index < extra_header_count)) + << "Index " << index + << " out of range [0, " << extra_header_count << ")"; + this_header = extra_headers[index * 2]; + // Sanity check: Non-empty header. + if (!*this_header) + return 0; + std::string::size_type header_len = strlen(this_header); + if (!header_len) + return 0; + this_value = extra_headers[1 + (index * 2)]; + // Sanity check: Non-empty value. + if (!*this_value) + this_value = ""; + int n = base::snprintf(buffer, + buffer_length, + "%s: %s\r\n", + this_header, + this_value); + return n; +} + +// Constructs a standard SPDY GET packet. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], + int extra_header_count) { + SpdyHeaderInfo SynStartHeader = { + spdy::SYN_STREAM, // Kind = Syn + 1, // Stream ID + 0, // Associated stream ID + SPDY_PRIORITY_LOWEST, // Priority + spdy::CONTROL_FLAG_FIN, // Control Flags + false, // Compressed + spdy::INVALID, // Status + NULL, // Data + 0, // Length + spdy::DATA_FLAG_NONE // Data Flags + }; + static const char* const kStandardGetHeaders[] = { + "method", + "GET", + "url", + "http://www.google.com/", + "version", + "HTTP/1.1" + }; + return ConstructSpdyPacket( + &SynStartHeader, + extra_headers, + extra_header_count, + kStandardGetHeaders, + arraysize(kStandardGetHeaders) / 2); +} + +// Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], + int extra_header_count) { + SpdyHeaderInfo SynStartHeader = { + spdy::SYN_REPLY, // Kind = SynReply + 1, // Stream ID + 0, // Associated stream ID + SPDY_PRIORITY_LOWEST, // Priority + spdy::CONTROL_FLAG_NONE, // Control Flags + false, // Compressed + spdy::INVALID, // Status + NULL, // Data + 0, // Length + spdy::DATA_FLAG_NONE // Data Flags + }; + static const char* const kStandardGetHeaders[] = { + "hello", + "bye", + "status", + "200", + "url", + "/index.php", + "version", + "HTTP/1.1" + }; + return ConstructSpdyPacket( + &SynStartHeader, + extra_headers, + extra_header_count, + kStandardGetHeaders, + arraysize(kStandardGetHeaders) / 2); +} + +// Construct an expected SPDY reply string. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// |buffer| is the buffer we're filling in. +// Returns the number of bytes written into |buffer|. +int ConstructSpdyReplyString(const char* const extra_headers[], + int extra_header_count, + char* buffer, + int buffer_length) { + int packet_size = 0; + int header_count = 0; + char* buffer_write = buffer; + int buffer_left = buffer_length; + spdy::SpdyHeaderBlock headers; + if (!buffer || !buffer_length) + return 0; + // Copy in the extra headers. + AppendHeadersToSpdyFrame(extra_headers, extra_header_count, &headers); + header_count = headers.size(); + // The iterator gets us the list of header/value pairs in sorted order. + spdy::SpdyHeaderBlock::iterator next = headers.begin(); + spdy::SpdyHeaderBlock::iterator last = headers.end(); + for ( ; next != last; ++next) { + // Write the header. + int value_len, current_len, offset; + const char* header_string = next->first.c_str(); + packet_size += AppendToBuffer(header_string, + next->first.length(), + &buffer_write, + &buffer_left); + packet_size += AppendToBuffer(": ", + strlen(": "), + &buffer_write, + &buffer_left); + // Write the value(s). + const char* value_string = next->second.c_str(); + // Check if it's split among two or more values. + value_len = next->second.length(); + current_len = strlen(value_string); + offset = 0; + // Handle the first N-1 values. + while (current_len < value_len) { + // Finish this line -- write the current value. + packet_size += AppendToBuffer(value_string + offset, + current_len - offset, + &buffer_write, + &buffer_left); + packet_size += AppendToBuffer("\n", + strlen("\n"), + &buffer_write, + &buffer_left); + // Advance to next value. + offset = current_len + 1; + current_len += 1 + strlen(value_string + offset); + // Start another line -- add the header again. + packet_size += AppendToBuffer(header_string, + next->first.length(), + &buffer_write, + &buffer_left); + packet_size += AppendToBuffer(": ", + strlen(": "), + &buffer_write, + &buffer_left); + } + EXPECT_EQ(value_len, current_len); + // Copy the last (or only) value. + packet_size += AppendToBuffer(value_string + offset, + value_len - offset, + &buffer_write, + &buffer_left); + packet_size += AppendToBuffer("\n", + strlen("\n"), + &buffer_write, + &buffer_left); + } + return packet_size; +} + +// Create a MockWrite from the given SpdyFrame. +MockWrite CreateMockWrite(spdy::SpdyFrame* req) { + return MockWrite( + true, req->data(), req->length() + spdy::SpdyFrame::size()); +} + +// Create a MockRead from the given SpdyFrame. +MockRead CreateMockRead(spdy::SpdyFrame* resp) { + return MockRead( + true, resp->data(), resp->length() + spdy::SpdyFrame::size()); +} + +// Create a MockRead from the given SpdyFrame and sequence number. +MockRead CreateMockRead(spdy::SpdyFrame* resp, int seq) { + return MockRead( + true, resp->data(), resp->length() + spdy::SpdyFrame::size(), seq); +} + +} // namespace net diff --git a/net/spdy/spdy_test_util.h b/net/spdy/spdy_test_util.h index 830a290..3aa45ab 100644 --- a/net/spdy/spdy_test_util.h +++ b/net/spdy/spdy_test_util.h @@ -6,40 +6,11 @@ #define NET_SPDY_SPDY_TEST_UTIL_H_ #include "base/basictypes.h" +#include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_framer.h" namespace net { -const uint8 kGetSyn[] = { - 0x80, 0x01, 0x00, 0x01, // header - 0x01, 0x00, 0x00, 0x49, // FIN, len - 0x00, 0x00, 0x00, 0x01, // stream id - 0x00, 0x00, 0x00, 0x00, // associated - 0xc0, 0x00, 0x00, 0x03, // 3 headers - 0x00, 0x06, 'm', 'e', 't', 'h', 'o', 'd', - 0x00, 0x03, 'G', 'E', 'T', - 0x00, 0x03, 'u', 'r', 'l', - 0x00, 0x16, 'h', 't', 't', 'p', ':', '/', '/', 'w', 'w', 'w', - '.', 'g', 'o', 'o', 'g', 'l', 'e', '.', 'c', 'o', - 'm', '/', - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', -}; - -const uint8 kGetSynReply[] = { - 0x80, 0x01, 0x00, 0x02, // header - 0x00, 0x00, 0x00, 0x45, - 0x00, 0x00, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x04, // 4 headers - 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', // "hello" - 0x00, 0x03, 'b', 'y', 'e', // "bye" - 0x00, 0x06, 's', 't', 'a', 't', 'u', 's', // "status" - 0x00, 0x03, '2', '0', '0', // "200" - 0x00, 0x03, 'u', 'r', 'l', // "url" - 0x00, 0x0a, '/', 'i', 'n', 'd', 'e', 'x', '.', 'p', 'h', 'p', // "/index... - 0x00, 0x07, 'v', 'e', 'r', 's', 'i', 'o', 'n', // "version" - 0x00, 0x08, 'H', 'T', 'T', 'P', '/', '1', '.', '1', // "HTTP/1.1" -}; - const uint8 kGetBodyFrame[] = { 0x00, 0x00, 0x00, 0x01, // header 0x01, 0x00, 0x00, 0x06, // FIN, length @@ -110,6 +81,136 @@ const uint8 kGoAway[] = { 0x00, 0x00, 0x00, 0x00, // last-accepted-stream-id }; +// ---------------------------------------------------------------------------- + +// NOTE: In GCC, on a Mac, this can't be in an anonymous namespace! +// This struct holds information used to construct spdy control and data frames. +struct SpdyHeaderInfo { + spdy::SpdyControlType kind; + spdy::SpdyStreamId id; + spdy::SpdyStreamId assoc_id; + spdy::SpdyPriority priority; + spdy::SpdyControlFlags control_flags; + bool compressed; + spdy::SpdyStatusCodes status; + const char* data; + uint32 data_length; + spdy::SpdyDataFlags data_flags; +}; + +// Chop a frame into an array of MockWrites. +// |data| is the frame to chop. +// |length| is the length of the frame to chop. +// |num_chunks| is the number of chunks to create. +MockWrite* ChopFrame(const char* data, int length, int num_chunks); + +// Chop a SpdyFrame into an array of MockWrites. +// |frame| is the frame to chop. +// |num_chunks| is the number of chunks to create. +MockWrite* ChopFrame(const spdy::SpdyFrame* frame, int num_chunks); + +// Adds headers and values to a map. +// |extra_headers| is an array of { name, value } pairs, arranged as strings +// where the even entries are the header names, and the odd entries are the +// header values. +// |headers| gets filled in from |extra_headers|. +void AppendHeadersToSpdyFrame(const char* const extra_headers[], + int extra_header_count, + spdy::SpdyHeaderBlock* headers); + +// Writes |str| of the given |len| to the buffer pointed to by |buffer_handle|. +// Uses a template so buffer_handle can be a char* or an unsigned char*. +// Updates the |*buffer_handle| pointer by |len| +// Returns the number of bytes written into *|buffer_handle| +template<class T> +int AppendToBuffer(const char* str, + int len, + T** buffer_handle, + int* buffer_len_remaining) { + DCHECK_GT(len, 0); + DCHECK(NULL != buffer_handle) << "NULL buffer handle"; + DCHECK(NULL != *buffer_handle) << "NULL pointer"; + DCHECK(NULL != buffer_len_remaining) + << "NULL buffer remainder length pointer"; + DCHECK_GE(*buffer_len_remaining, len) << "Insufficient buffer size"; + memcpy(*buffer_handle, str, len); + *buffer_handle += len; + *buffer_len_remaining -= len; + return len; +} + +// Writes |val| to a location of size |len|, in big-endian format. +// in the buffer pointed to by |buffer_handle|. +// Updates the |*buffer_handle| pointer by |len| +// Returns the number of bytes written +int AppendToBuffer(int val, + int len, + unsigned char** buffer_handle, + int* buffer_len_remaining); + +// Construct a SPDY packet. +// |head| is the start of the packet, up to but not including +// the header value pairs. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// |tail| is any (relatively constant) header-value pairs to add. +// |buffer| is the buffer we're filling in. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyPacket(const SpdyHeaderInfo* header_info, + const char* const extra_headers[], + int extra_header_count, + const char* const tail[], + int tail_header_count); + +// Construct an expected SPDY reply string. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// |buffer| is the buffer we're filling in. +// Returns the number of bytes written into |buffer|. +int ConstructSpdyReplyString(const char* const extra_headers[], + int extra_header_count, + char* buffer, + int buffer_length); + +// Construct an expected SPDY SETTINGS frame. +// |settings| are the settings to set. +// Returns the constructed frame. The caller takes ownership of the frame. +spdy::SpdyFrame* ConstructSpdySettings(spdy::SpdySettings settings); + +// Construct a single SPDY header entry, for validation. +// |extra_headers| are the extra header-value pairs. +// |buffer| is the buffer we're filling in. +// |index| is the index of the header we want. +// Returns the number of bytes written into |buffer|. +int ConstructSpdyHeader(const char* const extra_headers[], + int extra_header_count, + char* buffer, + int buffer_length, + int index); + +// Constructs a standard SPDY GET packet. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGet(const char* const extra_headers[], + int extra_header_count); + +// Constructs a standard SPDY SYN_REPLY packet to match the SPDY GET. +// |extra_headers| are the extra header-value pairs, which typically +// will vary the most between calls. +// Returns a SpdyFrame. +spdy::SpdyFrame* ConstructSpdyGetSynReply(const char* const extra_headers[], + int extra_header_count); + +// Create an async MockWrite from the given SpdyFrame. +MockWrite CreateMockWrite(spdy::SpdyFrame* req); + +// Create a MockRead from the given SpdyFrame. +MockRead CreateMockRead(spdy::SpdyFrame* resp); + +// Create a MockRead from the given SpdyFrame and sequence number. +MockRead CreateMockRead(spdy::SpdyFrame* resp, int seq); + } // namespace net #endif // NET_SPDY_SPDY_TEST_UTIL_H_ |