websocketpp  0.4.0
C++/Boost Asio based websocket client/server library
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Pages
parser.hpp
1 /*
2  * Copyright (c) 2014, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  * * Redistributions of source code must retain the above copyright
7  * notice, this list of conditions and the following disclaimer.
8  * * Redistributions in binary form must reproduce the above copyright
9  * notice, this list of conditions and the following disclaimer in the
10  * documentation and/or other materials provided with the distribution.
11  * * Neither the name of the WebSocket++ Project nor the
12  * names of its contributors may be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef HTTP_PARSER_HPP
29 #define HTTP_PARSER_HPP
30 
31 #include <algorithm>
32 #include <iostream>
33 #include <map>
34 
35 #include <websocketpp/utilities.hpp>
36 #include <websocketpp/http/constants.hpp>
37 
38 namespace websocketpp {
39 namespace http {
40 namespace parser {
41 
42 namespace state {
43  enum value {
44  method,
45  resource,
46  version,
47  headers
48  };
49 }
50 
51 typedef std::map<std::string, std::string, utility::ci_less > header_list;
52 
54 
63 template <typename InputIterator>
64 std::pair<std::string,InputIterator> extract_token(InputIterator begin,
65  InputIterator end)
66 {
67  InputIterator it = std::find_if(begin,end,&is_not_token_char);
68  return std::make_pair(std::string(begin,it),it);
69 }
70 
72 
82 template <typename InputIterator>
83 std::pair<std::string,InputIterator> extract_quoted_string(InputIterator begin,
84  InputIterator end)
85 {
86  std::string s;
87 
88  if (end == begin) {
89  return std::make_pair(s,begin);
90  }
91 
92  if (*begin != '"') {
93  return std::make_pair(s,begin);
94  }
95 
96  InputIterator cursor = begin+1;
97  InputIterator marker = cursor;
98 
99  cursor = std::find(cursor,end,'"');
100 
101  while (cursor != end) {
102  // either this is the end or a quoted string
103  if (*(cursor-1) == '\\') {
104  s.append(marker,cursor-1);
105  s.append(1,'"');
106  ++cursor;
107  marker = cursor;
108  } else {
109  s.append(marker,cursor);
110  ++cursor;
111  return std::make_pair(s,cursor);
112  }
113 
114  cursor = std::find(cursor,end,'"');
115  }
116 
117  return std::make_pair("",begin);
118 }
119 
121 
129 template <typename InputIterator>
130 InputIterator extract_lws(InputIterator begin, InputIterator end) {
131  InputIterator it = begin;
132 
133  // strip leading CRLF
134  if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' &&
135  is_whitespace_char(static_cast<unsigned char>(*(begin+2))))
136  {
137  it+=3;
138  }
139 
140  it = std::find_if(it,end,&is_not_whitespace_char);
141  return it;
142 }
143 
145 
154 template <typename InputIterator>
155 InputIterator extract_all_lws(InputIterator begin, InputIterator end) {
156  InputIterator old_it;
157  InputIterator new_it = begin;
158 
159  do {
160  // Pull value from previous iteration
161  old_it = new_it;
162 
163  // look ahead another pass
164  new_it = extract_lws(old_it,end);
165  } while (new_it != end && old_it != new_it);
166 
167  return new_it;
168 }
169 
171 
185 template <typename InputIterator>
186 InputIterator extract_attributes(InputIterator begin, InputIterator end,
187  attribute_list & attributes)
188 {
189  InputIterator cursor;
190  bool first = true;
191 
192  if (begin == end) {
193  return begin;
194  }
195 
196  cursor = begin;
197  std::pair<std::string,InputIterator> ret;
198 
199  while (cursor != end) {
200  std::string name;
201 
202  cursor = http::parser::extract_all_lws(cursor,end);
203  if (cursor == end) {
204  break;
205  }
206 
207  if (first) {
208  // ignore this check for the very first pass
209  first = false;
210  } else {
211  if (*cursor == ';') {
212  // advance past the ';'
213  ++cursor;
214  } else {
215  // non-semicolon in this position indicates end end of the
216  // attribute list, break and return.
217  break;
218  }
219  }
220 
221  cursor = http::parser::extract_all_lws(cursor,end);
222  ret = http::parser::extract_token(cursor,end);
223 
224  if (ret.first == "") {
225  // error: expected a token
226  return begin;
227  } else {
228  name = ret.first;
229  cursor = ret.second;
230  }
231 
232  cursor = http::parser::extract_all_lws(cursor,end);
233  if (cursor == end || *cursor != '=') {
234  // if there is an equals sign, read the attribute value. Otherwise
235  // record a blank value and continue
236  attributes[name] = "";
237  continue;
238  }
239 
240  // advance past the '='
241  ++cursor;
242 
243  cursor = http::parser::extract_all_lws(cursor,end);
244  if (cursor == end) {
245  // error: expected a token or quoted string
246  return begin;
247  }
248 
249  ret = http::parser::extract_quoted_string(cursor,end);
250  if (ret.second != cursor) {
251  attributes[name] = ret.first;
252  cursor = ret.second;
253  continue;
254  }
255 
256  ret = http::parser::extract_token(cursor,end);
257  if (ret.first == "") {
258  // error : expected token or quoted string
259  return begin;
260  } else {
261  attributes[name] = ret.first;
262  cursor = ret.second;
263  }
264  }
265 
266  return cursor;
267 }
268 
270 
283 template <typename InputIterator>
284 InputIterator extract_parameters(InputIterator begin, InputIterator end,
285  parameter_list &parameters)
286 {
287  InputIterator cursor;
288 
289  if (begin == end) {
290  // error: expected non-zero length range
291  return begin;
292  }
293 
294  cursor = begin;
295  std::pair<std::string,InputIterator> ret;
296 
305  while (cursor != end) {
306  std::string parameter_name;
307  attribute_list attributes;
308 
309  // extract any stray whitespace
310  cursor = http::parser::extract_all_lws(cursor,end);
311  if (cursor == end) {break;}
312 
313  ret = http::parser::extract_token(cursor,end);
314 
315  if (ret.first == "") {
316  // error: expected a token
317  return begin;
318  } else {
319  parameter_name = ret.first;
320  cursor = ret.second;
321  }
322 
323  // Safe break point, insert parameter with blank attributes and exit
324  cursor = http::parser::extract_all_lws(cursor,end);
325  if (cursor == end) {
326  //parameters[parameter_name] = attributes;
327  parameters.push_back(std::make_pair(parameter_name,attributes));
328  break;
329  }
330 
331  // If there is an attribute list, read it in
332  if (*cursor == ';') {
333  InputIterator acursor;
334 
335  ++cursor;
336  acursor = http::parser::extract_attributes(cursor,end,attributes);
337 
338  if (acursor == cursor) {
339  // attribute extraction ended in syntax error
340  return begin;
341  }
342 
343  cursor = acursor;
344  }
345 
346  // insert parameter into output list
347  //parameters[parameter_name] = attributes;
348  parameters.push_back(std::make_pair(parameter_name,attributes));
349 
350  cursor = http::parser::extract_all_lws(cursor,end);
351  if (cursor == end) {break;}
352 
353  // if next char is ',' then read another parameter, else stop
354  if (*cursor != ',') {
355  break;
356  }
357 
358  // advance past comma
359  ++cursor;
360 
361  if (cursor == end) {
362  // expected more bytes after a comma
363  return begin;
364  }
365  }
366 
367  return cursor;
368 }
369 
370 inline std::string strip_lws(std::string const & input) {
371  std::string::const_iterator begin = extract_all_lws(input.begin(),input.end());
372  if (begin == input.end()) {
373  return std::string();
374  }
375  std::string::const_reverse_iterator end = extract_all_lws(input.rbegin(),input.rend());
376 
377  return std::string(begin,end.base());
378 }
379 
381 
385 class parser {
386 public:
388 
391  std::string const & get_version() const {
392  return m_version;
393  }
394 
396 
402  void set_version(std::string const & version);
403 
405 
411  std::string const & get_header(std::string const & key) const;
412 
414 
422  bool get_header_as_plist(std::string const & key, parameter_list & out)
423  const;
424 
426 
440  void append_header(std::string const & key, std::string const & val);
441 
443 
457  void replace_header(std::string const & key, std::string const & val);
458 
460 
468  void remove_header(std::string const & key);
469 
471 
477  std::string const & get_body() const {
478  return m_body;
479  }
480 
482 
490  void set_body(std::string const & value);
491 
493 
498  bool parse_parameter_list(std::string const & in, parameter_list & out)
499  const;
500 protected:
502 
507  bool parse_headers(std::istream & s);
508 
510 
516  void process_header(std::string::iterator begin, std::string::iterator end);
517 
519 
525  std::string raw_headers() const;
526 
527  std::string m_version;
528  header_list m_headers;
529  std::string m_body;
530 };
531 
532 } // namespace parser
533 } // namespace http
534 } // namespace websocketpp
535 
536 #include <websocketpp/http/impl/parser.hpp>
537 
538 #endif // HTTP_PARSER_HPP
uint16_t value
The type of a close code value.
Definition: close.hpp:49
bool get_header_as_plist(std::string const &key, parameter_list &out) const
Extract an HTTP parameter list from a parser header.
Definition: parser.hpp:53
bool is_not_whitespace_char(unsigned char c)
Is the character non-whitespace.
Definition: constants.hpp:111
std::string const & get_body() const
Set HTTP body.
Definition: parser.hpp:477
void set_body(std::string const &value)
Set body content.
Definition: parser.hpp:89
bool parse_parameter_list(std::string const &in, parameter_list &out) const
Extract an HTTP parameter list from a string.
Definition: parser.hpp:102
std::vector< std::pair< std::string, attribute_list > > parameter_list
The type of an HTTP parameter list.
Definition: constants.hpp:51
bool is_not_token_char(unsigned char c)
Is the character a non-token.
Definition: constants.hpp:98
void process_header(std::string::iterator begin, std::string::iterator end)
Process a header line.
Definition: parser.hpp:136
bool parse_headers(std::istream &s)
Parse headers from an istream.
Definition: parser.hpp:114
std::string const & get_header(std::string const &key) const
Get the value of an HTTP header.
Definition: parser.hpp:43
std::string const & get_version() const
Get the HTTP version string.
Definition: parser.hpp:391
Namespace for the WebSocket++ project.
Definition: base64.hpp:41
void append_header(std::string const &key, std::string const &val)
Append a value to an existing HTTP header.
Definition: parser.hpp:65
std::map< std::string, std::string > attribute_list
The type of an HTTP attribute list.
Definition: constants.hpp:43
void replace_header(std::string const &key, std::string const &val)
Set a value for an HTTP header, replacing an existing value.
Definition: parser.hpp:79
std::string raw_headers() const
Generate and return the HTTP headers as a string.
Definition: parser.hpp:154
void set_version(std::string const &version)
Set HTTP parser Version.
Definition: parser.hpp:39
void remove_header(std::string const &key)
Remove a header from the parser.
Definition: parser.hpp:85
bool is_whitespace_char(unsigned char c)
Is the character whitespace.
Definition: constants.hpp:106