Index: net/spdy/spdy_test_util.cc |
diff --git a/net/spdy/spdy_test_util.cc b/net/spdy/spdy_test_util.cc |
new file mode 100755 |
index 0000000000000000000000000000000000000000..bed70319f4b6644cf714878bc34120b76b947a67 |
--- /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 |