forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwebsocket_handshake_stream_base.cc
148 lines (134 loc) · 5.25 KB
/
websocket_handshake_stream_base.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright 2018 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/websockets/websocket_handshake_stream_base.h"
#include <unordered_set>
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/websockets/websocket_extension.h"
#include "net/websockets/websocket_extension_parser.h"
#include "net/websockets/websocket_handshake_constants.h"
namespace net {
// static
std::string WebSocketHandshakeStreamBase::MultipleHeaderValuesMessage(
const std::string& header_name) {
return std::string("'") + header_name +
"' header must not appear more than once in a response";
}
// static
void WebSocketHandshakeStreamBase::AddVectorHeaderIfNonEmpty(
const char* name,
const std::vector<std::string>& value,
HttpRequestHeaders* headers) {
if (value.empty())
return;
headers->SetHeader(name, base::JoinString(value, ", "));
}
// static
bool WebSocketHandshakeStreamBase::ValidateSubProtocol(
const HttpResponseHeaders* headers,
const std::vector<std::string>& requested_sub_protocols,
std::string* sub_protocol,
std::string* failure_message) {
size_t iter = 0;
std::string value;
std::unordered_set<std::string> requested_set(requested_sub_protocols.begin(),
requested_sub_protocols.end());
int count = 0;
bool has_multiple_protocols = false;
bool has_invalid_protocol = false;
while (!has_invalid_protocol || !has_multiple_protocols) {
std::string temp_value;
if (!headers->EnumerateHeader(&iter, websockets::kSecWebSocketProtocol,
&temp_value))
break;
value = temp_value;
if (requested_set.count(value) == 0)
has_invalid_protocol = true;
if (++count > 1)
has_multiple_protocols = true;
}
if (has_multiple_protocols) {
*failure_message =
MultipleHeaderValuesMessage(websockets::kSecWebSocketProtocol);
return false;
} else if (count > 0 && requested_sub_protocols.size() == 0) {
*failure_message = std::string(
"Response must not include 'Sec-WebSocket-Protocol' "
"header if not present in request: ") +
value;
return false;
} else if (has_invalid_protocol) {
*failure_message = "'Sec-WebSocket-Protocol' header value '" + value +
"' in response does not match any of sent values";
return false;
} else if (requested_sub_protocols.size() > 0 && count == 0) {
*failure_message =
"Sent non-empty 'Sec-WebSocket-Protocol' header "
"but no response was received";
return false;
}
*sub_protocol = value;
return true;
}
// static
bool WebSocketHandshakeStreamBase::ValidateExtensions(
const HttpResponseHeaders* headers,
std::string* accepted_extensions_descriptor,
std::string* failure_message,
WebSocketExtensionParams* params) {
size_t iter = 0;
std::string header_value;
std::vector<std::string> header_values;
// TODO(ricea): If adding support for additional extensions, generalise this
// code.
bool seen_permessage_deflate = false;
while (headers->EnumerateHeader(&iter, websockets::kSecWebSocketExtensions,
&header_value)) {
WebSocketExtensionParser parser;
if (!parser.Parse(header_value)) {
// TODO(yhirano) Set appropriate failure message.
*failure_message =
"'Sec-WebSocket-Extensions' header value is "
"rejected by the parser: " +
header_value;
return false;
}
const std::vector<WebSocketExtension>& extensions = parser.extensions();
for (const auto& extension : extensions) {
if (extension.name() == "permessage-deflate") {
if (seen_permessage_deflate) {
*failure_message = "Received duplicate permessage-deflate response";
return false;
}
seen_permessage_deflate = true;
auto& deflate_parameters = params->deflate_parameters;
if (!deflate_parameters.Initialize(extension, failure_message) ||
!deflate_parameters.IsValidAsResponse(failure_message)) {
*failure_message = "Error in permessage-deflate: " + *failure_message;
return false;
}
// Note that we don't have to check the request-response compatibility
// here because we send a request compatible with any valid responses.
// TODO(yhirano): Place a DCHECK here.
header_values.push_back(header_value);
} else {
*failure_message = "Found an unsupported extension '" +
extension.name() +
"' in 'Sec-WebSocket-Extensions' header";
return false;
}
}
}
*accepted_extensions_descriptor = base::JoinString(header_values, ", ");
params->deflate_enabled = seen_permessage_deflate;
return true;
}
void WebSocketHandshakeStreamBase::RecordHandshakeResult(
HandshakeResult result) {
UMA_HISTOGRAM_ENUMERATION("Net.WebSocket.HandshakeResult2", result,
HandshakeResult::NUM_HANDSHAKE_RESULT_TYPES);
}
} // namespace net