1 // Copyright 2015 The Chromium Authors
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_deflate_parameters.h"
6
7 #include <vector> // for iterating over extension.parameters()
8
9 #include "base/strings/string_number_conversions.h"
10
11 namespace net {
12
13 namespace {
14
15 const WebSocketDeflater::ContextTakeOverMode kTakeOverContext =
16 WebSocketDeflater::TAKE_OVER_CONTEXT;
17 const WebSocketDeflater::ContextTakeOverMode kDoNotTakeOverContext =
18 WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT;
19
20 constexpr char kServerNoContextTakeOver[] = "server_no_context_takeover";
21 constexpr char kClientNoContextTakeOver[] = "client_no_context_takeover";
22 constexpr char kServerMaxWindowBits[] = "server_max_window_bits";
23 constexpr char kClientMaxWindowBits[] = "client_max_window_bits";
24 constexpr char kExtensionName[] = "permessage-deflate";
25
GetWindowBits(const std::string & value,int * window_bits)26 bool GetWindowBits(const std::string& value, int* window_bits) {
27 return !value.empty() && value[0] != '0' &&
28 value.find_first_not_of("0123456789") == std::string::npos &&
29 base::StringToInt(value, window_bits);
30 }
31
DuplicateError(const std::string & name,std::string * failure_message)32 bool DuplicateError(const std::string& name, std::string* failure_message) {
33 *failure_message =
34 "Received duplicate permessage-deflate extension parameter " + name;
35 return false;
36 }
37
InvalidError(const std::string & name,std::string * failure_message)38 bool InvalidError(const std::string& name, std::string* failure_message) {
39 *failure_message = "Received invalid " + name + " parameter";
40 return false;
41 }
42
43 } // namespace
44
AsExtension() const45 WebSocketExtension WebSocketDeflateParameters::AsExtension() const {
46 WebSocketExtension e(kExtensionName);
47
48 if (server_context_take_over_mode_ == kDoNotTakeOverContext)
49 e.Add(WebSocketExtension::Parameter(kServerNoContextTakeOver));
50 if (client_context_take_over_mode_ == kDoNotTakeOverContext)
51 e.Add(WebSocketExtension::Parameter(kClientNoContextTakeOver));
52 if (is_server_max_window_bits_specified()) {
53 DCHECK(server_max_window_bits_.has_value);
54 e.Add(WebSocketExtension::Parameter(
55 kServerMaxWindowBits, base::NumberToString(server_max_window_bits())));
56 }
57 if (is_client_max_window_bits_specified()) {
58 if (has_client_max_window_bits_value()) {
59 e.Add(WebSocketExtension::Parameter(
60 kClientMaxWindowBits,
61 base::NumberToString(client_max_window_bits())));
62 } else {
63 e.Add(WebSocketExtension::Parameter(kClientMaxWindowBits));
64 }
65 }
66
67 return e;
68 }
69
IsValidAsRequest(std::string *) const70 bool WebSocketDeflateParameters::IsValidAsRequest(std::string*) const {
71 if (server_max_window_bits_.is_specified) {
72 DCHECK(server_max_window_bits_.has_value);
73 DCHECK(IsValidWindowBits(server_max_window_bits_.bits));
74 }
75 if (client_max_window_bits_.is_specified &&
76 client_max_window_bits_.has_value) {
77 DCHECK(IsValidWindowBits(client_max_window_bits_.bits));
78 }
79 return true;
80 }
81
IsValidAsResponse(std::string * failure_message) const82 bool WebSocketDeflateParameters::IsValidAsResponse(
83 std::string* failure_message) const {
84 if (server_max_window_bits_.is_specified) {
85 DCHECK(server_max_window_bits_.has_value);
86 DCHECK(IsValidWindowBits(server_max_window_bits_.bits));
87 }
88 if (client_max_window_bits_.is_specified) {
89 if (!client_max_window_bits_.has_value) {
90 *failure_message = "client_max_window_bits must have value";
91 return false;
92 }
93 DCHECK(IsValidWindowBits(client_max_window_bits_.bits));
94 }
95
96 return true;
97 }
98
Initialize(const WebSocketExtension & extension,std::string * failure_message)99 bool WebSocketDeflateParameters::Initialize(const WebSocketExtension& extension,
100 std::string* failure_message) {
101 *this = WebSocketDeflateParameters();
102
103 if (extension.name() != kExtensionName) {
104 *failure_message = "extension name doesn't match";
105 return false;
106 }
107 for (const auto& p : extension.parameters()) {
108 if (p.name() == kServerNoContextTakeOver) {
109 if (server_context_take_over_mode() == kDoNotTakeOverContext)
110 return DuplicateError(p.name(), failure_message);
111 if (p.HasValue())
112 return InvalidError(p.name(), failure_message);
113 SetServerNoContextTakeOver();
114 } else if (p.name() == kClientNoContextTakeOver) {
115 if (client_context_take_over_mode() == kDoNotTakeOverContext)
116 return DuplicateError(p.name(), failure_message);
117 if (p.HasValue())
118 return InvalidError(p.name(), failure_message);
119 SetClientNoContextTakeOver();
120 } else if (p.name() == kServerMaxWindowBits) {
121 if (server_max_window_bits_.is_specified)
122 return DuplicateError(p.name(), failure_message);
123 int bits;
124 if (!GetWindowBits(p.value(), &bits) || !IsValidWindowBits(bits))
125 return InvalidError(p.name(), failure_message);
126 SetServerMaxWindowBits(bits);
127 } else if (p.name() == kClientMaxWindowBits) {
128 if (client_max_window_bits_.is_specified)
129 return DuplicateError(p.name(), failure_message);
130 if (p.value().empty()) {
131 SetClientMaxWindowBits();
132 } else {
133 int bits;
134 if (!GetWindowBits(p.value(), &bits) || !IsValidWindowBits(bits))
135 return InvalidError(p.name(), failure_message);
136 SetClientMaxWindowBits(bits);
137 }
138 } else {
139 *failure_message =
140 "Received an unexpected permessage-deflate extension parameter";
141 return false;
142 }
143 }
144 return true;
145 }
146
IsCompatibleWith(const WebSocketDeflateParameters & response) const147 bool WebSocketDeflateParameters::IsCompatibleWith(
148 const WebSocketDeflateParameters& response) const {
149 const auto& request = *this;
150 DCHECK(request.IsValidAsRequest());
151 DCHECK(response.IsValidAsResponse());
152
153 // server_no_context_take_over
154 if (request.server_context_take_over_mode() == kDoNotTakeOverContext &&
155 response.server_context_take_over_mode() == kTakeOverContext) {
156 return false;
157 }
158
159 // No compatibility check is needed for client_no_context_take_over
160
161 // server_max_window_bits
162 if (request.server_max_window_bits_.is_specified) {
163 DCHECK(request.server_max_window_bits_.has_value);
164 if (!response.server_max_window_bits_.is_specified)
165 return false;
166 DCHECK(response.server_max_window_bits_.has_value);
167 if (request.server_max_window_bits_.bits <
168 response.server_max_window_bits_.bits) {
169 return false;
170 }
171 }
172
173 // client_max_window_bits
174 if (!request.client_max_window_bits_.is_specified &&
175 response.client_max_window_bits_.is_specified) {
176 return false;
177 }
178
179 return true;
180 }
181
182 } // namespace net
183