diff options
author | mlloyd@chromium.org <mlloyd@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 21:41:38 +0000 |
---|---|---|
committer | mlloyd@chromium.org <mlloyd@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-06-28 21:41:38 +0000 |
commit | 75f30cc2d6e90120b4ac43fcea1c38e783299f6e (patch) | |
tree | 53baa194842805393bb5ad720dcccfbf24af2e7e /net | |
parent | 0d03f958e30b7e6915b73f61cbe3ef79af2916dd (diff) | |
download | chromium_src-75f30cc2d6e90120b4ac43fcea1c38e783299f6e.zip chromium_src-75f30cc2d6e90120b4ac43fcea1c38e783299f6e.tar.gz chromium_src-75f30cc2d6e90120b4ac43fcea1c38e783299f6e.tar.bz2 |
Refactors SPDY frame construction methods out of
spdy_network_transaction_unittest.cc
to promote code reuse. Removes the kGetSyn and kGetSynReply binary
SPDY frame constants and replaces them with calls to factory methods,
for better clarity and to reduce maintenance costs going forward.
Also adds some helper methods for constructing mock reads and writes
from SpdyFrames.
TEST=net_unittests pass.
BUG=None
Review URL: http://codereview.chromium.org/2881001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@51049 0039d316-1c4b-4281-b951-d872f2087c98
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_ |