summaryrefslogtreecommitdiff
path: root/chromium/net/tools/quic/spdy_utils.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/net/tools/quic/spdy_utils.cc')
-rw-r--r--chromium/net/tools/quic/spdy_utils.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/chromium/net/tools/quic/spdy_utils.cc b/chromium/net/tools/quic/spdy_utils.cc
new file mode 100644
index 00000000000..13d05e5e706
--- /dev/null
+++ b/chromium/net/tools/quic/spdy_utils.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2012 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/tools/quic/spdy_utils.h"
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_piece.h"
+#include "base/strings/string_util.h"
+#include "net/spdy/spdy_frame_builder.h"
+#include "net/spdy/spdy_framer.h"
+#include "net/spdy/spdy_protocol.h"
+#include "net/tools/flip_server/balsa_headers.h"
+#include "url/gurl.h"
+
+using base::StringPiece;
+using std::pair;
+using std::string;
+
+namespace net {
+namespace tools {
+
+const char* const kV3Host = ":host";
+const char* const kV3Path = ":path";
+const char* const kV3Scheme = ":scheme";
+const char* const kV3Status = ":status";
+const char* const kV3Method = ":method";
+const char* const kV3Version = ":version";
+
+void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
+ SpdyHeaderBlock* block,
+ bool allow_empty_values) {
+ for (BalsaHeaders::const_header_lines_iterator hi =
+ headers.header_lines_begin();
+ hi != headers.header_lines_end();
+ ++hi) {
+ if ((hi->second.length() == 0) && !allow_empty_values) {
+ DLOG(INFO) << "Dropping empty header " << hi->first.as_string()
+ << " from headers";
+ continue;
+ }
+
+ // This unfortunately involves loads of copying, but its the simplest way
+ // to sort the headers and leverage the framer.
+ string name = hi->first.as_string();
+ StringToLowerASCII(&name);
+ SpdyHeaderBlock::iterator it = block->find(name);
+ if (it != block->end()) {
+ it->second.reserve(it->second.size() + 1 + hi->second.size());
+ it->second.append("\0", 1);
+ it->second.append(hi->second.data(), hi->second.size());
+ } else {
+ block->insert(make_pair(name, hi->second.as_string()));
+ }
+ }
+}
+
+void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
+ const string& scheme,
+ const string& host_and_port,
+ const string& path,
+ SpdyHeaderBlock* block) {
+ PopulateSpdyHeaderBlock(headers, block, true);
+ StringPiece host_header = headers.GetHeader("Host");
+ if (!host_header.empty()) {
+ DCHECK(host_and_port.empty() || host_header == host_and_port);
+ block->insert(make_pair(kV3Host, host_header.as_string()));
+ } else {
+ block->insert(make_pair(kV3Host, host_and_port));
+ }
+ block->insert(make_pair(kV3Path, path));
+ block->insert(make_pair(kV3Scheme, scheme));
+
+ if (!headers.request_method().empty()) {
+ block->insert(make_pair(kV3Method, headers.request_method().as_string()));
+ }
+
+ if (!headers.request_version().empty()) {
+ (*block)[kV3Version] = headers.request_version().as_string();
+ }
+}
+
+void PopulateSpdyResponseHeaderBlock(const BalsaHeaders& headers,
+ SpdyHeaderBlock* block) {
+ string status = headers.response_code().as_string();
+ status.append(" ");
+ status.append(headers.response_reason_phrase().as_string());
+ (*block)[kV3Status] = status;
+ (*block)[kV3Version] =
+ headers.response_version().as_string();
+
+ // Empty header values are only allowed because this is spdy3.
+ PopulateSpdyHeaderBlock(headers, block, true);
+}
+
+// static
+SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdyHeaders(
+ const BalsaHeaders& request_headers) {
+ string scheme;
+ string host_and_port;
+ string path;
+
+ string url = request_headers.request_uri().as_string();
+ if (url.empty() || url[0] == '/') {
+ path = url;
+ } else {
+ GURL request_uri(url);
+ if (request_headers.request_method() == "CONNECT") {
+ path = url;
+ } else {
+ path = request_uri.path();
+ if (!request_uri.query().empty()) {
+ path = path + "?" + request_uri.query();
+ }
+ host_and_port = request_uri.host();
+ scheme = request_uri.scheme();
+ }
+ }
+
+ DCHECK(!scheme.empty());
+ DCHECK(!host_and_port.empty());
+ DCHECK(!path.empty());
+
+ SpdyHeaderBlock block;
+ PopulateSpdy3RequestHeaderBlock(
+ request_headers, scheme, host_and_port, path, &block);
+ if (block.find("host") != block.end()) {
+ block.erase(block.find("host"));
+ }
+ return block;
+}
+
+// static
+string SpdyUtils::SerializeRequestHeaders(const BalsaHeaders& request_headers) {
+ SpdyHeaderBlock block = RequestHeadersToSpdyHeaders(request_headers);
+ return SerializeUncompressedHeaders(block);
+}
+
+// static
+SpdyHeaderBlock SpdyUtils::ResponseHeadersToSpdyHeaders(
+ const BalsaHeaders& response_headers) {
+ SpdyHeaderBlock block;
+ PopulateSpdyResponseHeaderBlock(response_headers, &block);
+ return block;
+}
+
+// static
+string SpdyUtils::SerializeResponseHeaders(
+ const BalsaHeaders& response_headers) {
+ SpdyHeaderBlock block = ResponseHeadersToSpdyHeaders(response_headers);
+
+ return SerializeUncompressedHeaders(block);
+}
+
+// static
+string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
+ int length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
+ SpdyFrameBuilder builder(length);
+ SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
+ scoped_ptr<SpdyFrame> block(builder.take());
+ return string(block->data(), length);
+}
+
+bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
+ BalsaHeaders* headers) {
+ if (header->first.empty() || header->second.empty()) {
+ return true;
+ }
+ const string& header_name = header->first;
+ return header_name.c_str()[0] == ':';
+}
+
+bool SpdyUtils::FillBalsaRequestHeaders(
+ const SpdyHeaderBlock& header_block,
+ BalsaHeaders* request_headers) {
+ typedef SpdyHeaderBlock::const_iterator BlockIt;
+
+ BlockIt host_it = header_block.find(kV3Host);
+ BlockIt path_it = header_block.find(kV3Path);
+ BlockIt scheme_it = header_block.find(kV3Scheme);
+ BlockIt method_it = header_block.find(kV3Method);
+ BlockIt end_it = header_block.end();
+ if (host_it == end_it || path_it == end_it || scheme_it == end_it ||
+ method_it == end_it) {
+ return false;
+ }
+ string url = scheme_it->second;
+ url.append("://");
+ url.append(host_it->second);
+ url.append(path_it->second);
+ request_headers->SetRequestUri(url);
+ request_headers->SetRequestMethod(method_it->second);
+
+ BlockIt cl_it = header_block.find("content-length");
+ if (cl_it != header_block.end()) {
+ int content_length;
+ if (!base::StringToInt(cl_it->second, &content_length)) {
+ return false;
+ }
+ request_headers->SetContentLength(content_length);
+ }
+
+ for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
+ if (!IsSpecialSpdyHeader(it, request_headers)) {
+ request_headers->AppendHeader(it->first, it->second);
+ }
+ }
+
+ return true;
+}
+
+// The reason phrase should match regexp [\d\d\d [^\r\n]+]. If not, we will
+// fail to parse it.
+bool ParseReasonAndStatus(StringPiece status_and_reason,
+ BalsaHeaders* headers) {
+ if (status_and_reason.size() < 5)
+ return false;
+
+ if (status_and_reason[3] != ' ')
+ return false;
+
+ const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
+ int status;
+ if (!base::StringToInt(status_str, &status)) {
+ return false;
+ }
+
+ headers->SetResponseCode(status_str);
+ headers->set_parsed_response_code(status);
+
+ StringPiece reason(status_and_reason.data() + 4,
+ status_and_reason.length() - 4);
+
+ headers->SetResponseReasonPhrase(reason);
+ return true;
+}
+
+bool SpdyUtils::FillBalsaResponseHeaders(
+ const SpdyHeaderBlock& header_block,
+ BalsaHeaders* request_headers) {
+ typedef SpdyHeaderBlock::const_iterator BlockIt;
+
+ BlockIt status_it = header_block.find(kV3Status);
+ BlockIt version_it = header_block.find(kV3Version);
+ BlockIt end_it = header_block.end();
+ if (status_it == end_it || version_it == end_it) {
+ return false;
+ }
+
+ request_headers->SetRequestVersion(version_it->second);
+ if (!ParseReasonAndStatus(status_it->second, request_headers)) {
+ return false;
+ }
+ for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
+ if (!IsSpecialSpdyHeader(it, request_headers)) {
+ request_headers->AppendHeader(it->first, it->second);
+ }
+ }
+ return true;
+}
+
+} // namespace tools
+} // namespace net