Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / net / websockets / websocket_handshake_handler.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/websockets/websocket_handshake_handler.h"
6
7 #include <limits>
8
9 #include "base/base64.h"
10 #include "base/sha1.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_tokenizer.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "net/http/http_request_headers.h"
17 #include "net/http/http_response_headers.h"
18 #include "net/http/http_util.h"
19 #include "net/websockets/websocket_handshake_constants.h"
20 #include "url/gurl.h"
21
22 namespace net {
23 namespace {
24
25 const int kVersionHeaderValueForRFC6455 = 13;
26
27 // Splits |handshake_message| into Status-Line or Request-Line (including CRLF)
28 // and headers (excluding 2nd CRLF of double CRLFs at the end of a handshake
29 // response).
30 void ParseHandshakeHeader(
31     const char* handshake_message, int len,
32     std::string* request_line,
33     std::string* headers) {
34   size_t i = base::StringPiece(handshake_message, len).find_first_of("\r\n");
35   if (i == base::StringPiece::npos) {
36     *request_line = std::string(handshake_message, len);
37     *headers = "";
38     return;
39   }
40   // |request_line| includes \r\n.
41   *request_line = std::string(handshake_message, i + 2);
42
43   int header_len = len - (i + 2) - 2;
44   if (header_len > 0) {
45     // |handshake_message| includes trailing \r\n\r\n.
46     // |headers| doesn't include 2nd \r\n.
47     *headers = std::string(handshake_message + i + 2, header_len);
48   } else {
49     *headers = "";
50   }
51 }
52
53 void FetchHeaders(const std::string& headers,
54                   const char* const headers_to_get[],
55                   size_t headers_to_get_len,
56                   std::vector<std::string>* values) {
57   net::HttpUtil::HeadersIterator iter(headers.begin(), headers.end(), "\r\n");
58   while (iter.GetNext()) {
59     for (size_t i = 0; i < headers_to_get_len; i++) {
60       if (LowerCaseEqualsASCII(iter.name_begin(), iter.name_end(),
61                                headers_to_get[i])) {
62         values->push_back(iter.values());
63       }
64     }
65   }
66 }
67
68 bool GetHeaderName(std::string::const_iterator line_begin,
69                    std::string::const_iterator line_end,
70                    std::string::const_iterator* name_begin,
71                    std::string::const_iterator* name_end) {
72   std::string::const_iterator colon = std::find(line_begin, line_end, ':');
73   if (colon == line_end) {
74     return false;
75   }
76   *name_begin = line_begin;
77   *name_end = colon;
78   if (*name_begin == *name_end || net::HttpUtil::IsLWS(**name_begin))
79     return false;
80   net::HttpUtil::TrimLWS(name_begin, name_end);
81   return true;
82 }
83
84 // Similar to HttpUtil::StripHeaders, but it preserves malformed headers, that
85 // is, lines that are not formatted as "<name>: <value>\r\n".
86 std::string FilterHeaders(
87     const std::string& headers,
88     const char* const headers_to_remove[],
89     size_t headers_to_remove_len) {
90   std::string filtered_headers;
91
92   base::StringTokenizer lines(headers.begin(), headers.end(), "\r\n");
93   while (lines.GetNext()) {
94     std::string::const_iterator line_begin = lines.token_begin();
95     std::string::const_iterator line_end = lines.token_end();
96     std::string::const_iterator name_begin;
97     std::string::const_iterator name_end;
98     bool should_remove = false;
99     if (GetHeaderName(line_begin, line_end, &name_begin, &name_end)) {
100       for (size_t i = 0; i < headers_to_remove_len; ++i) {
101         if (LowerCaseEqualsASCII(name_begin, name_end, headers_to_remove[i])) {
102           should_remove = true;
103           break;
104         }
105       }
106     }
107     if (!should_remove) {
108       filtered_headers.append(line_begin, line_end);
109       filtered_headers.append("\r\n");
110     }
111   }
112   return filtered_headers;
113 }
114
115 bool CheckVersionInRequest(const std::string& request_headers) {
116   std::vector<std::string> values;
117   const char* const headers_to_get[1] = {
118     websockets::kSecWebSocketVersionLowercase};
119   FetchHeaders(request_headers, headers_to_get, 1, &values);
120   DCHECK_LE(values.size(), 1U);
121   if (values.empty())
122     return false;
123
124   int version;
125   bool conversion_success = base::StringToInt(values[0], &version);
126   if (!conversion_success)
127     return false;
128
129   return version == kVersionHeaderValueForRFC6455;
130 }
131
132 // Append a header to a string. Equivalent to
133 //   response_message += header + ": " + value + "\r\n"
134 // but avoids unnecessary allocations and copies.
135 void AppendHeader(const base::StringPiece& header,
136                   const base::StringPiece& value,
137                   std::string* response_message) {
138   static const char kColonSpace[] = ": ";
139   const size_t kColonSpaceSize = sizeof(kColonSpace) - 1;
140   static const char kCrNl[] = "\r\n";
141   const size_t kCrNlSize = sizeof(kCrNl) - 1;
142
143   size_t extra_size =
144       header.size() + kColonSpaceSize + value.size() + kCrNlSize;
145   response_message->reserve(response_message->size() + extra_size);
146   response_message->append(header.begin(), header.end());
147   response_message->append(kColonSpace, kColonSpace + kColonSpaceSize);
148   response_message->append(value.begin(), value.end());
149   response_message->append(kCrNl, kCrNl + kCrNlSize);
150 }
151
152 }  // namespace
153
154 WebSocketHandshakeRequestHandler::WebSocketHandshakeRequestHandler()
155     : original_length_(0),
156       raw_length_(0) {}
157
158 bool WebSocketHandshakeRequestHandler::ParseRequest(
159     const char* data, int length) {
160   DCHECK_GT(length, 0);
161   std::string input(data, length);
162   int input_header_length =
163       HttpUtil::LocateEndOfHeaders(input.data(), input.size(), 0);
164   if (input_header_length <= 0)
165     return false;
166
167   ParseHandshakeHeader(input.data(),
168                        input_header_length,
169                        &request_line_,
170                        &headers_);
171
172   if (!CheckVersionInRequest(headers_)) {
173     NOTREACHED();
174     return false;
175   }
176
177   original_length_ = input_header_length;
178   return true;
179 }
180
181 size_t WebSocketHandshakeRequestHandler::original_length() const {
182   return original_length_;
183 }
184
185 void WebSocketHandshakeRequestHandler::AppendHeaderIfMissing(
186     const std::string& name, const std::string& value) {
187   DCHECK(!headers_.empty());
188   HttpUtil::AppendHeaderIfMissing(name.c_str(), value, &headers_);
189 }
190
191 void WebSocketHandshakeRequestHandler::RemoveHeaders(
192     const char* const headers_to_remove[],
193     size_t headers_to_remove_len) {
194   DCHECK(!headers_.empty());
195   headers_ = FilterHeaders(
196       headers_, headers_to_remove, headers_to_remove_len);
197 }
198
199 HttpRequestInfo WebSocketHandshakeRequestHandler::GetRequestInfo(
200     const GURL& url, std::string* challenge) {
201   HttpRequestInfo request_info;
202   request_info.url = url;
203   size_t method_end = base::StringPiece(request_line_).find_first_of(" ");
204   if (method_end != base::StringPiece::npos)
205     request_info.method = std::string(request_line_.data(), method_end);
206
207   request_info.extra_headers.Clear();
208   request_info.extra_headers.AddHeadersFromString(headers_);
209
210   request_info.extra_headers.RemoveHeader(websockets::kUpgrade);
211   request_info.extra_headers.RemoveHeader(HttpRequestHeaders::kConnection);
212
213   std::string key;
214   bool header_present = request_info.extra_headers.GetHeader(
215       websockets::kSecWebSocketKey, &key);
216   DCHECK(header_present);
217   request_info.extra_headers.RemoveHeader(websockets::kSecWebSocketKey);
218   *challenge = key;
219   return request_info;
220 }
221
222 bool WebSocketHandshakeRequestHandler::GetRequestHeaderBlock(
223     const GURL& url,
224     SpdyHeaderBlock* headers,
225     std::string* challenge,
226     int spdy_protocol_version) {
227   // Construct opening handshake request headers as a SPDY header block.
228   // For details, see WebSocket Layering over SPDY/3 Draft 8.
229   if (spdy_protocol_version <= 2) {
230     (*headers)["path"] = url.path();
231     (*headers)["version"] = "WebSocket/13";
232     (*headers)["scheme"] = url.scheme();
233   } else {
234     (*headers)[":path"] = url.path();
235     (*headers)[":version"] = "WebSocket/13";
236     (*headers)[":scheme"] = url.scheme();
237   }
238
239   HttpUtil::HeadersIterator iter(headers_.begin(), headers_.end(), "\r\n");
240   while (iter.GetNext()) {
241     if (LowerCaseEqualsASCII(iter.name_begin(),
242                              iter.name_end(),
243                              websockets::kUpgradeLowercase) ||
244         LowerCaseEqualsASCII(
245             iter.name_begin(), iter.name_end(), "connection") ||
246         LowerCaseEqualsASCII(iter.name_begin(),
247                              iter.name_end(),
248                              websockets::kSecWebSocketVersionLowercase)) {
249       // These headers must be ignored.
250       continue;
251     } else if (LowerCaseEqualsASCII(iter.name_begin(),
252                                     iter.name_end(),
253                                     websockets::kSecWebSocketKeyLowercase)) {
254       *challenge = iter.values();
255       // Sec-WebSocket-Key is not sent to the server.
256       continue;
257     } else if (LowerCaseEqualsASCII(
258                    iter.name_begin(), iter.name_end(), "host") ||
259                LowerCaseEqualsASCII(
260                    iter.name_begin(), iter.name_end(), "origin") ||
261                LowerCaseEqualsASCII(
262                    iter.name_begin(),
263                    iter.name_end(),
264                    websockets::kSecWebSocketProtocolLowercase) ||
265                LowerCaseEqualsASCII(
266                    iter.name_begin(),
267                    iter.name_end(),
268                    websockets::kSecWebSocketExtensionsLowercase)) {
269       // TODO(toyoshim): Some WebSocket extensions may not be compatible with
270       // SPDY. We should omit them from a Sec-WebSocket-Extension header.
271       std::string name;
272       if (spdy_protocol_version <= 2)
273         name = base::StringToLowerASCII(iter.name());
274       else
275         name = ":" + base::StringToLowerASCII(iter.name());
276       (*headers)[name] = iter.values();
277       continue;
278     }
279     // Others should be sent out to |headers|.
280     std::string name = base::StringToLowerASCII(iter.name());
281     SpdyHeaderBlock::iterator found = headers->find(name);
282     if (found == headers->end()) {
283       (*headers)[name] = iter.values();
284     } else {
285       // For now, websocket doesn't use multiple headers, but follows to http.
286       found->second.append(1, '\0');  // +=() doesn't append 0's
287       found->second.append(iter.values());
288     }
289   }
290
291   return true;
292 }
293
294 std::string WebSocketHandshakeRequestHandler::GetRawRequest() {
295   DCHECK(!request_line_.empty());
296   DCHECK(!headers_.empty());
297
298   std::string raw_request = request_line_ + headers_ + "\r\n";
299   raw_length_ = raw_request.size();
300   return raw_request;
301 }
302
303 size_t WebSocketHandshakeRequestHandler::raw_length() const {
304   DCHECK_GT(raw_length_, 0);
305   return raw_length_;
306 }
307
308 WebSocketHandshakeResponseHandler::WebSocketHandshakeResponseHandler()
309     : original_header_length_(0) {}
310
311 WebSocketHandshakeResponseHandler::~WebSocketHandshakeResponseHandler() {}
312
313 size_t WebSocketHandshakeResponseHandler::ParseRawResponse(
314     const char* data, int length) {
315   DCHECK_GT(length, 0);
316   if (HasResponse()) {
317     DCHECK(!status_line_.empty());
318     // headers_ might be empty for wrong response from server.
319
320     return 0;
321   }
322
323   size_t old_original_length = original_.size();
324
325   original_.append(data, length);
326   // TODO(ukai): fail fast when response gives wrong status code.
327   original_header_length_ = HttpUtil::LocateEndOfHeaders(
328       original_.data(), original_.size(), 0);
329   if (!HasResponse())
330     return length;
331
332   ParseHandshakeHeader(original_.data(),
333                        original_header_length_,
334                        &status_line_,
335                        &headers_);
336   int header_size = status_line_.size() + headers_.size();
337   DCHECK_GE(original_header_length_, header_size);
338   header_separator_ = std::string(original_.data() + header_size,
339                                   original_header_length_ - header_size);
340   return original_header_length_ - old_original_length;
341 }
342
343 bool WebSocketHandshakeResponseHandler::HasResponse() const {
344   return original_header_length_ > 0 &&
345       static_cast<size_t>(original_header_length_) <= original_.size();
346 }
347
348 void ComputeSecWebSocketAccept(const std::string& key,
349                                std::string* accept) {
350   DCHECK(accept);
351
352   std::string hash =
353       base::SHA1HashString(key + websockets::kWebSocketGuid);
354   base::Base64Encode(hash, accept);
355 }
356
357 bool WebSocketHandshakeResponseHandler::ParseResponseInfo(
358     const HttpResponseInfo& response_info,
359     const std::string& challenge) {
360   if (!response_info.headers.get())
361     return false;
362
363   // TODO(ricea): Eliminate all the reallocations and string copies.
364   std::string response_message;
365   response_message = response_info.headers->GetStatusLine();
366   response_message += "\r\n";
367
368   AppendHeader(websockets::kUpgrade,
369                websockets::kWebSocketLowercase,
370                &response_message);
371
372   AppendHeader(
373       HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
374
375   std::string websocket_accept;
376   ComputeSecWebSocketAccept(challenge, &websocket_accept);
377   AppendHeader(
378       websockets::kSecWebSocketAccept, websocket_accept, &response_message);
379
380   void* iter = NULL;
381   std::string name;
382   std::string value;
383   while (response_info.headers->EnumerateHeaderLines(&iter, &name, &value)) {
384     AppendHeader(name, value, &response_message);
385   }
386   response_message += "\r\n";
387
388   return ParseRawResponse(response_message.data(),
389                           response_message.size()) == response_message.size();
390 }
391
392 bool WebSocketHandshakeResponseHandler::ParseResponseHeaderBlock(
393     const SpdyHeaderBlock& headers,
394     const std::string& challenge,
395     int spdy_protocol_version) {
396   SpdyHeaderBlock::const_iterator status;
397   if (spdy_protocol_version <= 2)
398     status = headers.find("status");
399   else
400     status = headers.find(":status");
401   if (status == headers.end())
402     return false;
403
404   std::string hash =
405       base::SHA1HashString(challenge + websockets::kWebSocketGuid);
406   std::string websocket_accept;
407   base::Base64Encode(hash, &websocket_accept);
408
409   std::string response_message = base::StringPrintf(
410       "%s %s\r\n", websockets::kHttpProtocolVersion, status->second.c_str());
411
412   AppendHeader(
413       websockets::kUpgrade, websockets::kWebSocketLowercase, &response_message);
414   AppendHeader(
415       HttpRequestHeaders::kConnection, websockets::kUpgrade, &response_message);
416   AppendHeader(
417       websockets::kSecWebSocketAccept, websocket_accept, &response_message);
418
419   for (SpdyHeaderBlock::const_iterator iter = headers.begin();
420        iter != headers.end();
421        ++iter) {
422     // For each value, if the server sends a NUL-separated list of values,
423     // we separate that back out into individual headers for each value
424     // in the list.
425     if ((spdy_protocol_version <= 2 &&
426          LowerCaseEqualsASCII(iter->first, "status")) ||
427         (spdy_protocol_version >= 3 &&
428          LowerCaseEqualsASCII(iter->first, ":status"))) {
429       // The status value is already handled as the first line of
430       // |response_message|. Just skip here.
431       continue;
432     }
433     const std::string& value = iter->second;
434     size_t start = 0;
435     size_t end = 0;
436     do {
437       end = value.find('\0', start);
438       std::string tval;
439       if (end != std::string::npos)
440         tval = value.substr(start, (end - start));
441       else
442         tval = value.substr(start);
443       if (spdy_protocol_version >= 3 &&
444           (LowerCaseEqualsASCII(iter->first,
445                                 websockets::kSecWebSocketProtocolSpdy3) ||
446            LowerCaseEqualsASCII(iter->first,
447                                 websockets::kSecWebSocketExtensionsSpdy3)))
448         AppendHeader(iter->first.substr(1), tval, &response_message);
449       else
450         AppendHeader(iter->first, tval, &response_message);
451       start = end + 1;
452     } while (end != std::string::npos);
453   }
454   response_message += "\r\n";
455
456   return ParseRawResponse(response_message.data(),
457                           response_message.size()) == response_message.size();
458 }
459
460 void WebSocketHandshakeResponseHandler::GetHeaders(
461     const char* const headers_to_get[],
462     size_t headers_to_get_len,
463     std::vector<std::string>* values) {
464   DCHECK(HasResponse());
465   DCHECK(!status_line_.empty());
466   // headers_ might be empty for wrong response from server.
467   if (headers_.empty())
468     return;
469
470   FetchHeaders(headers_, headers_to_get, headers_to_get_len, values);
471 }
472
473 void WebSocketHandshakeResponseHandler::RemoveHeaders(
474     const char* const headers_to_remove[],
475     size_t headers_to_remove_len) {
476   DCHECK(HasResponse());
477   DCHECK(!status_line_.empty());
478   // headers_ might be empty for wrong response from server.
479   if (headers_.empty())
480     return;
481
482   headers_ = FilterHeaders(headers_, headers_to_remove, headers_to_remove_len);
483 }
484
485 std::string WebSocketHandshakeResponseHandler::GetRawResponse() const {
486   DCHECK(HasResponse());
487   return original_.substr(0, original_header_length_);
488 }
489
490 std::string WebSocketHandshakeResponseHandler::GetResponse() {
491   DCHECK(HasResponse());
492   DCHECK(!status_line_.empty());
493   // headers_ might be empty for wrong response from server.
494
495   return status_line_ + headers_ + header_separator_;
496 }
497
498 }  // namespace net