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