mirror of
https://github.com/davisking/dlib.git
synced 2024-11-01 10:14:53 +08:00
Refactored the code in the http server so that it will be more reusable
by other tools.
This commit is contained in:
parent
05b1ba8bcb
commit
1db057d69c
@ -25,6 +25,472 @@
|
||||
namespace dlib
|
||||
{
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
class http_parse_error : public error
|
||||
{
|
||||
public:
|
||||
http_parse_error(const std::string& str, int http_error_code_):
|
||||
error(str),http_error_code(http_error_code_) {}
|
||||
|
||||
const int http_error_code;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class constmap : public std::map<Key, Value>
|
||||
{
|
||||
public:
|
||||
const Value& operator[](const Key& k) const
|
||||
{
|
||||
static const Value dummy = Value();
|
||||
|
||||
typename std::map<Key, Value>::const_iterator ci = std::map<Key, Value>::find(k);
|
||||
|
||||
if ( ci == this->end() )
|
||||
return dummy;
|
||||
else
|
||||
return ci->second;
|
||||
}
|
||||
|
||||
Value& operator[](const Key& k)
|
||||
{
|
||||
return std::map<Key, Value>::operator [](k);
|
||||
}
|
||||
};
|
||||
|
||||
typedef constmap< std::string, std::string > key_value_map;
|
||||
|
||||
struct incoming_things
|
||||
{
|
||||
incoming_things (
|
||||
const std::string& foreign_ip_,
|
||||
const std::string& local_ip_,
|
||||
unsigned short foreign_port_,
|
||||
unsigned short local_port_
|
||||
):
|
||||
foreign_ip(foreign_ip_),
|
||||
foreign_port(foreign_port_),
|
||||
local_ip(local_ip_),
|
||||
local_port(local_port_)
|
||||
{}
|
||||
|
||||
|
||||
std::string path;
|
||||
std::string request_type;
|
||||
std::string content_type;
|
||||
std::string protocol;
|
||||
std::string body;
|
||||
|
||||
key_value_map queries;
|
||||
key_value_map cookies;
|
||||
key_value_map headers;
|
||||
|
||||
std::string foreign_ip;
|
||||
unsigned short foreign_port;
|
||||
std::string local_ip;
|
||||
unsigned short local_port;
|
||||
};
|
||||
|
||||
struct outgoing_things
|
||||
{
|
||||
outgoing_things() : http_return(200), http_return_status("OK") { }
|
||||
|
||||
key_value_map cookies;
|
||||
key_value_map headers;
|
||||
unsigned short http_return;
|
||||
std::string http_return_status;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
namespace http_impl
|
||||
{
|
||||
inline unsigned char to_hex( unsigned char x )
|
||||
{
|
||||
return x + (x > 9 ? ('A'-10) : '0');
|
||||
}
|
||||
|
||||
inline const std::string urlencode( const std::string& s )
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
|
||||
{
|
||||
if ( (*ci >= 'a' && *ci <= 'z') ||
|
||||
(*ci >= 'A' && *ci <= 'Z') ||
|
||||
(*ci >= '0' && *ci <= '9') )
|
||||
{ // allowed
|
||||
os << *ci;
|
||||
}
|
||||
else if ( *ci == ' ')
|
||||
{
|
||||
os << '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << '%' << to_hex(*ci >> 4) << to_hex(*ci % 16);
|
||||
}
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline unsigned char from_hex (
|
||||
unsigned char ch
|
||||
)
|
||||
{
|
||||
if (ch <= '9' && ch >= '0')
|
||||
ch -= '0';
|
||||
else if (ch <= 'f' && ch >= 'a')
|
||||
ch -= 'a' - 10;
|
||||
else if (ch <= 'F' && ch >= 'A')
|
||||
ch -= 'A' - 10;
|
||||
else
|
||||
ch = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
inline const std::string urldecode (
|
||||
const std::string& str
|
||||
)
|
||||
{
|
||||
using namespace std;
|
||||
string result;
|
||||
string::size_type i;
|
||||
for (i = 0; i < str.size(); ++i)
|
||||
{
|
||||
if (str[i] == '+')
|
||||
{
|
||||
result += ' ';
|
||||
}
|
||||
else if (str[i] == '%' && str.size() > i+2)
|
||||
{
|
||||
const unsigned char ch1 = from_hex(str[i+1]);
|
||||
const unsigned char ch2 = from_hex(str[i+2]);
|
||||
const unsigned char ch = (ch1 << 4) | ch2;
|
||||
result += ch;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += str[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void parse_url(
|
||||
std::string word,
|
||||
key_value_map& queries
|
||||
)
|
||||
/*!
|
||||
Parses the query string of a URL. word should be the stuff that comes
|
||||
after the ? in the query URL.
|
||||
!*/
|
||||
{
|
||||
std::string::size_type pos;
|
||||
|
||||
for (pos = 0; pos < word.size(); ++pos)
|
||||
{
|
||||
if (word[pos] == '&')
|
||||
word[pos] = ' ';
|
||||
}
|
||||
|
||||
std::istringstream sin(word);
|
||||
sin >> word;
|
||||
while (sin)
|
||||
{
|
||||
pos = word.find_first_of("=");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string key = urldecode(word.substr(0,pos));
|
||||
std::string value = urldecode(word.substr(pos+1));
|
||||
|
||||
queries[key] = value;
|
||||
}
|
||||
sin >> word;
|
||||
}
|
||||
}
|
||||
|
||||
inline void read_with_limit(
|
||||
std::istream& in,
|
||||
std::string& buffer,
|
||||
int delim = '\n'
|
||||
)
|
||||
{
|
||||
using namespace std;
|
||||
const size_t max = 16*1024;
|
||||
buffer.clear();
|
||||
buffer.reserve(300);
|
||||
|
||||
while (in.peek() != delim && in.peek() != EOF && buffer.size() < max)
|
||||
{
|
||||
buffer += (char)in.get();
|
||||
}
|
||||
|
||||
// if we quit the loop because the data is longer than expected or we hit EOF
|
||||
if (in.peek() == EOF || buffer.size() == max)
|
||||
throw http_parse_error("HTTP field from client is too long", 414);
|
||||
|
||||
// Make sure the last char is the delim.
|
||||
if (in.get() != delim)
|
||||
{
|
||||
in.setstate(ios::badbit);
|
||||
buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the remaining delimiters
|
||||
if (delim == ' ')
|
||||
{
|
||||
while (in.peek() == ' ')
|
||||
in.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline unsigned long parse_http_request (
|
||||
std::istream& in,
|
||||
incoming_things& incoming,
|
||||
unsigned long max_content_length
|
||||
)
|
||||
{
|
||||
using namespace std;
|
||||
using namespace http_impl;
|
||||
read_with_limit(in, incoming.request_type, ' ');
|
||||
|
||||
// get the path
|
||||
read_with_limit(in, incoming.path, ' ');
|
||||
|
||||
// Get the HTTP/1.1 - Ignore for now...
|
||||
read_with_limit(in, incoming.protocol);
|
||||
|
||||
key_value_map& incoming_headers = incoming.headers;
|
||||
key_value_map& cookies = incoming.cookies;
|
||||
std::string& path = incoming.path;
|
||||
std::string& content_type = incoming.content_type;
|
||||
unsigned long content_length = 0;
|
||||
|
||||
string line;
|
||||
read_with_limit(in, line);
|
||||
string first_part_of_header;
|
||||
string::size_type position_of_double_point;
|
||||
// now loop over all the incoming_headers
|
||||
while (line.size() > 2)
|
||||
{
|
||||
position_of_double_point = line.find_first_of(':');
|
||||
if ( position_of_double_point != string::npos )
|
||||
{
|
||||
first_part_of_header = dlib::trim(line.substr(0, position_of_double_point));
|
||||
|
||||
if ( !incoming_headers[first_part_of_header].empty() )
|
||||
incoming_headers[ first_part_of_header ] += " ";
|
||||
incoming_headers[first_part_of_header] += dlib::trim(line.substr(position_of_double_point+1));
|
||||
|
||||
// look for Content-Type:
|
||||
if (line.size() > 14 && strings_equal_ignore_case(line, "Content-Type:", 13))
|
||||
{
|
||||
content_type = line.substr(14);
|
||||
if (content_type[content_type.size()-1] == '\r')
|
||||
content_type.erase(content_type.size()-1);
|
||||
}
|
||||
// look for Content-Length:
|
||||
else if (line.size() > 16 && strings_equal_ignore_case(line, "Content-Length:", 15))
|
||||
{
|
||||
istringstream sin(line.substr(16));
|
||||
sin >> content_length;
|
||||
if (!sin)
|
||||
{
|
||||
throw http_parse_error("Invalid Content-Length of '" + line.substr(16) + "'", 411);
|
||||
}
|
||||
|
||||
if (content_length > max_content_length)
|
||||
{
|
||||
std::ostringstream sout;
|
||||
sout << "Content-Length of post back is too large. It must be less than " << max_content_length;
|
||||
throw http_parse_error(sout.str(), 413);
|
||||
}
|
||||
}
|
||||
// look for any cookies
|
||||
else if (line.size() > 6 && strings_equal_ignore_case(line, "Cookie:", 7))
|
||||
{
|
||||
string::size_type pos = 6;
|
||||
string key, value;
|
||||
bool seen_key_start = false;
|
||||
bool seen_equal_sign = false;
|
||||
while (pos + 1 < line.size())
|
||||
{
|
||||
++pos;
|
||||
// ignore whitespace between cookies
|
||||
if (!seen_key_start && line[pos] == ' ')
|
||||
continue;
|
||||
|
||||
seen_key_start = true;
|
||||
if (!seen_equal_sign)
|
||||
{
|
||||
if (line[pos] == '=')
|
||||
{
|
||||
seen_equal_sign = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
key += line[pos];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line[pos] == ';')
|
||||
{
|
||||
cookies[urldecode(key)] = urldecode(value);
|
||||
seen_equal_sign = false;
|
||||
seen_key_start = false;
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
value += line[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key.size() > 0)
|
||||
{
|
||||
cookies[urldecode(key)] = urldecode(value);
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
}
|
||||
} // no ':' in it!
|
||||
read_with_limit(in, line);
|
||||
} // while (line.size() > 2 )
|
||||
|
||||
|
||||
// If there is data being posted back to us as a query string then
|
||||
// pick out the queries using parse_url.
|
||||
if ((strings_equal_ignore_case(incoming.request_type, "POST") ||
|
||||
strings_equal_ignore_case(incoming.request_type, "PUT")) &&
|
||||
strings_equal_ignore_case(left_substr(content_type,";"), "application/x-www-form-urlencoded"))
|
||||
{
|
||||
if (content_length > 0)
|
||||
{
|
||||
incoming.body.resize(content_length);
|
||||
in.read(&incoming.body[0],content_length);
|
||||
}
|
||||
parse_url(incoming.body, incoming.queries);
|
||||
}
|
||||
|
||||
string::size_type pos = path.find_first_of("?");
|
||||
if (pos != string::npos)
|
||||
{
|
||||
parse_url(path.substr(pos+1), incoming.queries);
|
||||
}
|
||||
|
||||
|
||||
if (!in)
|
||||
throw http_parse_error("Error parsing HTTP request", 500);
|
||||
|
||||
return content_length;
|
||||
}
|
||||
|
||||
inline void read_body (
|
||||
std::istream& in,
|
||||
incoming_things& incoming
|
||||
)
|
||||
{
|
||||
// if the body hasn't already been loaded and there is data to load
|
||||
if (incoming.body.size() == 0 &&
|
||||
incoming.headers.count("Content-Length") != 0)
|
||||
{
|
||||
const unsigned long content_length = string_cast<unsigned long>(incoming.headers["Content-Length"]);
|
||||
|
||||
incoming.body.resize(content_length);
|
||||
if (content_length > 0)
|
||||
{
|
||||
in.read(&incoming.body[0],content_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void write_http_response (
|
||||
std::ostream& out,
|
||||
outgoing_things outgoing,
|
||||
const std::string& result
|
||||
)
|
||||
{
|
||||
using namespace http_impl;
|
||||
key_value_map& new_cookies = outgoing.cookies;
|
||||
key_value_map& response_headers = outgoing.headers;
|
||||
|
||||
// only send this header if the user hasn't told us to send another kind
|
||||
bool has_content_type = false, has_location = false;
|
||||
for( typename key_value_map::const_iterator ci = response_headers.begin(); ci != response_headers.end(); ++ci )
|
||||
{
|
||||
if ( !has_content_type && strings_equal_ignore_case(ci->first , "content-type") )
|
||||
{
|
||||
has_content_type = true;
|
||||
}
|
||||
else if ( !has_location && strings_equal_ignore_case(ci->first , "location") )
|
||||
{
|
||||
has_location = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( has_location )
|
||||
{
|
||||
outgoing.http_return = 302;
|
||||
}
|
||||
|
||||
if ( !has_content_type )
|
||||
{
|
||||
response_headers["Content-Type"] = "text/html";
|
||||
}
|
||||
|
||||
response_headers["Content-Length"] = cast_to_string(result.size());
|
||||
|
||||
out << "HTTP/1.0 " << outgoing.http_return << " " << outgoing.http_return_status << "\r\n";
|
||||
|
||||
// Set any new headers
|
||||
for( typename key_value_map::const_iterator ci = response_headers.begin(); ci != response_headers.end(); ++ci )
|
||||
{
|
||||
out << ci->first << ": " << ci->second << "\r\n";
|
||||
}
|
||||
|
||||
// set any cookies
|
||||
for( typename key_value_map::const_iterator ci = new_cookies.begin(); ci != new_cookies.end(); ++ci )
|
||||
{
|
||||
out << "Set-Cookie: " << urlencode(ci->first) << '=' << urlencode(ci->second) << "\r\n";
|
||||
}
|
||||
out << "\r\n" << result;
|
||||
}
|
||||
|
||||
inline void write_http_response (
|
||||
std::ostream& out,
|
||||
const http_parse_error& e
|
||||
)
|
||||
{
|
||||
outgoing_things outgoing;
|
||||
outgoing.http_return = e.http_error_code;
|
||||
outgoing.http_return_status = e.what();
|
||||
write_http_response(out, outgoing, std::string("Error processing request: ") + e.what());
|
||||
}
|
||||
|
||||
inline void write_http_response (
|
||||
std::ostream& out,
|
||||
const std::exception& e
|
||||
)
|
||||
{
|
||||
outgoing_things outgoing;
|
||||
outgoing.http_return = 500;
|
||||
outgoing.http_return_status = e.what();
|
||||
write_http_response(out, outgoing, std::string("Error processing request: ") + e.what());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
template <
|
||||
typename server_base
|
||||
>
|
||||
@ -54,61 +520,6 @@ namespace dlib
|
||||
max_content_length = max_length;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class constmap : public std::map<Key, Value>
|
||||
{
|
||||
public:
|
||||
const Value& operator[](const Key& k) const
|
||||
{
|
||||
static const Value dummy = Value();
|
||||
|
||||
typename std::map<Key, Value>::const_iterator ci = std::map<Key, Value>::find(k);
|
||||
|
||||
if ( ci == this->end() )
|
||||
return dummy;
|
||||
else
|
||||
return ci->second;
|
||||
}
|
||||
|
||||
Value& operator[](const Key& k)
|
||||
{
|
||||
return std::map<Key, Value>::operator [](k);
|
||||
}
|
||||
};
|
||||
|
||||
typedef constmap< std::string, std::string > key_value_map;
|
||||
|
||||
|
||||
struct incoming_things
|
||||
{
|
||||
incoming_things() : foreign_port(0), local_port(0) {}
|
||||
|
||||
std::string path;
|
||||
std::string request_type;
|
||||
std::string content_type;
|
||||
std::string protocol;
|
||||
std::string body;
|
||||
|
||||
key_value_map queries;
|
||||
key_value_map cookies;
|
||||
key_value_map headers;
|
||||
|
||||
std::string foreign_ip;
|
||||
unsigned short foreign_port;
|
||||
std::string local_ip;
|
||||
unsigned short local_port;
|
||||
};
|
||||
|
||||
struct outgoing_things
|
||||
{
|
||||
outgoing_things() : http_return(200) { }
|
||||
|
||||
key_value_map cookies;
|
||||
key_value_map headers;
|
||||
unsigned short http_return;
|
||||
std::string http_return_status;
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
virtual const std::string on_request (
|
||||
@ -116,142 +527,6 @@ namespace dlib
|
||||
outgoing_things& outgoing
|
||||
) = 0;
|
||||
|
||||
unsigned char to_hex( unsigned char x ) const
|
||||
{
|
||||
return x + (x > 9 ? ('A'-10) : '0');
|
||||
}
|
||||
|
||||
const std::string urlencode( const std::string& s ) const
|
||||
{
|
||||
std::ostringstream os;
|
||||
|
||||
for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
|
||||
{
|
||||
if ( (*ci >= 'a' && *ci <= 'z') ||
|
||||
(*ci >= 'A' && *ci <= 'Z') ||
|
||||
(*ci >= '0' && *ci <= '9') )
|
||||
{ // allowed
|
||||
os << *ci;
|
||||
}
|
||||
else if ( *ci == ' ')
|
||||
{
|
||||
os << '+';
|
||||
}
|
||||
else
|
||||
{
|
||||
os << '%' << to_hex(*ci >> 4) << to_hex(*ci % 16);
|
||||
}
|
||||
}
|
||||
|
||||
return os.str();
|
||||
}
|
||||
|
||||
unsigned char from_hex (
|
||||
unsigned char ch
|
||||
) const
|
||||
{
|
||||
if (ch <= '9' && ch >= '0')
|
||||
ch -= '0';
|
||||
else if (ch <= 'f' && ch >= 'a')
|
||||
ch -= 'a' - 10;
|
||||
else if (ch <= 'F' && ch >= 'A')
|
||||
ch -= 'A' - 10;
|
||||
else
|
||||
ch = 0;
|
||||
return ch;
|
||||
}
|
||||
|
||||
const std::string urldecode (
|
||||
const std::string& str
|
||||
) const
|
||||
{
|
||||
using namespace std;
|
||||
string result;
|
||||
string::size_type i;
|
||||
for (i = 0; i < str.size(); ++i)
|
||||
{
|
||||
if (str[i] == '+')
|
||||
{
|
||||
result += ' ';
|
||||
}
|
||||
else if (str[i] == '%' && str.size() > i+2)
|
||||
{
|
||||
const unsigned char ch1 = from_hex(str[i+1]);
|
||||
const unsigned char ch2 = from_hex(str[i+2]);
|
||||
const unsigned char ch = (ch1 << 4) | ch2;
|
||||
result += ch;
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += str[i];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void parse_url(std::string word, key_value_map& queries)
|
||||
/*!
|
||||
Parses the query string of a URL. word should be the stuff that comes
|
||||
after the ? in the query URL.
|
||||
!*/
|
||||
{
|
||||
std::string::size_type pos;
|
||||
|
||||
for (pos = 0; pos < word.size(); ++pos)
|
||||
{
|
||||
if (word[pos] == '&')
|
||||
word[pos] = ' ';
|
||||
}
|
||||
|
||||
std::istringstream sin(word);
|
||||
sin >> word;
|
||||
while (sin)
|
||||
{
|
||||
pos = word.find_first_of("=");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string key = urldecode(word.substr(0,pos));
|
||||
std::string value = urldecode(word.substr(pos+1));
|
||||
|
||||
queries[key] = value;
|
||||
}
|
||||
sin >> word;
|
||||
}
|
||||
}
|
||||
|
||||
void read_with_limit(
|
||||
std::istream& in,
|
||||
std::string& buffer,
|
||||
int delim = '\n'
|
||||
) const
|
||||
{
|
||||
using namespace std;
|
||||
const size_t max = 16*1024;
|
||||
buffer.clear();
|
||||
buffer.reserve(300);
|
||||
|
||||
while (in.peek() != delim && in.peek() != EOF && buffer.size() < max)
|
||||
{
|
||||
buffer += (char)in.get();
|
||||
}
|
||||
|
||||
// Make sure the last char is the delim.
|
||||
if (in.get() != delim)
|
||||
{
|
||||
in.setstate(ios::badbit);
|
||||
buffer.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Read the remaining delimiters
|
||||
if (delim == ' ')
|
||||
{
|
||||
while (in.peek() == ' ')
|
||||
in.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void on_connect (
|
||||
std::istream& in,
|
||||
@ -263,227 +538,26 @@ namespace dlib
|
||||
uint64
|
||||
)
|
||||
{
|
||||
bool my_fault = true;
|
||||
|
||||
using namespace std;
|
||||
|
||||
try
|
||||
{
|
||||
incoming_things incoming;
|
||||
incoming_things incoming(foreign_ip, local_ip, foreign_port, local_port);
|
||||
outgoing_things outgoing;
|
||||
|
||||
incoming.foreign_ip = foreign_ip;
|
||||
incoming.foreign_port = foreign_port;
|
||||
incoming.local_ip = local_ip;
|
||||
incoming.local_port = local_port;
|
||||
|
||||
read_with_limit(in, incoming.request_type, ' ');
|
||||
|
||||
// get the path
|
||||
read_with_limit(in, incoming.path, ' ');
|
||||
|
||||
// Get the HTTP/1.1 - Ignore for now...
|
||||
read_with_limit(in, incoming.protocol);
|
||||
|
||||
key_value_map& incoming_headers = incoming.headers;
|
||||
key_value_map& cookies = incoming.cookies;
|
||||
std::string& path = incoming.path;
|
||||
std::string& content_type = incoming.content_type;
|
||||
unsigned long content_length = 0;
|
||||
|
||||
string line;
|
||||
read_with_limit(in, line);
|
||||
string first_part_of_header;
|
||||
string::size_type position_of_double_point;
|
||||
// now loop over all the incoming_headers
|
||||
while (line.size() > 2)
|
||||
{
|
||||
position_of_double_point = line.find_first_of(':');
|
||||
if ( position_of_double_point != string::npos )
|
||||
{
|
||||
first_part_of_header = dlib::trim(line.substr(0, position_of_double_point));
|
||||
|
||||
if ( !incoming_headers[first_part_of_header].empty() )
|
||||
incoming_headers[ first_part_of_header ] += " ";
|
||||
incoming_headers[first_part_of_header] += dlib::trim(line.substr(position_of_double_point+1));
|
||||
|
||||
// look for Content-Type:
|
||||
if (line.size() > 14 && strings_equal_ignore_case(line, "Content-Type:", 13))
|
||||
{
|
||||
content_type = line.substr(14);
|
||||
if (content_type[content_type.size()-1] == '\r')
|
||||
content_type.erase(content_type.size()-1);
|
||||
}
|
||||
// look for Content-Length:
|
||||
else if (line.size() > 16 && strings_equal_ignore_case(line, "Content-Length:", 15))
|
||||
{
|
||||
istringstream sin(line.substr(16));
|
||||
sin >> content_length;
|
||||
if (!sin)
|
||||
content_length = 0;
|
||||
}
|
||||
// look for any cookies
|
||||
else if (line.size() > 6 && strings_equal_ignore_case(line, "Cookie:", 7))
|
||||
{
|
||||
string::size_type pos = 6;
|
||||
string key, value;
|
||||
bool seen_key_start = false;
|
||||
bool seen_equal_sign = false;
|
||||
while (pos + 1 < line.size())
|
||||
{
|
||||
++pos;
|
||||
// ignore whitespace between cookies
|
||||
if (!seen_key_start && line[pos] == ' ')
|
||||
continue;
|
||||
|
||||
seen_key_start = true;
|
||||
if (!seen_equal_sign)
|
||||
{
|
||||
if (line[pos] == '=')
|
||||
{
|
||||
seen_equal_sign = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
key += line[pos];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (line[pos] == ';')
|
||||
{
|
||||
cookies[urldecode(key)] = urldecode(value);
|
||||
seen_equal_sign = false;
|
||||
seen_key_start = false;
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
value += line[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (key.size() > 0)
|
||||
{
|
||||
cookies[urldecode(key)] = urldecode(value);
|
||||
key.clear();
|
||||
value.clear();
|
||||
}
|
||||
}
|
||||
} // no ':' in it!
|
||||
read_with_limit(in, line);
|
||||
} // while (line.size() > 2 )
|
||||
|
||||
// If there is data being posted back to us then load it into the incoming.body
|
||||
// string.
|
||||
if (content_length > max_content_length)
|
||||
{
|
||||
dlog << LERROR << "Request from: " << foreign_ip << " - body content length " << content_length << " exceeded max content length of " << max_content_length;
|
||||
in.setstate(ios::badbit);
|
||||
}
|
||||
else if ( content_length > 0)
|
||||
{
|
||||
incoming.body.resize(content_length);
|
||||
in.read(&incoming.body[0],content_length);
|
||||
}
|
||||
|
||||
// If there is data being posted back to us as a query string then
|
||||
// pick out the queries using parse_url.
|
||||
if ((strings_equal_ignore_case(incoming.request_type, "POST") ||
|
||||
strings_equal_ignore_case(incoming.request_type, "PUT")) &&
|
||||
strings_equal_ignore_case(left_substr(content_type,";"), "application/x-www-form-urlencoded"))
|
||||
{
|
||||
parse_url(incoming.body, incoming.queries);
|
||||
}
|
||||
|
||||
string::size_type pos = path.find_first_of("?");
|
||||
if (pos != string::npos)
|
||||
{
|
||||
parse_url(path.substr(pos+1), incoming.queries);
|
||||
}
|
||||
|
||||
|
||||
my_fault = false;
|
||||
key_value_map& new_cookies = outgoing.cookies;
|
||||
key_value_map& response_headers = outgoing.headers;
|
||||
|
||||
// Set some defaults
|
||||
outgoing.http_return = 200;
|
||||
outgoing.http_return_status = "OK";
|
||||
|
||||
// if there wasn't a problem with the input stream at some point
|
||||
// then lets trigger this request callback.
|
||||
std::string result;
|
||||
if (in)
|
||||
{
|
||||
result = on_request(incoming, outgoing);
|
||||
}
|
||||
else
|
||||
{
|
||||
dlog << LERROR << "Request from: " << foreign_ip << " - Invalid request - Request Entity Too Large";
|
||||
outgoing.http_return = 413;
|
||||
outgoing.http_return_status = "Request Entity Too Large";
|
||||
}
|
||||
my_fault = true;
|
||||
|
||||
// only send this header if the user hasn't told us to send another kind
|
||||
bool has_content_type(false),
|
||||
has_location(false);
|
||||
for( typename key_value_map::const_iterator ci = response_headers.begin(); ci != response_headers.end(); ++ci )
|
||||
{
|
||||
if ( !has_content_type && strings_equal_ignore_case(ci->first , "content-type") )
|
||||
{
|
||||
has_content_type = true;
|
||||
}
|
||||
else if ( !has_location && strings_equal_ignore_case(ci->first , "location") )
|
||||
{
|
||||
has_location = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( has_location )
|
||||
{
|
||||
outgoing.http_return = 302;
|
||||
}
|
||||
|
||||
if ( !has_content_type )
|
||||
{
|
||||
response_headers["Content-Type"] = "text/html";
|
||||
}
|
||||
|
||||
{
|
||||
ostringstream os;
|
||||
os << result.size();
|
||||
|
||||
response_headers["Content-Length"] = os.str();
|
||||
}
|
||||
|
||||
out << "HTTP/1.0 " << outgoing.http_return << " " << outgoing.http_return_status << "\r\n";
|
||||
|
||||
// Set any new headers
|
||||
for( typename key_value_map::const_iterator ci = response_headers.begin(); ci != response_headers.end(); ++ci )
|
||||
{
|
||||
out << ci->first << ": " << ci->second << "\r\n";
|
||||
}
|
||||
|
||||
// set any cookies
|
||||
for( typename key_value_map::const_iterator ci = new_cookies.begin(); ci != new_cookies.end(); ++ci )
|
||||
{
|
||||
out << "Set-Cookie: " << urlencode(ci->first) << '=' << urlencode(ci->second) << "\r\n";
|
||||
}
|
||||
out << "\r\n" << result;
|
||||
parse_http_request(in, incoming, max_content_length);
|
||||
read_body(in, incoming);
|
||||
const std::string& result = on_request(incoming, outgoing);
|
||||
write_http_response(out, outgoing, result);
|
||||
}
|
||||
catch (std::bad_alloc&)
|
||||
catch (http_parse_error& e)
|
||||
{
|
||||
dlog << LERROR << "We ran out of memory in server_http::on_connect()";
|
||||
// If this is an escaped exception from on_request then let it fly!
|
||||
// Seriously though, this way it is obvious to the user that something bad happened
|
||||
// since they probably won't have the dlib logger enabled.
|
||||
if (!my_fault)
|
||||
throw;
|
||||
dlog << LERROR << "Error processing request from: " << foreign_ip << " - " << e.what();
|
||||
write_http_response(out, e);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
dlog << LERROR << "Error processing request from: " << foreign_ip << " - " << e.what();
|
||||
write_http_response(out, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsigned long max_content_length;
|
||||
@ -498,6 +572,3 @@ namespace dlib
|
||||
|
||||
#endif // DLIB_SERVER_HTTp_1_
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -193,7 +193,8 @@ namespace dlib
|
||||
- outgoing.http_return and outgoing.http_return_status may be set to override the
|
||||
default HTTP return code of 200 OK
|
||||
throws
|
||||
- does not throw any exceptions
|
||||
- throws only exceptions derived from std::exception. If an exception is thrown
|
||||
then the error string from the exception is returned to the web browser.
|
||||
!*/
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user