dlib C++ Library - server_http.cpp

// Copyright (C) 2003 Davis E. King (davis@dlib.net)
// License: Boost Software License See LICENSE.txt for the full license.
#ifndef DLIB_SERVER_HTTP_CPp_
#define DLIB_SERVER_HTTP_CPp_
#include "server_http.h"
namespace dlib
{
// ----------------------------------------------------------------------------------------
 namespace http_impl
 {
 inline unsigned char to_hex( unsigned char x ) 
 {
 return x + (x > 9 ? ('A'-10) : '0');
 }
 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;
 }
 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;
 }
 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'
 ) 
 {
 using namespace std;
 const size_t max = 64*1024;
 buffer.clear();
 buffer.reserve(300);
 while (in.peek() != delim && in.peek() != '\n' && 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)
 throw http_parse_error("HTTP field from client terminated incorrectly", 414);
 if (buffer.size() == max)
 throw http_parse_error("HTTP field from client is too long", 414);
 in.get();
 // eat any remaining whitespace
 if (delim == ' ')
 {
 while (in.peek() == ' ')
 in.get();
 }
 }
 }
// ----------------------------------------------------------------------------------------
 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_ci& 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 != "\r")
 {
 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 != "\r")
 // 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;
 }
// ----------------------------------------------------------------------------------------
 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);
 }
 }
 }
// ----------------------------------------------------------------------------------------
 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_ci& 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(key_value_map_ci::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(key_value_map_ci::const_iterator ci = response_headers.begin(); ci != response_headers.end(); ++ci )
 {
 out << ci->first << ": " << ci->second << "\r\n";
 }
 // set any cookies 
 for(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;
 }
// ----------------------------------------------------------------------------------------
 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());
 }
// ----------------------------------------------------------------------------------------
 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());
 }
// ----------------------------------------------------------------------------------------
 const logger server_http::dlog("dlib.server_http");
// ----------------------------------------------------------------------------------------
}
#endif // DLIB_SERVER_HTTP_CPp_

AltStyle によって変換されたページ (->オリジナル) /