Fix WebSocketServer extension parser.

This CL makes the WebSocket server in net/server use the net/websockets
parser for parsing Sec-WebSocket-Extensions in the extension negotiation.

The new implementation validates the extension negotiation offer more
strictly than before. Specifically,

- Malformed Sec-WebSocket-Extensions header value causes connection failure.
 - Previously it was just ignored.
- Malformed permessage-deflate parameters are declined.
 - Previously part of such params were accepted partially.

BUG=523228

Review URL: https://codereview.chromium.org/1340523002

Cr-Commit-Position: refs/heads/master@{#351040}
diff --git a/net/server/web_socket_encoder.cc b/net/server/web_socket_encoder.cc
index 1a5431af..b1b93ee 100644
--- a/net/server/web_socket_encoder.cc
+++ b/net/server/web_socket_encoder.cc
@@ -4,10 +4,14 @@
 
 #include "net/server/web_socket_encoder.h"
 
+#include <vector>
+
 #include "base/logging.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/stringprintf.h"
 #include "net/base/io_buffer.h"
+#include "net/websockets/websocket_deflate_parameters.h"
+#include "net/websockets/websocket_extension.h"
 #include "net/websockets/websocket_extension_parser.h"
 
 namespace net {
@@ -180,151 +184,111 @@
 }  // anonymous namespace
 
 // static
-WebSocketEncoder* WebSocketEncoder::CreateServer(
-    const std::string& request_extensions,
-    std::string* response_extensions) {
-  bool deflate;
-  bool has_client_window_bits;
-  int client_window_bits;
-  int server_window_bits;
-  bool client_no_context_takeover;
-  bool server_no_context_takeover;
-  ParseExtensions(request_extensions, &deflate, &has_client_window_bits,
-                  &client_window_bits, &server_window_bits,
-                  &client_no_context_takeover, &server_no_context_takeover);
+scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer() {
+  return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
+}
 
-  if (deflate) {
-    *response_extensions = base::StringPrintf(
-        "permessage-deflate; server_max_window_bits=%d%s", server_window_bits,
-        server_no_context_takeover ? "; server_no_context_takeover" : "");
-    if (has_client_window_bits) {
-      base::StringAppendF(response_extensions, "; client_max_window_bits=%d",
-                          client_window_bits);
-    } else {
-      DCHECK_EQ(client_window_bits, 15);
-    }
-    return new WebSocketEncoder(true /* is_server */, server_window_bits,
-                                client_window_bits, server_no_context_takeover);
-  } else {
-    *response_extensions = std::string();
-    return new WebSocketEncoder(true /* is_server */);
+// static
+scoped_ptr<WebSocketEncoder> WebSocketEncoder::CreateServer(
+    const std::string& extensions,
+    WebSocketDeflateParameters* deflate_parameters) {
+  WebSocketExtensionParser parser;
+  if (!parser.Parse(extensions)) {
+    // Failed to parse Sec-WebSocket-Extensions header. We MUST fail the
+    // connection.
+    return nullptr;
   }
+
+  for (const auto& extension : parser.extensions()) {
+    std::string failure_message;
+    WebSocketDeflateParameters offer;
+    if (!offer.Initialize(extension, &failure_message) ||
+        !offer.IsValidAsRequest(&failure_message)) {
+      // We decline unknown / malformed extensions.
+      continue;
+    }
+
+    WebSocketDeflateParameters response = offer;
+    if (offer.is_client_max_window_bits_specified() &&
+        !offer.has_client_max_window_bits_value()) {
+      // We need to choose one value for the response.
+      response.SetClientMaxWindowBits(15);
+    }
+    DCHECK(response.IsValidAsResponse());
+    DCHECK(offer.IsCompatibleWith(response));
+    auto deflater = make_scoped_ptr(
+        new WebSocketDeflater(response.server_context_take_over_mode()));
+    auto inflater = make_scoped_ptr(
+        new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
+    if (!deflater->Initialize(response.PermissiveServerMaxWindowBits()) ||
+        !inflater->Initialize(response.PermissiveClientMaxWindowBits())) {
+      // For some reason we cannot accept the parameters.
+      continue;
+    }
+    *deflate_parameters = response;
+    return make_scoped_ptr(
+        new WebSocketEncoder(FOR_SERVER, deflater.Pass(), inflater.Pass()));
+  }
+
+  // We cannot find an acceptable offer.
+  return make_scoped_ptr(new WebSocketEncoder(FOR_SERVER, nullptr, nullptr));
 }
 
 // static
 WebSocketEncoder* WebSocketEncoder::CreateClient(
     const std::string& response_extensions) {
-  bool deflate;
-  bool has_client_window_bits;
-  int client_window_bits;
-  int server_window_bits;
-  bool client_no_context_takeover;
-  bool server_no_context_takeover;
-  ParseExtensions(response_extensions, &deflate, &has_client_window_bits,
-                  &client_window_bits, &server_window_bits,
-                  &client_no_context_takeover, &server_no_context_takeover);
-
-  if (deflate) {
-    return new WebSocketEncoder(false /* is_server */, client_window_bits,
-                                server_window_bits, client_no_context_takeover);
-  } else {
-    return new WebSocketEncoder(false /* is_server */);
-  }
-}
-
-// static
-void WebSocketEncoder::ParseExtensions(const std::string& header_value,
-                                       bool* deflate,
-                                       bool* has_client_window_bits,
-                                       int* client_window_bits,
-                                       int* server_window_bits,
-                                       bool* client_no_context_takeover,
-                                       bool* server_no_context_takeover) {
-  *deflate = false;
-  *has_client_window_bits = false;
-  *client_window_bits = 15;
-  *server_window_bits = 15;
-  *client_no_context_takeover = false;
-  *server_no_context_takeover = false;
-
-  if (header_value.empty())
-    return;
+  // TODO(yhirano): Add a way to return an error.
 
   WebSocketExtensionParser parser;
-  if (!parser.Parse(header_value))
-    return;
-  const std::vector<WebSocketExtension>& extensions = parser.extensions();
-  // TODO(tyoshino): Fail if this method is used for parsing a response and
-  // there are multiple permessage-deflate extensions or there are any unknown
-  // extensions.
-  for (const auto& extension : extensions) {
-    if (extension.name() != "permessage-deflate") {
-      continue;
-    }
-
-    const std::vector<WebSocketExtension::Parameter>& parameters =
-        extension.parameters();
-    for (const auto& param : parameters) {
-      const std::string& name = param.name();
-      // TODO(tyoshino): Fail the connection when an invalid value is given.
-      if (name == "client_max_window_bits") {
-        *has_client_window_bits = true;
-        if (param.HasValue()) {
-          int bits = 0;
-          if (base::StringToInt(param.value(), &bits) && bits >= 8 &&
-              bits <= 15) {
-            *client_window_bits = bits;
-          }
-        }
-      }
-      if (name == "server_max_window_bits" && param.HasValue()) {
-        int bits = 0;
-        if (base::StringToInt(param.value(), &bits) && bits >= 8 && bits <= 15)
-          *server_window_bits = bits;
-      }
-      if (name == "client_no_context_takeover")
-        *client_no_context_takeover = true;
-      if (name == "server_no_context_takeover")
-        *server_no_context_takeover = true;
-    }
-    *deflate = true;
-
-    break;
+  if (!parser.Parse(response_extensions)) {
+    // Parse error. Note that there are two cases here.
+    // 1) There is no Sec-WebSocket-Extensions header.
+    // 2) There is a malformed Sec-WebSocketExtensions header.
+    // We should return a deflate-disabled encoder for the former case and
+    // fail the connection for the latter case.
+    return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
   }
-}
+  if (parser.extensions().size() != 1) {
+    // Only permessage-deflate extension is supported.
+    // TODO (yhirano): Fail the connection.
+    return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
+  }
+  const auto& extension = parser.extensions()[0];
+  WebSocketDeflateParameters params;
+  std::string failure_message;
+  if (!params.Initialize(extension, &failure_message) ||
+      !params.IsValidAsResponse(&failure_message)) {
+    // TODO (yhirano): Fail the connection.
+    return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
+  }
 
-WebSocketEncoder::WebSocketEncoder(bool is_server) : is_server_(is_server) {
-}
-
-WebSocketEncoder::WebSocketEncoder(bool is_server,
-                                   int deflate_bits,
-                                   int inflate_bits,
-                                   bool no_context_takeover)
-    : is_server_(is_server) {
-  deflater_.reset(new WebSocketDeflater(
-      no_context_takeover ? WebSocketDeflater::DO_NOT_TAKE_OVER_CONTEXT
-                          : WebSocketDeflater::TAKE_OVER_CONTEXT));
-  inflater_.reset(
+  auto deflater = make_scoped_ptr(
+      new WebSocketDeflater(params.client_context_take_over_mode()));
+  auto inflater = make_scoped_ptr(
       new WebSocketInflater(kInflaterChunkSize, kInflaterChunkSize));
-
-  if (!deflater_->Initialize(deflate_bits) ||
-      !inflater_->Initialize(inflate_bits)) {
-    // Disable deflate support.
-    deflater_.reset();
-    inflater_.reset();
+  if (!deflater->Initialize(params.PermissiveClientMaxWindowBits()) ||
+      !inflater->Initialize(params.PermissiveServerMaxWindowBits())) {
+    // TODO (yhirano): Fail the connection.
+    return new WebSocketEncoder(FOR_CLIENT, nullptr, nullptr);
   }
+
+  return new WebSocketEncoder(FOR_CLIENT, deflater.Pass(), inflater.Pass());
 }
 
-WebSocketEncoder::~WebSocketEncoder() {
-}
+WebSocketEncoder::WebSocketEncoder(Type type,
+                                   scoped_ptr<WebSocketDeflater> deflater,
+                                   scoped_ptr<WebSocketInflater> inflater)
+    : type_(type), deflater_(deflater.Pass()), inflater_(inflater.Pass()) {}
+
+WebSocketEncoder::~WebSocketEncoder() {}
 
 WebSocket::ParseResult WebSocketEncoder::DecodeFrame(
     const base::StringPiece& frame,
     int* bytes_consumed,
     std::string* output) {
   bool compressed;
-  WebSocket::ParseResult result =
-      DecodeFrameHybi17(frame, is_server_, bytes_consumed, output, &compressed);
+  WebSocket::ParseResult result = DecodeFrameHybi17(
+      frame, type_ == FOR_SERVER, bytes_consumed, output, &compressed);
   if (result == WebSocket::FRAME_OK && compressed) {
     if (!Inflate(output))
       result = WebSocket::FRAME_ERROR;