Home | History | Annotate | Download | only in websockets
      1 // Copyright 2013 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_extension_parser.h"
      6 
      7 #include "base/strings/string_util.h"
      8 
      9 namespace net {
     10 
     11 WebSocketExtensionParser::WebSocketExtensionParser() {}
     12 
     13 WebSocketExtensionParser::~WebSocketExtensionParser() {}
     14 
     15 void WebSocketExtensionParser::Parse(const char* data, size_t size) {
     16   current_ = data;
     17   end_ = data + size;
     18   has_error_ = false;
     19 
     20   ConsumeExtension(&extension_);
     21   if (has_error_) return;
     22   ConsumeSpaces();
     23   has_error_ = has_error_ || (current_ != end_);
     24 }
     25 
     26 void WebSocketExtensionParser::Consume(char c) {
     27   DCHECK(!has_error_);
     28   ConsumeSpaces();
     29   DCHECK(!has_error_);
     30   if (current_ == end_ || c != current_[0]) {
     31     has_error_ = true;
     32     return;
     33   }
     34   ++current_;
     35 }
     36 
     37 void WebSocketExtensionParser::ConsumeExtension(WebSocketExtension* extension) {
     38   DCHECK(!has_error_);
     39   base::StringPiece name;
     40   ConsumeToken(&name);
     41   if (has_error_) return;
     42   *extension = WebSocketExtension(name.as_string());
     43 
     44   while (ConsumeIfMatch(';')) {
     45     WebSocketExtension::Parameter parameter((std::string()));
     46     ConsumeExtensionParameter(&parameter);
     47     if (has_error_) return;
     48     extension->Add(parameter);
     49   }
     50 }
     51 
     52 void WebSocketExtensionParser::ConsumeExtensionParameter(
     53     WebSocketExtension::Parameter* parameter) {
     54   DCHECK(!has_error_);
     55   base::StringPiece name, value;
     56   std::string value_string;
     57 
     58   ConsumeToken(&name);
     59   if (has_error_) return;
     60   if (!ConsumeIfMatch('=')) {
     61     *parameter = WebSocketExtension::Parameter(name.as_string());
     62     return;
     63   }
     64 
     65   if (Lookahead('\"')) {
     66     ConsumeQuotedToken(&value_string);
     67   } else {
     68     ConsumeToken(&value);
     69     value_string = value.as_string();
     70   }
     71   if (has_error_) return;
     72   *parameter = WebSocketExtension::Parameter(name.as_string(), value_string);
     73 }
     74 
     75 void WebSocketExtensionParser::ConsumeToken(base::StringPiece* token) {
     76   DCHECK(!has_error_);
     77   ConsumeSpaces();
     78   DCHECK(!has_error_);
     79   const char* head = current_;
     80   while (current_ < end_ &&
     81          !IsControl(current_[0]) && !IsSeparator(current_[0]))
     82     ++current_;
     83   if (current_ == head) {
     84     has_error_ = true;
     85     return;
     86   }
     87   *token = base::StringPiece(head, current_ - head);
     88 }
     89 
     90 void WebSocketExtensionParser::ConsumeQuotedToken(std::string* token) {
     91   DCHECK(!has_error_);
     92   Consume('"');
     93   if (has_error_) return;
     94   *token = "";
     95   while (current_ < end_ && !IsControl(current_[0])) {
     96     if (UnconsumedBytes() >= 2 && current_[0] == '\\') {
     97       char next = current_[1];
     98       if (IsControl(next) || IsSeparator(next)) break;
     99       *token += next;
    100       current_ += 2;
    101     } else if (IsSeparator(current_[0])) {
    102       break;
    103     } else {
    104       *token += current_[0];
    105       ++current_;
    106     }
    107   }
    108   // We can't use Consume here because we don't want to consume spaces.
    109   if (current_ < end_ && current_[0] == '"')
    110     ++current_;
    111   else
    112     has_error_ = true;
    113   has_error_ = has_error_ || token->empty();
    114 }
    115 
    116 void WebSocketExtensionParser::ConsumeSpaces() {
    117   DCHECK(!has_error_);
    118   while (current_ < end_ && (current_[0] == ' ' || current_[0] == '\t'))
    119     ++current_;
    120   return;
    121 }
    122 
    123 bool WebSocketExtensionParser::Lookahead(char c) {
    124   DCHECK(!has_error_);
    125   const char* head = current_;
    126 
    127   Consume(c);
    128   bool result = !has_error_;
    129   current_ = head;
    130   has_error_ = false;
    131   return result;
    132 }
    133 
    134 bool WebSocketExtensionParser::ConsumeIfMatch(char c) {
    135   DCHECK(!has_error_);
    136   const char* head = current_;
    137 
    138   Consume(c);
    139   if (has_error_) {
    140     current_ = head;
    141     has_error_ = false;
    142     return false;
    143   }
    144   return true;
    145 }
    146 
    147 // static
    148 bool WebSocketExtensionParser::IsControl(char c) {
    149   return (0 <= c && c <= 31) || c == 127;
    150 }
    151 
    152 // static
    153 bool WebSocketExtensionParser::IsSeparator(char c) {
    154   const char separators[] = "()<>@,;:\\\"/[]?={} \t";
    155   return strchr(separators, c) != NULL;
    156 }
    157 
    158 }  // namespace net
    159