Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / net / tools / quic / spdy_utils.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/tools/quic/spdy_utils.h"
6
7 #include <string>
8
9 #include "base/memory/scoped_ptr.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "net/spdy/spdy_frame_builder.h"
14 #include "net/spdy/spdy_framer.h"
15 #include "net/spdy/spdy_protocol.h"
16 #include "net/tools/balsa/balsa_headers.h"
17 #include "url/gurl.h"
18
19 using base::StringPiece;
20 using std::pair;
21 using std::string;
22
23 namespace net {
24 namespace tools {
25
26 const char* const kV3Host = ":host";
27 const char* const kV3Path = ":path";
28 const char* const kV3Scheme = ":scheme";
29 const char* const kV3Status = ":status";
30 const char* const kV3Method = ":method";
31 const char* const kV3Version = ":version";
32
33 void PopulateSpdyHeaderBlock(const BalsaHeaders& headers,
34                              SpdyHeaderBlock* block,
35                              bool allow_empty_values) {
36   for (BalsaHeaders::const_header_lines_iterator hi =
37        headers.header_lines_begin();
38        hi != headers.header_lines_end();
39        ++hi) {
40     if ((hi->second.length() == 0) && !allow_empty_values) {
41       DVLOG(1) << "Dropping empty header " << hi->first.as_string()
42                << " from headers";
43       continue;
44     }
45
46     // This unfortunately involves loads of copying, but its the simplest way
47     // to sort the headers and leverage the framer.
48     string name = hi->first.as_string();
49     base::StringToLowerASCII(&name);
50     SpdyHeaderBlock::iterator it = block->find(name);
51     if (it != block->end()) {
52       it->second.reserve(it->second.size() + 1 + hi->second.size());
53       it->second.append("\0", 1);
54       it->second.append(hi->second.data(), hi->second.size());
55     } else {
56       block->insert(make_pair(name, hi->second.as_string()));
57     }
58   }
59 }
60
61 void PopulateSpdy3RequestHeaderBlock(const BalsaHeaders& headers,
62                                      const string& scheme,
63                                      const string& host_and_port,
64                                      const string& path,
65                                      SpdyHeaderBlock* block) {
66   PopulateSpdyHeaderBlock(headers, block, true);
67   StringPiece host_header = headers.GetHeader("Host");
68   if (!host_header.empty()) {
69     DCHECK(host_and_port.empty() || host_header == host_and_port);
70     block->insert(make_pair(kV3Host, host_header.as_string()));
71   } else {
72     block->insert(make_pair(kV3Host, host_and_port));
73   }
74   block->insert(make_pair(kV3Path, path));
75   block->insert(make_pair(kV3Scheme, scheme));
76
77   if (!headers.request_method().empty()) {
78     block->insert(make_pair(kV3Method, headers.request_method().as_string()));
79   }
80
81   if (!headers.request_version().empty()) {
82     (*block)[kV3Version] = headers.request_version().as_string();
83   }
84 }
85
86 void PopulateSpdyResponseHeaderBlock(const BalsaHeaders& headers,
87                                      SpdyHeaderBlock* block) {
88   string status = headers.response_code().as_string();
89   status.append(" ");
90   status.append(headers.response_reason_phrase().as_string());
91   (*block)[kV3Status] = status;
92   (*block)[kV3Version] =
93       headers.response_version().as_string();
94
95   // Empty header values are only allowed because this is spdy3.
96   PopulateSpdyHeaderBlock(headers, block, true);
97 }
98
99 // static
100 SpdyHeaderBlock SpdyUtils::RequestHeadersToSpdyHeaders(
101     const BalsaHeaders& request_headers) {
102   string scheme;
103   string host_and_port;
104   string path;
105
106   string url = request_headers.request_uri().as_string();
107   if (url.empty() || url[0] == '/') {
108     path = url;
109   } else {
110     GURL request_uri(url);
111     if (request_headers.request_method() == "CONNECT") {
112       path = url;
113     } else {
114       path = request_uri.path();
115       if (!request_uri.query().empty()) {
116         path = path + "?" + request_uri.query();
117       }
118       host_and_port = request_uri.host();
119       scheme = request_uri.scheme();
120     }
121   }
122
123   DCHECK(!scheme.empty());
124   DCHECK(!host_and_port.empty());
125   DCHECK(!path.empty());
126
127   SpdyHeaderBlock block;
128   PopulateSpdy3RequestHeaderBlock(
129       request_headers, scheme, host_and_port, path, &block);
130   if (block.find("host") != block.end()) {
131     block.erase(block.find("host"));
132   }
133   return block;
134 }
135
136 // static
137 string SpdyUtils::SerializeRequestHeaders(const BalsaHeaders& request_headers) {
138   SpdyHeaderBlock block = RequestHeadersToSpdyHeaders(request_headers);
139   return SerializeUncompressedHeaders(block);
140 }
141
142 // static
143 SpdyHeaderBlock SpdyUtils::ResponseHeadersToSpdyHeaders(
144     const BalsaHeaders& response_headers) {
145   SpdyHeaderBlock block;
146   PopulateSpdyResponseHeaderBlock(response_headers, &block);
147   return block;
148 }
149
150 // static
151 string SpdyUtils::SerializeResponseHeaders(
152     const BalsaHeaders& response_headers) {
153   SpdyHeaderBlock block = ResponseHeadersToSpdyHeaders(response_headers);
154
155   return SerializeUncompressedHeaders(block);
156 }
157
158 // static
159 string SpdyUtils::SerializeUncompressedHeaders(const SpdyHeaderBlock& headers) {
160   size_t length = SpdyFramer::GetSerializedLength(SPDY3, &headers);
161   SpdyFrameBuilder builder(length, SPDY3);
162   SpdyFramer::WriteHeaderBlock(&builder, SPDY3, &headers);
163   scoped_ptr<SpdyFrame> block(builder.take());
164   return string(block->data(), length);
165 }
166
167 bool IsSpecialSpdyHeader(SpdyHeaderBlock::const_iterator header,
168                             BalsaHeaders* headers) {
169   if (header->first.empty() || header->second.empty()) {
170     return true;
171   }
172   const string& header_name = header->first;
173   return header_name.c_str()[0] == ':';
174 }
175
176 bool SpdyUtils::FillBalsaRequestHeaders(
177     const SpdyHeaderBlock& header_block,
178     BalsaHeaders* request_headers) {
179   typedef SpdyHeaderBlock::const_iterator BlockIt;
180
181   BlockIt host_it = header_block.find(kV3Host);
182   BlockIt path_it = header_block.find(kV3Path);
183   BlockIt scheme_it = header_block.find(kV3Scheme);
184   BlockIt method_it = header_block.find(kV3Method);
185   BlockIt end_it = header_block.end();
186   if (host_it == end_it || path_it == end_it || scheme_it == end_it ||
187       method_it == end_it) {
188     return false;
189   }
190   string url = scheme_it->second;
191   url.append("://");
192   url.append(host_it->second);
193   url.append(path_it->second);
194   request_headers->SetRequestUri(url);
195   request_headers->SetRequestMethod(method_it->second);
196
197   BlockIt cl_it = header_block.find("content-length");
198   if (cl_it != header_block.end()) {
199     int content_length;
200     if (!base::StringToInt(cl_it->second, &content_length)) {
201       return false;
202     }
203     request_headers->SetContentLength(content_length);
204   }
205
206   for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
207    if (!IsSpecialSpdyHeader(it, request_headers)) {
208      request_headers->AppendHeader(it->first, it->second);
209    }
210   }
211
212   return true;
213 }
214
215 // The reason phrase should match regexp [\d\d\d [^\r\n]+].  If not, we will
216 // fail to parse it.
217 bool ParseReasonAndStatus(StringPiece status_and_reason,
218                           BalsaHeaders* headers) {
219   if (status_and_reason.size() < 5)
220     return false;
221
222   if (status_and_reason[3] != ' ')
223     return false;
224
225   const StringPiece status_str = StringPiece(status_and_reason.data(), 3);
226   int status;
227   if (!base::StringToInt(status_str, &status)) {
228     return false;
229   }
230
231   headers->SetResponseCode(status_str);
232   headers->set_parsed_response_code(status);
233
234   StringPiece reason(status_and_reason.data() + 4,
235                      status_and_reason.length() - 4);
236
237   headers->SetResponseReasonPhrase(reason);
238   return true;
239 }
240
241 bool SpdyUtils::FillBalsaResponseHeaders(
242     const SpdyHeaderBlock& header_block,
243     BalsaHeaders* request_headers) {
244   typedef SpdyHeaderBlock::const_iterator BlockIt;
245
246   BlockIt status_it = header_block.find(kV3Status);
247   BlockIt version_it = header_block.find(kV3Version);
248   BlockIt end_it = header_block.end();
249   if (status_it == end_it || version_it == end_it) {
250     return false;
251   }
252
253   if (!ParseReasonAndStatus(status_it->second, request_headers)) {
254     return false;
255   }
256   request_headers->SetResponseVersion(version_it->second);
257   for (BlockIt it = header_block.begin(); it != header_block.end(); ++it) {
258    if (!IsSpecialSpdyHeader(it, request_headers)) {
259      request_headers->AppendHeader(it->first, it->second);
260    }
261   }
262   return true;
263 }
264
265 }  // namespace tools
266 }  // namespace net