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