diff options
Diffstat (limited to 'net/spdy/spdy_test_util.cc')
-rwxr-xr-x | net/spdy/spdy_test_util.cc | 367 |
1 files changed, 367 insertions, 0 deletions
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 |