mirror of
https://github.com/XRPLF/rippled.git
synced 2026-04-29 15:37:57 +00:00
415 lines
12 KiB
C++
415 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2011, Peter Thorson. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of the WebSocket++ Project nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#ifndef HTTP_PARSER_HPP
|
|
#define HTTP_PARSER_HPP
|
|
|
|
#include <iostream>
|
|
#include <map>
|
|
|
|
#include <websocketpp/http/constants.hpp>
|
|
|
|
namespace websocketpp {
|
|
namespace http {
|
|
namespace parser {
|
|
|
|
namespace state {
|
|
enum value {
|
|
METHOD,
|
|
RESOURCE,
|
|
VERSION,
|
|
HEADERS
|
|
};
|
|
}
|
|
|
|
typedef std::map<std::string,std::string> header_list;
|
|
|
|
/// Read until a non-token character is found and then return the token and
|
|
/// iterator to the next character to read
|
|
template <typename InputIterator>
|
|
std::pair<std::string,InputIterator> extract_token(InputIterator begin,
|
|
InputIterator end)
|
|
{
|
|
InputIterator it = std::find_if(begin,end,&is_not_token_char);
|
|
return std::make_pair(std::string(begin,it),it);
|
|
}
|
|
|
|
template <typename InputIterator>
|
|
std::pair<std::string,InputIterator> extract_quoted_string(InputIterator begin,
|
|
InputIterator end)
|
|
{
|
|
std::string s;
|
|
|
|
if (end == begin) {
|
|
return std::make_pair(s,begin);
|
|
}
|
|
|
|
if (*begin != '"') {
|
|
return std::make_pair(s,begin);
|
|
}
|
|
|
|
InputIterator cursor = begin+1;
|
|
InputIterator marker = cursor;
|
|
|
|
cursor = std::find(cursor,end,'"');
|
|
|
|
while (cursor != end) {
|
|
// either this is the end or a quoted string
|
|
if (*(cursor-1) == '\\') {
|
|
s.append(marker,cursor-1);
|
|
s.append(1,'"');
|
|
++cursor;
|
|
marker = cursor;
|
|
} else {
|
|
s.append(marker,cursor);
|
|
++cursor;
|
|
return std::make_pair(s,cursor);
|
|
}
|
|
|
|
cursor = std::find(cursor,end,'"');
|
|
}
|
|
|
|
return std::make_pair("",begin);
|
|
}
|
|
|
|
/// Read one unit of linear white space and return the iterator to the character
|
|
/// afterwards. If ret = begin no whitespace was extracted.
|
|
template <typename InputIterator>
|
|
InputIterator extract_lws(InputIterator begin, InputIterator end) {
|
|
InputIterator it = begin;
|
|
|
|
// strip leading CRLF
|
|
if (end-begin > 2 && *begin == '\r' && *(begin+1) == '\n' && is_whitespace_char(*(begin+2))) {
|
|
it+=3;
|
|
}
|
|
|
|
it = std::find_if(it,end,&is_not_whitespace_char);
|
|
return it;
|
|
}
|
|
|
|
/// SImilar to extract_lws but extracts all lws instead of just one line
|
|
template <typename InputIterator>
|
|
InputIterator extract_all_lws(InputIterator begin, InputIterator end) {
|
|
InputIterator old_it;
|
|
InputIterator new_it = begin;
|
|
|
|
do {
|
|
// Pull value from previous iteration
|
|
old_it = new_it;
|
|
|
|
// look ahead another pass
|
|
new_it = extract_lws(old_it,end);
|
|
} while (new_it != end && old_it != new_it);
|
|
|
|
return new_it;
|
|
}
|
|
|
|
/*
|
|
struct attribute {
|
|
attribute(const std::string &n, const std::string &v)
|
|
: name(n), value(v){}
|
|
|
|
std::string name;
|
|
std::string value;
|
|
};
|
|
typedef std::vector<attribute> attribute_list;
|
|
|
|
struct parameter {
|
|
parameter(std::string n) : name(n) {}
|
|
|
|
void add_attribute(const attribute& p) {
|
|
attributes.push_back(p);
|
|
}
|
|
|
|
void add_attribute(const std::string& key, const std::string & value) {
|
|
attributes.push_back(attribute(key,value));
|
|
}
|
|
|
|
std::string name;
|
|
attribute_list attributes;
|
|
};
|
|
typedef std::vector<parameter> parameter_list;
|
|
*/
|
|
|
|
typedef std::map<std::string,std::string> attribute_list;
|
|
typedef std::map<std::string,attribute_list> parameter_list;
|
|
|
|
template <typename InputIterator>
|
|
InputIterator extract_attributes(InputIterator begin, InputIterator end,
|
|
attribute_list & attributes)
|
|
{
|
|
InputIterator cursor;
|
|
bool first = true;
|
|
|
|
if (begin == end) {
|
|
return begin;
|
|
}
|
|
|
|
cursor = begin;
|
|
std::pair<std::string,InputIterator> ret;
|
|
|
|
while (cursor != end) {
|
|
std::string name;
|
|
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end) {
|
|
break;
|
|
}
|
|
|
|
if (first) {
|
|
// ignore this check for the very first pass
|
|
first = false;
|
|
} else {
|
|
if (*cursor == ';') {
|
|
// advance past the ';'
|
|
++cursor;
|
|
} else {
|
|
// non-semicolon in this position indicates end end of the
|
|
// attribute list, break and return.
|
|
break;
|
|
}
|
|
}
|
|
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
ret = http::parser::extract_token(cursor,end);
|
|
|
|
if (ret.first == "") {
|
|
// error: expected a token
|
|
return begin;
|
|
} else {
|
|
name = ret.first;
|
|
cursor = ret.second;
|
|
}
|
|
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end || *cursor != '=') {
|
|
// if there is an equals sign, read the attribute value. Otherwise
|
|
// record a blank value and continue
|
|
attributes[name] = "";
|
|
continue;
|
|
}
|
|
|
|
// advance past the '='
|
|
++cursor;
|
|
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end) {
|
|
// error: expected a token or quoted string
|
|
return begin;
|
|
}
|
|
|
|
ret = http::parser::extract_quoted_string(cursor,end);
|
|
if (ret.second != cursor) {
|
|
attributes[name] = ret.first;
|
|
cursor = ret.second;
|
|
continue;
|
|
}
|
|
|
|
ret = http::parser::extract_token(cursor,end);
|
|
if (ret.first == "") {
|
|
// error : expected token or quoted string
|
|
return begin;
|
|
} else {
|
|
attributes[name] = ret.first;
|
|
cursor = ret.second;
|
|
}
|
|
}
|
|
|
|
return cursor;
|
|
}
|
|
|
|
template <typename InputIterator>
|
|
InputIterator extract_parameters(InputIterator begin, InputIterator end,
|
|
parameter_list ¶meters)
|
|
{
|
|
InputIterator cursor;
|
|
|
|
if (begin == end) {
|
|
// error: expected non-zero length range
|
|
return begin;
|
|
}
|
|
|
|
cursor = begin;
|
|
std::pair<std::string,InputIterator> ret;
|
|
|
|
/**
|
|
* LWS
|
|
* token
|
|
* LWS
|
|
* *(";" method-param)
|
|
* LWS
|
|
* ,=loop again
|
|
*/
|
|
while (cursor != end) {
|
|
std::string parameter_name;
|
|
attribute_list attributes;
|
|
|
|
// extract any stray whitespace
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end) {break;}
|
|
|
|
ret = http::parser::extract_token(cursor,end);
|
|
|
|
if (ret.first == "") {
|
|
// error: expected a token
|
|
return begin;
|
|
} else {
|
|
parameter_name = ret.first;
|
|
cursor = ret.second;
|
|
}
|
|
|
|
// Safe break point, insert parameter with blank attributes and exit
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end) {
|
|
parameters[parameter_name] = attributes;
|
|
break;
|
|
}
|
|
|
|
// If there is an attribute list, read it in
|
|
if (*cursor == ';') {
|
|
InputIterator acursor;
|
|
|
|
++cursor;
|
|
acursor = http::parser::extract_attributes(cursor,end,attributes);
|
|
|
|
if (acursor == cursor) {
|
|
// attribute extraction ended in syntax error
|
|
return begin;
|
|
}
|
|
|
|
cursor = acursor;
|
|
}
|
|
|
|
// insert parameter into output list
|
|
parameters[parameter_name] = attributes;
|
|
|
|
cursor = http::parser::extract_all_lws(cursor,end);
|
|
if (cursor == end) {break;}
|
|
|
|
// if next char is ',' then read another parameter, else stop
|
|
if (*cursor != ',') {
|
|
break;
|
|
}
|
|
|
|
// advance past comma
|
|
++cursor;
|
|
|
|
if (cursor == end) {
|
|
// expected more bytes after a comma
|
|
return begin;
|
|
}
|
|
}
|
|
|
|
return cursor;
|
|
}
|
|
|
|
class parser {
|
|
public:
|
|
typedef http::parser::attribute_list attribute_list;
|
|
typedef http::parser::parameter_list parameter_list;
|
|
|
|
// Convenience method versions of some of the free utility functions.
|
|
bool parse_parameter_list(const std::string& in, parameter_list& out) const;
|
|
|
|
/// Set the HTTP version string
|
|
/**
|
|
* @param version HTTP version string to use. Must be in format HTTP/x.y
|
|
* where x and y are positive integers.
|
|
*/
|
|
void set_version(const std::string& version);
|
|
|
|
/// Get the HTTP version string
|
|
const std::string& get_version() const {
|
|
return m_version;
|
|
}
|
|
|
|
/// Get the HTTP header with name `key`
|
|
/**
|
|
* @param key Name of the header to return
|
|
* @return Value of the header
|
|
*/
|
|
const std::string& get_header(const std::string& key) const;
|
|
|
|
/// Get the HTTP header with name `key`
|
|
/**
|
|
*
|
|
* @param key The header name to retrieve
|
|
*
|
|
* @param out A reference to a parameter list to store any extracted
|
|
* paramters.
|
|
*
|
|
* @return True if the value of this header was not a valid parameter list
|
|
*/
|
|
bool get_header_as_plist(const std::string& key, parameter_list& out) const;
|
|
|
|
/// Append a header
|
|
/**
|
|
* If a header with this name already exists the value will be appended to
|
|
* the existing header to form a comma separated list of values. Use
|
|
* replace_header to overwrite existing values.
|
|
*
|
|
* @param key Name of the header to set
|
|
* @param val Value to add
|
|
* @see replace_header
|
|
*/
|
|
void append_header(const std::string &key,const std::string &val);
|
|
|
|
/// Replace a header
|
|
/**
|
|
* If a header with this name already exists the old value will be replaced
|
|
* Use add_header to append to a list of existing values.
|
|
*
|
|
* @param key Name of the header to set
|
|
* @param val Value to set
|
|
* @see add_header
|
|
*/
|
|
void replace_header(const std::string &key,const std::string &val);
|
|
|
|
/// Remove a header
|
|
void remove_header(const std::string &key);
|
|
protected:
|
|
/// DEPRECATED Read headers out of an istream
|
|
bool parse_headers(std::istream& s);
|
|
|
|
/// Helper function for consume. Process header line
|
|
void process_header(std::string::iterator begin, std::string::iterator end);
|
|
|
|
/// Return headers in raw string form.
|
|
std::string raw_headers() const;
|
|
private:
|
|
std::string m_version;
|
|
header_list m_headers;
|
|
};
|
|
|
|
} // namespace parser
|
|
} // namespace http
|
|
} // namespace websocketpp
|
|
|
|
#include <websocketpp/http/impl/parser.hpp>
|
|
|
|
#endif // HTTP_PARSER_HPP
|