summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/server
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/server')
-rw-r--r--ml/dlib/dlib/server/server_http.cpp409
-rw-r--r--ml/dlib/dlib/server/server_http.h242
-rw-r--r--ml/dlib/dlib/server/server_http_abstract.h390
-rw-r--r--ml/dlib/dlib/server/server_iostream.cpp14
-rw-r--r--ml/dlib/dlib/server/server_iostream.h155
-rw-r--r--ml/dlib/dlib/server/server_iostream_abstract.h84
-rw-r--r--ml/dlib/dlib/server/server_kernel.cpp595
-rw-r--r--ml/dlib/dlib/server/server_kernel.h234
-rw-r--r--ml/dlib/dlib/server/server_kernel_abstract.h310
9 files changed, 2433 insertions, 0 deletions
diff --git a/ml/dlib/dlib/server/server_http.cpp b/ml/dlib/dlib/server/server_http.cpp
new file mode 100644
index 000000000..9e3051a43
--- /dev/null
+++ b/ml/dlib/dlib/server/server_http.cpp
@@ -0,0 +1,409 @@
+// 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_
+
diff --git a/ml/dlib/dlib/server/server_http.h b/ml/dlib/dlib/server/server_http.h
new file mode 100644
index 000000000..4e95f679f
--- /dev/null
+++ b/ml/dlib/dlib/server/server_http.h
@@ -0,0 +1,242 @@
+// Copyright (C) 2006 Davis E. King (davis@dlib.net), Steven Van Ingelgem
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_SERVER_HTTp_1_
+#define DLIB_SERVER_HTTp_1_
+
+
+#include "server_http_abstract.h"
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <cctype>
+#include <map>
+#include "../logger.h"
+#include "../string.h"
+#include "server_iostream.h"
+
+#ifdef __INTEL_COMPILER
+// ignore the bogus warning about hiding on_connect()
+#pragma warning (disable: 1125)
+#endif
+
+#if _MSC_VER
+# pragma warning( disable: 4503 )
+#endif // _MSC_VER
+
+
+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, typename Comparer = std::less<Key> >
+ class constmap : public std::map<Key, Value, Comparer>
+ {
+ public:
+ const Value& operator[](const Key& k) const
+ {
+ static const Value dummy = Value();
+
+ typename std::map<Key, Value, Comparer>::const_iterator ci = std::map<Key, Value, Comparer>::find(k);
+
+ if ( ci == this->end() )
+ return dummy;
+ else
+ return ci->second;
+ }
+
+ Value& operator[](const Key& k)
+ {
+ return std::map<Key, Value, Comparer>::operator [](k);
+ }
+ };
+
+
+ class less_case_insensitive
+ {
+ public:
+ bool operator()(const std::string& a, const std::string& b) const
+ {
+ unsigned long i = 0;
+ while (i < a.size() && i < b.size())
+ {
+ const int cha = std::tolower(a[i]);
+ const int chb = std::tolower(b[i]);
+ if (cha < chb)
+ return true;
+ else if (cha > chb)
+ return false;
+ ++i;
+ }
+ if (a.size() < b.size())
+ return true;
+ else
+ return false;
+ }
+ };
+ typedef constmap< std::string, std::string, less_case_insensitive > key_value_map_ci;
+ 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_ci 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_ci headers;
+ unsigned short http_return;
+ std::string http_return_status;
+ };
+
+// ----------------------------------------------------------------------------------------
+
+ unsigned long parse_http_request (
+ std::istream& in,
+ incoming_things& incoming,
+ unsigned long max_content_length
+ );
+
+ void read_body (
+ std::istream& in,
+ incoming_things& incoming
+ );
+
+ void write_http_response (
+ std::ostream& out,
+ outgoing_things outgoing,
+ const std::string& result
+ );
+
+ void write_http_response (
+ std::ostream& out,
+ const http_parse_error& e
+ );
+
+ void write_http_response (
+ std::ostream& out,
+ const std::exception& e
+ );
+
+// ----------------------------------------------------------------------------------------
+
+ class server_http : public server_iostream
+ {
+
+ public:
+
+ server_http()
+ {
+ max_content_length = 10*1024*1024; // 10MB
+ }
+
+ unsigned long get_max_content_length (
+ ) const
+ {
+ auto_mutex lock(http_class_mutex);
+ return max_content_length;
+ }
+
+ void set_max_content_length (
+ unsigned long max_length
+ )
+ {
+ auto_mutex lock(http_class_mutex);
+ max_content_length = max_length;
+ }
+
+
+ private:
+ virtual const std::string on_request (
+ const incoming_things& incoming,
+ outgoing_things& outgoing
+ ) = 0;
+
+
+ virtual void on_connect (
+ std::istream& in,
+ std::ostream& out,
+ const std::string& foreign_ip,
+ const std::string& local_ip,
+ unsigned short foreign_port,
+ unsigned short local_port,
+ uint64
+ )
+ {
+ try
+ {
+ incoming_things incoming(foreign_ip, local_ip, foreign_port, local_port);
+ outgoing_things outgoing;
+
+ parse_http_request(in, incoming, get_max_content_length());
+ read_body(in, incoming);
+ const std::string& result = on_request(incoming, outgoing);
+ write_http_response(out, outgoing, result);
+ }
+ catch (http_parse_error& e)
+ {
+ 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);
+ }
+ }
+
+ mutex http_class_mutex;
+ unsigned long max_content_length;
+ const static logger dlog;
+ };
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#ifdef NO_MAKEFILE
+#include "server_http.cpp"
+#endif
+
+#endif // DLIB_SERVER_HTTp_1_
+
diff --git a/ml/dlib/dlib/server/server_http_abstract.h b/ml/dlib/dlib/server/server_http_abstract.h
new file mode 100644
index 000000000..0bebfb61d
--- /dev/null
+++ b/ml/dlib/dlib/server/server_http_abstract.h
@@ -0,0 +1,390 @@
+// Copyright (C) 2006 Davis E. King (davis@dlib.net), Steven Van Ingelgem
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_SERVER_HTTp_ABSTRACT_
+#ifdef DLIB_SERVER_HTTp_ABSTRACT_
+
+#include "server_iostream_abstract.h"
+#include <iostream>
+#include <string>
+#include <map>
+
+namespace dlib
+{
+
+// -----------------------------------------------------------------------------------------
+
+ template <
+ typename Key,
+ typename Value,
+ typename Comparer = std::less<Key>
+ >
+ class constmap : public std::map<Key, Value, Comparer>
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is simply an extension to the std::map that allows you
+ to use the operator[] accessor with a constant map.
+ !*/
+ public:
+
+ const Value& operator[](
+ const Key& k
+ ) const;
+ /*!
+ ensures
+ - if (this->find(k) != this->end()) then
+ - This map contains the given key
+ - return the value associated with the given key
+ - else
+ - return a default initialized Value object
+ !*/
+
+ Value& operator[](
+ const Key& k
+ ) { return std::map<Key, Value>::operator [](k); }
+ /*!
+ ensures
+ - This function does the same thing as the normal std::map operator[]
+ function.
+ - if (this->find(k) != this->end()) then
+ - This map contains the given key
+ - return the value associated with the given key
+ - else
+ - Adds a new entry into the map that is associated with the
+ given key. The new entry will be default initialized and
+ this function returns a reference to it.
+ !*/
+ };
+
+ typedef constmap<std::string, std::string> key_value_map;
+ // This version of key_value_map treats the key string as being case-insensitive.
+ // For example, a key string of "Content-Type" would access the same element as a key
+ // of "content-type".
+ typedef constmap<std::string, std::string, less_case_insensitive> key_value_map_ci;
+
+// -----------------------------------------------------------------------------------------
+
+ struct incoming_things
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object contains all the various bits of information that describe a
+ HTTP request that comes into a web server.
+
+ For a detailed discussion of the fields of this object, see the
+ server_http::on_request() method defined later in this file.
+ !*/
+
+ incoming_things (
+ const std::string& foreign_ip_,
+ const std::string& local_ip_,
+ unsigned short foreign_port_,
+ unsigned short local_port_
+ );
+ /*!
+ ensures
+ - #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_ci headers;
+
+ std::string foreign_ip;
+ unsigned short foreign_port;
+ std::string local_ip;
+ unsigned short local_port;
+ };
+
+ struct outgoing_things
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This object contains all the various bits of information that describe a
+ HTTP response from a web server.
+
+ For a detailed discussion of the fields of this object, see the
+ server_http::on_request() method defined later in this file.
+ !*/
+
+ outgoing_things(
+ );
+ /*!
+ ensures
+ - #http_return == 200
+ - #http_return_status == "OK"
+ !*/
+
+ key_value_map cookies;
+ key_value_map_ci headers;
+ unsigned short http_return;
+ std::string http_return_status;
+ };
+
+// -----------------------------------------------------------------------------------------
+
+ class http_parse_error : public error
+ {
+ /*!
+ WHAT THIS OBJECT REPRESENTS
+ This is an exception thrown by the parse_http_request() routine if
+ there is a problem.
+ !*/
+ };
+
+// -----------------------------------------------------------------------------------------
+
+ unsigned long parse_http_request (
+ std::istream& in,
+ incoming_things& incoming,
+ unsigned long max_content_length
+ );
+ /*!
+ ensures
+ - Attempts to read a HTTP GET, POST, or PUT request from the given input
+ stream.
+ - Reads all headers of the request and puts them into #incoming. In particular,
+ this function populates the following fields:
+ - #incoming.path
+ - #incoming.request_type
+ - #incoming.content_type
+ - #incoming.protocol
+ - #incoming.queries
+ - #incoming.cookies
+ - #incoming.headers
+ - This function also populates the #incoming.body field if and only if the
+ Content-Type field is equal to "application/x-www-form-urlencoded".
+ Otherwise, the content is not read from the stream.
+ throws
+ - http_parse_error
+ This exception is thrown if the Content-Length coming from the web
+ browser is greater than max_content_length or if any other problem
+ is detected with the request.
+ !*/
+
+ void read_body (
+ std::istream& in,
+ incoming_things& incoming
+ );
+ /*!
+ requires
+ - parse_http_request(in,incoming,max_content_length) has already been called
+ and therefore populated the fields of incoming.
+ ensures
+ - if (incoming.body has already been populated with the content of an HTTP
+ request) then
+ - this function does nothing
+ - else
+ - reads the body of the HTTP request into #incoming.body.
+ !*/
+
+ void write_http_response (
+ std::ostream& out,
+ outgoing_things outgoing,
+ const std::string& result
+ );
+ /*!
+ ensures
+ - Writes an HTTP response, defined by the data in outgoing, to the given output
+ stream.
+ - The result variable is written out as the content of the response.
+ !*/
+
+ void write_http_response (
+ std::ostream& out,
+ const http_parse_error& e
+ );
+ /*!
+ ensures
+ - Writes an HTTP error response based on the information in the exception
+ object e.
+ !*/
+
+ void write_http_response (
+ std::ostream& out,
+ const std::exception& e
+ );
+ /*!
+ ensures
+ - Writes an HTTP error response based on the information in the exception
+ object e.
+ !*/
+
+// -----------------------------------------------------------------------------------------
+// -----------------------------------------------------------------------------------------
+
+ class server_http : public server_iostream
+ {
+
+ /*!
+ WHAT THIS EXTENSION DOES FOR server_iostream
+ This extension turns the server object into a simple HTTP server. It only
+ handles HTTP GET, PUT and POST requests and each incoming request triggers
+ the on_request() callback.
+
+ COOKIE STRINGS
+ The strings returned in the cookies key_value_map should be of the following form:
+ key: cookie_name
+ value: cookie contents; expires=Fri, 31-Dec-2010 23:59:59 GMT; path=/; domain=.example.net
+
+ You don't have to supply all the extra cookie arguments. So if you just want to
+ set a cookie that will expire when the client's browser is closed you can
+ just say something like incoming.cookies["cookie_name"] = "cookie contents";
+
+ HTTP HEADERS
+ The HTTP headers in the incoming.headers and outgoing.headers are the name/value pairs
+ of HTTP headers. For example, the HTTP header "Content-Type: text/html" would be
+ encoded such that outgoing.headers["Content-Type"] == "text/html".
+
+ Also note that if you wish to change the content type of your response to the
+ client you may do so by setting the "Content-Type" header to whatever you like.
+ However, setting this field manually is not necessary as it will default to
+ "text/html" if you don't explicitly set it to something.
+ !*/
+
+ public:
+
+ server_http (
+ );
+ /*!
+ ensures
+ - #get_max_content_length() == 10*1024*1024
+ !*/
+
+ unsigned long get_max_content_length (
+ ) const;
+ /*!
+ ensures
+ - returns the max allowable content length, in bytes, of the post back to
+ the web server. If a client attempts to send more data than this then an
+ error number 413 is returned back to the client and the request is not
+ processed by the web server.
+ !*/
+
+ void set_max_content_length (
+ unsigned long max_length
+ );
+ /*!
+ ensures
+ - #get_max_content_length() == max_length
+ !*/
+
+ private:
+
+ virtual const std::string on_request (
+ const incoming_things& incoming,
+ outgoing_things& outgoing
+ ) = 0;
+ /*!
+ requires
+ - on_request() is called when there is an HTTP GET or POST request to be serviced
+ - on_request() is run in its own thread
+ - is_running() == true
+ - the number of current on_request() functions running < get_max_connection()
+ - in incoming:
+ - incoming.path == the path being requested by this request
+ - incoming.request_type == the type of request, GET or POST
+ - incoming.content_type == the content type associated with this request
+ - incoming.protocol == The protocol being used by the web browser (e.g. HTTP/1.1)
+ - incoming.body == a string that contains the data that was posted back to the
+ web server by the client (e.g. The string has the length specified by the
+ Content-Length header).
+ - incoming.body.size() < get_max_content_length()
+ - incoming.queries == a map that contains all the key/value pairs in the query
+ string of this request. The key and value strings of the query string will
+ have been decoded back into their original form before being sent to this
+ function (i.e. '+' decoded back to ' ' and "%hh" into its corresponding
+ ascii value. So the URL-encoding is decoded automatically)
+ - incoming.cookies == The set of cookies that came from the client along with
+ this request. The cookies will have been decoded back to normal form
+ from the URL-encoding.
+ - incoming.headers == a map that contains all the incoming HTTP headers
+ from the client web browser.
+ - incoming.foreign_ip == the foreign ip address for this request
+ - incoming.foreign_port == the foreign port number for this request
+ - incoming.local_ip == the IP of the local interface this request is coming in on
+ - incoming.local_port == the local port number this request is coming in on
+ - in outgoing:
+ - outgoing.cookies.size() == 0
+ - outgoing.headers.size() == 0
+ - outgoing.http_return == 200
+ - outgoing.http_return_status == "OK"
+ ensures
+ - This function returns the HTML page to be displayed as the response to this request.
+ - this function will not call clear()
+ - #outgoing.cookies == a set of new cookies to pass back to the client along
+ with the result of this request. (Note that URL-encoding is automatically applied
+ so you don't have to do it yourself)
+ - #outgoing.headers == a set of additional headers you wish to appear in the
+ HTTP response to this request. (This may be empty, the minimum needed headers
+ will be added automatically if you don't set them)
+ - outgoing.http_return and outgoing.http_return_status may be set to override the
+ default HTTP return code of 200 OK
+ throws
+ - 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.
+ !*/
+
+
+ // -----------------------------------------------------------------------
+ // Implementation Notes
+ // -----------------------------------------------------------------------
+
+ virtual void on_connect (
+ std::istream& in,
+ std::ostream& out,
+ const std::string& foreign_ip,
+ const std::string& local_ip,
+ unsigned short foreign_port,
+ unsigned short local_port,
+ uint64
+ )
+ /*!
+ on_connect() is the function defined by server_iostream which is overloaded by
+ server_http. In particular, the server_http's implementation is shown below.
+ In it you can see how the server_http parses the incoming http request, gets a
+ response by calling on_request(), and sends it back using the helper routines
+ defined at the top of this file.
+
+ Therefore, if you want to modify the behavior of the HTTP server, for example,
+ to do some more complex data streaming requiring direct access to the
+ iostreams, then you can do so by defining your own on_connect() routine. In
+ particular, the default implementation shown below is a good starting point.
+ !*/
+ {
+ try
+ {
+ incoming_things incoming(foreign_ip, local_ip, foreign_port, local_port);
+ outgoing_things outgoing;
+
+ parse_http_request(in, incoming, get_max_content_length());
+ read_body(in, incoming);
+ const std::string& result = on_request(incoming, outgoing);
+ write_http_response(out, outgoing, result);
+ }
+ catch (http_parse_error& e)
+ {
+ write_http_response(out, e);
+ }
+ catch (std::exception& e)
+ {
+ write_http_response(out, e);
+ }
+ }
+ };
+
+}
+
+#endif // DLIB_SERVER_HTTp_ABSTRACT_
+
+
+
diff --git a/ml/dlib/dlib/server/server_iostream.cpp b/ml/dlib/dlib/server/server_iostream.cpp
new file mode 100644
index 000000000..0fd49b67c
--- /dev/null
+++ b/ml/dlib/dlib/server/server_iostream.cpp
@@ -0,0 +1,14 @@
+// Copyright (C) 2003 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_SERVER_IOSTREAM_CPp_
+#define DLIB_SERVER_IOSTREAM_CPp_
+
+#include "server_iostream.h"
+
+namespace dlib
+{
+ const logger server_iostream::_dLog("dlib.server_iostream");
+}
+
+#endif // DLIB_SERVER_IOSTREAM_CPp_
+
diff --git a/ml/dlib/dlib/server/server_iostream.h b/ml/dlib/dlib/server/server_iostream.h
new file mode 100644
index 000000000..eed349015
--- /dev/null
+++ b/ml/dlib/dlib/server/server_iostream.h
@@ -0,0 +1,155 @@
+// Copyright (C) 2006 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_SERVER_IOSTREAm_1_
+#define DLIB_SERVER_IOSTREAm_1_
+
+#include <iostream>
+#include "server_iostream_abstract.h"
+#include "../logger.h"
+#include "../uintn.h"
+#include "server_kernel.h"
+#include "../sockstreambuf.h"
+#include "../map.h"
+
+
+namespace dlib
+{
+
+ class server_iostream : public server
+ {
+
+ /*!
+ INITIAL VALUE
+ - next_id == 0
+ - con_map.size() == 0
+
+ CONVENTION
+ - next_id == the id of the next connection
+ - for all current connections
+ - con_map[id] == the connection object with the given id
+ - m == the mutex that protects the members of this object
+ !*/
+
+ typedef map<uint64,connection*,memory_manager<char>::kernel_2a>::kernel_1b id_map;
+
+ public:
+ server_iostream(
+ ) :
+ next_id(0)
+ {}
+
+ ~server_iostream(
+ )
+ {
+ server::clear();
+ }
+
+ protected:
+
+ void shutdown_connection (
+ uint64 id
+ )
+ {
+ auto_mutex M(m);
+ if (con_map.is_in_domain(id))
+ {
+ con_map[id]->shutdown();
+ }
+ }
+
+ private:
+
+ virtual void on_connect (
+ std::istream& in,
+ std::ostream& out,
+ const std::string& foreign_ip,
+ const std::string& local_ip,
+ unsigned short foreign_port,
+ unsigned short local_port,
+ uint64 connection_id
+ )=0;
+
+ void on_connect (
+ connection& con
+ )
+ {
+ bool my_fault = true;
+ uint64 this_con_id=0;
+ try
+ {
+ sockstreambuf buf(&con);
+ std::istream in(&buf);
+ std::ostream out(&buf);
+ in.tie(&out);
+
+ // add this connection to the con_map
+ {
+ auto_mutex M(m);
+ this_con_id = next_id;
+ connection* this_con = &con;
+ con_map.add(this_con_id,this_con);
+ this_con_id = next_id;
+ ++next_id;
+ }
+
+ my_fault = false;
+ on_connect(
+ in,
+ out,
+ con.get_foreign_ip(),
+ con.get_local_ip(),
+ con.get_foreign_port(),
+ con.get_local_port(),
+ this_con_id
+ );
+
+ // remove this connection from the con_map
+ {
+ auto_mutex M(m);
+ connection* this_con;
+ uint64 junk;
+ con_map.remove(this_con_id,junk,this_con);
+ }
+
+ }
+ catch (std::bad_alloc&)
+ {
+ // make sure we remove this connection from the con_map
+ {
+ auto_mutex M(m);
+ if (con_map.is_in_domain(this_con_id))
+ {
+ connection* this_con;
+ uint64 junk;
+ con_map.remove(this_con_id,junk,this_con);
+ }
+ }
+
+ _dLog << LERROR << "We ran out of memory in server_iostream::on_connect()";
+ // if this is an escaped exception from on_connect 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;
+ }
+ }
+
+ uint64 next_id;
+ id_map con_map;
+ const static logger _dLog;
+ mutex m;
+
+
+ };
+
+
+}
+
+#ifdef NO_MAKEFILE
+#include "server_iostream.cpp"
+#endif
+
+#endif // DLIB_SERVER_IOSTREAm_1_
+
+
+
diff --git a/ml/dlib/dlib/server/server_iostream_abstract.h b/ml/dlib/dlib/server/server_iostream_abstract.h
new file mode 100644
index 000000000..86cd460bf
--- /dev/null
+++ b/ml/dlib/dlib/server/server_iostream_abstract.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2006 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_SERVER_IOSTREAm_ABSTRACT_
+#ifdef DLIB_SERVER_IOSTREAm_ABSTRACT_
+
+
+#include "server_kernel_abstract.h"
+#include <iostream>
+#include <string>
+#include "../uintn.h"
+
+namespace dlib
+{
+
+ class server_iostream : public server
+ {
+
+ /*!
+ WHAT THIS EXTENSION DOES FOR server
+ This extension redefines the on_connect() function so that
+ instead of giving you a connection object you get an istream
+ and ostream object.
+
+ THREAD SAFETY
+ Note that in on_connect() the input stream in is tied to the output stream
+ out. This means that when you read from in it will modify out and thus
+ it is not safe to touch in and out concurrently from different threads
+ unless you untie them (which you do by saying in.tie(0);)
+ !*/
+
+ protected:
+
+ void shutdown_connection (
+ uint64 id
+ );
+ /*!
+ ensures
+ - if (there is a connection currently being serviced with the given id) then
+ - the specified connection is shutdown. (i.e. connection::shutdown() is
+ called on it so the iostreams operating on it will return EOF)
+ !*/
+
+ private:
+
+ virtual void on_connect (
+ std::istream& in,
+ std::ostream& out,
+ const std::string& foreign_ip,
+ const std::string& local_ip,
+ unsigned short foreign_port,
+ unsigned short local_port,
+ uint64 connection_id
+ )=0;
+ /*!
+ requires
+ - on_connect() is called when there is a new TCP connection that needs
+ to be serviced.
+ - in == the input stream that reads data from the new connection
+ - out == the output stream that writes data to the new connection
+ - in.tie() == &out (i.e. when you read from in it automatically calls out.flush())
+ - foreign_ip == the foreign ip address for this connection
+ - foreign_port == the foreign port number for this connection
+ - local_ip == the IP of the local interface this connection is using
+ - local_port == the local port number for this connection
+ - on_connect() is run in its own thread
+ - is_running() == true
+ - the number of current connections < get_max_connection()
+ - connection_id == an integer that uniquely identifies this connection.
+ It can be used by shutdown_connection() to terminate this connection.
+ ensures
+ - when the iostreams hit EOF on_connect() will terminate.
+ (because this is how clear() signals you the server is shutting down)
+ - this function will not call clear()
+ throws
+ - does not throw any exceptions
+ !*/
+
+ };
+
+}
+
+#endif // DLIB_SERVER_IOSTREAm_ABSTRACT_
+
+
diff --git a/ml/dlib/dlib/server/server_kernel.cpp b/ml/dlib/dlib/server/server_kernel.cpp
new file mode 100644
index 000000000..9e8130e78
--- /dev/null
+++ b/ml/dlib/dlib/server/server_kernel.cpp
@@ -0,0 +1,595 @@
+// Copyright (C) 2003 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_SERVER_KERNEL_CPp_
+#define DLIB_SERVER_KERNEL_CPp_
+
+#include "server_kernel.h"
+#include "../string.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ server::
+ server (
+ ) :
+ listening_port(0),
+ running(false),
+ shutting_down(false),
+ running_signaler(running_mutex),
+ thread_count(0),
+ thread_count_signaler(thread_count_mutex),
+ max_connections(1000),
+ thread_count_zero(thread_count_mutex),
+ graceful_close_timeout(500)
+ {
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ server::
+ ~server (
+ )
+ {
+ clear();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ unsigned long server::
+ get_graceful_close_timeout (
+ ) const
+ {
+ auto_mutex lock(max_connections_mutex);
+ return graceful_close_timeout;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ set_graceful_close_timeout (
+ unsigned long timeout
+ )
+ {
+ auto_mutex lock(max_connections_mutex);
+ graceful_close_timeout = timeout;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+
+ int server::
+ get_max_connections (
+ ) const
+ {
+ max_connections_mutex.lock();
+ int temp = max_connections;
+ max_connections_mutex.unlock();
+ return temp;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ set_max_connections (
+ int max
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_CASSERT(
+ max >= 0 ,
+ "\tvoid server::set_max_connections"
+ << "\n\tmax == " << max
+ << "\n\tthis: " << this
+ );
+
+ max_connections_mutex.lock();
+ max_connections = max;
+ max_connections_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ clear (
+ )
+ {
+ // signal that we are shutting down
+ shutting_down_mutex.lock();
+ shutting_down = true;
+ shutting_down_mutex.unlock();
+
+
+
+ max_connections_mutex.lock();
+ listening_port_mutex.lock();
+ listening_ip_mutex.lock();
+ listening_ip = "";
+ listening_port = 0;
+ max_connections = 1000;
+ graceful_close_timeout = 500;
+ listening_port_mutex.unlock();
+ listening_ip_mutex.unlock();
+ max_connections_mutex.unlock();
+
+
+ // tell all the connections to shut down
+ cons_mutex.lock();
+ connection* temp;
+ while (cons.size() > 0)
+ {
+ cons.remove_any(temp);
+ temp->shutdown();
+ }
+ cons_mutex.unlock();
+
+
+ // wait for all the connections to shut down
+ thread_count_mutex.lock();
+ while (thread_count > 0)
+ {
+ thread_count_zero.wait();
+ }
+ thread_count_mutex.unlock();
+
+
+
+
+ // wait for the listener to close
+ running_mutex.lock();
+ while (running == true)
+ {
+ running_signaler.wait();
+ }
+ running_mutex.unlock();
+
+
+
+ // signal that the shutdown is complete
+ shutting_down_mutex.lock();
+ shutting_down = false;
+ shutting_down_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ start_async_helper (
+ )
+ {
+ try
+ {
+ start_accepting_connections();
+ }
+ catch (std::exception& e)
+ {
+ sdlog << LERROR << e.what();
+ }
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ start_async (
+ )
+ {
+ auto_mutex lock(running_mutex);
+ if (running)
+ return;
+
+ // Any exceptions likely to be thrown by the server are going to be
+ // thrown when trying to bind the port. So calling this here rather
+ // than in the thread we are about to make will cause start_async()
+ // to report errors back to the user in a very straight forward way.
+ open_listening_socket();
+
+ async_start_thread.reset(new thread_function(make_mfp(*this,&server::start_async_helper)));
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ open_listening_socket (
+ )
+ {
+ if (!sock)
+ {
+ int status = create_listener(sock,listening_port,listening_ip);
+ const int port_used = listening_port;
+
+ // if there was an error then clear this object
+ if (status < 0)
+ {
+ max_connections_mutex.lock();
+ listening_port_mutex.lock();
+ listening_ip_mutex.lock();
+ listening_ip = "";
+ listening_port = 0;
+ max_connections = 1000;
+ graceful_close_timeout = 500;
+ listening_port_mutex.unlock();
+ listening_ip_mutex.unlock();
+ max_connections_mutex.unlock();
+ }
+
+
+
+ // throw an exception for the error
+ if (status == PORTINUSE)
+ {
+ throw dlib::socket_error(
+ EPORT_IN_USE,
+ "error occurred in server::start()\nport " + cast_to_string(port_used) + " already in use"
+ );
+ }
+ else if (status == OTHER_ERROR)
+ {
+ throw dlib::socket_error(
+ "error occurred in server::start()\nunable to create listener"
+ );
+ }
+ }
+
+ running_mutex.lock();
+ running = true;
+ running_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ start (
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_CASSERT(
+ this->is_running() == false,
+ "\tvoid server::start"
+ << "\n\tis_running() == " << this->is_running()
+ << "\n\tthis: " << this
+ );
+
+ start_accepting_connections();
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ start_accepting_connections (
+ )
+ {
+ open_listening_socket();
+
+ // determine the listening port
+ bool port_assigned = false;
+ listening_port_mutex.lock();
+ if (listening_port == 0)
+ {
+ port_assigned = true;
+ listening_port = sock->get_listening_port();
+ }
+ listening_port_mutex.unlock();
+ if (port_assigned)
+ on_listening_port_assigned();
+
+
+
+ int status = 0;
+
+ connection* client;
+ bool exit = false;
+ while ( true )
+ {
+
+
+ // accept the next connection
+ status = sock->accept(client,1000);
+
+
+ // if there was an error then quit the loop
+ if (status == OTHER_ERROR)
+ {
+ break;
+ }
+
+ shutting_down_mutex.lock();
+ // if we are shutting down then signal that we should quit the loop
+ exit = shutting_down;
+ shutting_down_mutex.unlock();
+
+
+ // if we should be shutting down
+ if (exit)
+ {
+ // if a connection was opened then close it
+ if (status == 0)
+ delete client;
+ break;
+ }
+
+
+
+ // if the accept timed out
+ if (status == TIMEOUT)
+ {
+ continue;
+ }
+
+
+
+
+
+ // add this new connection to cons
+ cons_mutex.lock();
+ connection* client_temp = client;
+ try{cons.add(client_temp);}
+ catch(...)
+ {
+ sock.reset();
+ delete client;
+ cons_mutex.unlock();
+
+ // signal that we are not running start() anymore
+ running_mutex.lock();
+ running = false;
+ running_signaler.broadcast();
+ running_mutex.unlock();
+
+
+ clear();
+ throw;
+ }
+ cons_mutex.unlock();
+
+
+ // make a param structure
+ param* temp = 0;
+ try{
+ temp = new param (
+ *this,
+ *client,
+ get_graceful_close_timeout()
+ );
+ } catch (...)
+ {
+ sock.reset();
+ delete client;
+ running_mutex.lock();
+ running = false;
+ running_signaler.broadcast();
+ running_mutex.unlock();
+ clear();
+ throw;
+ }
+
+
+ // if create_new_thread failed
+ if (!create_new_thread(service_connection,temp))
+ {
+ delete temp;
+ // close the listening socket
+ sock.reset();
+
+ // close the new connection and remove it from cons
+ cons_mutex.lock();
+ connection* ctemp;
+ if (cons.is_member(client))
+ {
+ cons.remove(client,ctemp);
+ }
+ delete client;
+ cons_mutex.unlock();
+
+
+ // signal that the listener has closed
+ running_mutex.lock();
+ running = false;
+ running_signaler.broadcast();
+ running_mutex.unlock();
+
+ // make sure the object is cleared
+ clear();
+
+ // throw the exception
+ throw dlib::thread_error(
+ ECREATE_THREAD,
+ "error occurred in server::start()\nunable to start thread"
+ );
+ }
+ // if we made the new thread then update thread_count
+ else
+ {
+ // increment the thread count
+ thread_count_mutex.lock();
+ ++thread_count;
+ if (thread_count == 0)
+ thread_count_zero.broadcast();
+ thread_count_mutex.unlock();
+ }
+
+
+
+
+ // check if we have hit the maximum allowed number of connections
+ max_connections_mutex.lock();
+ // if max_connections is zero or the loop is ending then skip this
+ if (max_connections != 0)
+ {
+ // wait for thread_count to be less than max_connections
+ thread_count_mutex.lock();
+ while (thread_count >= max_connections)
+ {
+ max_connections_mutex.unlock();
+ thread_count_signaler.wait();
+ max_connections_mutex.lock();
+
+ // if we are shutting down the quit the loop
+ shutting_down_mutex.lock();
+ exit = shutting_down;
+ shutting_down_mutex.unlock();
+ if (exit)
+ break;
+ }
+ thread_count_mutex.unlock();
+ }
+ max_connections_mutex.unlock();
+
+ if (exit)
+ {
+ break;
+ }
+ } //while ( true )
+
+
+ // close the socket
+ sock.reset();
+
+ // signal that the listener has closed
+ running_mutex.lock();
+ running = false;
+ running_signaler.broadcast();
+ running_mutex.unlock();
+
+ // if there was an error with accept then throw an exception
+ if (status == OTHER_ERROR)
+ {
+ // make sure the object is cleared
+ clear();
+
+ // throw the exception
+ throw dlib::socket_error(
+ "error occurred in server::start()\nlistening socket returned error"
+ );
+ }
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ bool server::
+ is_running (
+ ) const
+ {
+ running_mutex.lock();
+ bool temp = running;
+ running_mutex.unlock();
+ return temp;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ const std::string server::
+ get_listening_ip (
+ ) const
+ {
+ listening_ip_mutex.lock();
+ std::string ip(listening_ip);
+ listening_ip_mutex.unlock();
+ return ip;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ int server::
+ get_listening_port (
+ ) const
+ {
+ listening_port_mutex.lock();
+ int port = listening_port;
+ listening_port_mutex.unlock();
+ return port;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ set_listening_port (
+ int port
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_CASSERT(
+ ( port >= 0 &&
+ this->is_running() == false ),
+ "\tvoid server::set_listening_port"
+ << "\n\tport == " << port
+ << "\n\tis_running() == " << this->is_running()
+ << "\n\tthis: " << this
+ );
+
+ listening_port_mutex.lock();
+ listening_port = port;
+ listening_port_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ void server::
+ set_listening_ip (
+ const std::string& ip
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_CASSERT(
+ ( ( is_ip_address(ip) || ip == "" ) &&
+ this->is_running() == false ),
+ "\tvoid server::set_listening_ip"
+ << "\n\tip == " << ip
+ << "\n\tis_running() == " << this->is_running()
+ << "\n\tthis: " << this
+ );
+
+ listening_ip_mutex.lock();
+ listening_ip = ip;
+ listening_ip_mutex.unlock();
+ }
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+ // static member function definitions
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ const logger server::sdlog("dlib.server");
+
+ void server::
+ service_connection(
+ void* item
+ )
+ {
+ param& p = *static_cast<param*>(item);
+
+
+ p.the_server.on_connect(p.new_connection);
+
+
+ // remove this connection from cons and close it
+ p.the_server.cons_mutex.lock();
+ connection* temp;
+ if (p.the_server.cons.is_member(&p.new_connection))
+ p.the_server.cons.remove(&p.new_connection,temp);
+ p.the_server.cons_mutex.unlock();
+
+ try{ close_gracefully(&p.new_connection, p.graceful_close_timeout); }
+ catch (...) { sdlog << LERROR << "close_gracefully() threw"; }
+
+ // decrement the thread count and signal if it is now zero
+ p.the_server.thread_count_mutex.lock();
+ --p.the_server.thread_count;
+ p.the_server.thread_count_signaler.broadcast();
+ if (p.the_server.thread_count == 0)
+ p.the_server.thread_count_zero.broadcast();
+ p.the_server.thread_count_mutex.unlock();
+
+ delete &p;
+
+
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_SERVER_KERNEL_CPp_
+
diff --git a/ml/dlib/dlib/server/server_kernel.h b/ml/dlib/dlib/server/server_kernel.h
new file mode 100644
index 000000000..4232ff343
--- /dev/null
+++ b/ml/dlib/dlib/server/server_kernel.h
@@ -0,0 +1,234 @@
+// Copyright (C) 2003 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_SERVER_KERNEL_1_
+#define DLIB_SERVER_KERNEL_1_
+
+#include "server_kernel_abstract.h"
+
+#include <memory>
+#include <string>
+
+#include "../threads.h"
+#include "../sockets.h"
+#include "../algs.h"
+#include "../set.h"
+#include "../logger.h"
+
+
+namespace dlib
+{
+
+ // These forward declarations are here so we can use them in the typedefs in the server
+ // class. The reason for this is for backwards compatibility with previous versions of
+ // dlib.
+ class server_http;
+ class server_iostream;
+
+ class server
+ {
+
+
+ /*!
+ INITIAL VALUE
+ listening_port == 0
+ listening_ip == ""
+ running == false
+ shutting_down == false
+ cons.size() == 0
+ listening_port_mutex == a mutex
+ listening_ip_mutex == a mutex
+ running_mutex == a mutex
+ running_signaler == a signaler associated with running_mutex
+ shutting_down_mutex == a mutex
+ cons_mutex == a mutex
+ thread_count == 0
+ thread_count_mutex == a mutex
+ thread_count_signaler == a signaler associated with thread_count_mutex
+ thread_count_zero == a signaler associated with thread_count_mutex
+ max_connections == 1000
+ max_connections_mutex == a mutex for max_connections and graceful_close_timeout
+ graceful_close_timeout == 500
+
+ CONVENTION
+ listening_port == get_listening_port()
+ listening_ip == get_listening_ip()
+ running == is_running()
+ shutting_down == true while clear() is running. this
+ bool is used to tell the thread blocked on
+ accept that it should terminate
+ cons == a set containing all open connections
+ listening_port_mutex == a mutex for listening_port
+ listening_ip_mutex == a mutex for listening_ip
+ running_mutex == a mutex for running
+ running_signaler == a signaler for running and
+ is associated with running_mutex. it is
+ used to signal when running is false
+ shutting_down_mutex == a mutex for shutting_down
+ cons_mutex == a mutex for cons
+ thread_count == the number of threads currently running
+ thread_count_mutex == a mutex for thread_count
+ thread_count_signaler == a signaler for thread_count and
+ is associated with thread_count_mutex. it
+ is used to signal when thread_count is
+ decremented
+ thread_count_zero == a signaler for thread_count and
+ is associated with thread_count_mutex. it
+ is used to signal when thread_count becomes
+ zero
+ max_connections == get_max_connections()
+ max_connections_mutex == a mutex for max_connections
+ !*/
+
+
+ typedef set<connection*>::kernel_1a set_of_connections;
+
+ // this structure is used to pass parameters to new threads
+ struct param
+ {
+ param (
+ server& server_,
+ connection& new_connection_,
+ unsigned long graceful_close_timeout_
+ ) :
+ the_server(server_),
+ new_connection(new_connection_),
+ graceful_close_timeout(graceful_close_timeout_)
+ {}
+
+ server& the_server;
+ connection& new_connection;
+ unsigned long graceful_close_timeout;
+ };
+
+
+
+ public:
+
+ // These typedefs are here for backward compatibility with previous versions of dlib
+ typedef server kernel_1a;
+ typedef server kernel_1a_c;
+ typedef server_iostream iostream_1a;
+ typedef server_iostream iostream_1a_c;
+ typedef server_http http_1a;
+ typedef server_http http_1a_c;
+
+ server(
+ );
+
+ virtual ~server(
+ );
+
+ void clear(
+ );
+
+ void start (
+ );
+
+ bool is_running (
+ ) const;
+
+ const std::string get_listening_ip (
+ ) const;
+
+ int get_listening_port (
+ ) const;
+
+ void set_listening_port (
+ int port
+ );
+
+ void set_listening_ip (
+ const std::string& ip
+ );
+
+ void set_max_connections (
+ int max
+ );
+
+ int get_max_connections (
+ ) const;
+
+ void start_async (
+ );
+
+ void set_graceful_close_timeout (
+ unsigned long timeout
+ );
+
+ unsigned long get_graceful_close_timeout (
+ ) const;
+
+ private:
+
+ void start_async_helper (
+ );
+
+ void start_accepting_connections (
+ );
+
+ void open_listening_socket (
+ );
+
+ virtual void on_connect (
+ connection& new_connection
+ )=0;
+
+ virtual void on_listening_port_assigned (
+ ) {}
+
+ const static logger sdlog;
+
+ static void service_connection(
+ void* item
+ );
+ /*!
+ requires
+ item is a pointer to a param struct
+ ensures
+ services the new connection
+ will take care of closing the connection and
+ adding the connection to cons when it first starts and
+ remove the connection from cons and signal that it has
+ done so when it ends
+ !*/
+
+ // data members
+ int listening_port;
+ std::string listening_ip;
+ bool running;
+ bool shutting_down;
+ set_of_connections cons;
+ mutex listening_port_mutex;
+ mutex listening_ip_mutex;
+ rmutex running_mutex;
+ rsignaler running_signaler;
+ mutex shutting_down_mutex;
+ mutex cons_mutex;
+ int thread_count;
+ mutex thread_count_mutex;
+ signaler thread_count_signaler;
+ int max_connections;
+ mutex max_connections_mutex;
+ signaler thread_count_zero;
+ std::unique_ptr<thread_function> async_start_thread;
+ std::unique_ptr<listener> sock;
+ unsigned long graceful_close_timeout;
+
+
+ // restricted functions
+ server(server&);
+ server& operator= (
+ server&
+ );
+ };
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#ifdef NO_MAKEFILE
+#include "server_kernel.cpp"
+#endif
+
+#endif // DLIB_SERVER_KERNEL_1_
+
diff --git a/ml/dlib/dlib/server/server_kernel_abstract.h b/ml/dlib/dlib/server/server_kernel_abstract.h
new file mode 100644
index 000000000..f7860d26c
--- /dev/null
+++ b/ml/dlib/dlib/server/server_kernel_abstract.h
@@ -0,0 +1,310 @@
+// Copyright (C) 2003 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#undef DLIB_SERVER_KERNEL_ABSTRACT_
+#ifdef DLIB_SERVER_KERNEL_ABSTRACT_
+
+#include "../threads/threads_kernel_abstract.h"
+#include "../sockets/sockets_kernel_abstract.h"
+#include <string>
+
+
+namespace dlib
+{
+ class server
+ {
+
+ /*!
+ INITIAL VALUE
+ get_listening_ip() == ""
+ get_listening_port() == 0
+ is_running() == false
+ get_max_connections() == 1000
+ get_graceful_close_timeout() == 500
+
+
+ CALLBACK FUNCTIONS
+ on_connect():
+ To use this object inherit from it and define the pure virtual function
+ on_connect. Inside this function is where you will handle each new
+ connection. Note that the connection object passed to on_connect() should
+ NOT be closed, just let the function end and it will be gracefully closed
+ for you. Also note that each call to on_connect() is run in its own
+ thread. Also note that on_connect() should NOT throw any exceptions,
+ all exceptions must be dealt with inside on_connect() and cannot be
+ allowed to leave.
+
+ on_listening_port_assigned():
+ This function is called to let the client know that the operating
+ system has assigned a port number to the listening port. This
+ happens if a port number of zero was given. Note that this
+ function does not need to be defined. If you don't care then
+ don't define it and it will do nothing. Note also that this function
+ is NOT called in its own thread. Thus, making it block might hang the
+ server.
+
+ WHAT THIS OBJECT REPRESENTS
+ This object represents a server that listens on a port and spawns new
+ threads to handle each new connection.
+
+ Note that the clear() function does not return until all calls to
+ on_connect() have finished and the start() function has been shutdown.
+ Also note that when clear() is called all open connection objects
+ will be shutdown().
+
+ A note about get_max_connections(): when the maximum number of connections
+ has been reached accept() will simply not be called until the number of
+ open connections drops below get_max_connections(). This means connections
+ will just wait to be serviced, rather than being outright refused.
+
+ THREAD SAFETY
+ All member functions are thread-safe.
+ !*/
+
+ public:
+
+ server(
+ );
+ /*!
+ ensures
+ - #*this is properly initialized
+ throws
+ - std::bad_alloc
+ - dlib::thread_error
+ !*/
+
+ virtual ~server(
+ );
+ /*!
+ requires
+ - is not called from any of server's callbacks
+ ensures
+ - all resources associated with *this have been released
+ !*/
+
+ void clear(
+ );
+ /*!
+ requires
+ - is not called from any of server's callbacks
+ ensures
+ - #*this has its initial value
+ - all open connection objects passed to on_connect() are shutdown()
+ - blocks until all calls to on_connect() have finished
+ - blocks until the start() function has released all its resources
+ throws
+ - std::bad_alloc
+ if this exception is thrown then the server object is unusable
+ until clear() is called and succeeds
+ !*/
+
+ void start (
+ );
+ /*!
+ requires
+ - is_running() == false
+ ensures
+ - starts listening on the port and ip specified by get_listening_ip()
+ and #get_listening_port() for new connections.
+ - if (get_listening_port() == 0) then
+ - a port to listen on will be automatically selected
+ - #get_listening_port() == the selected port being used
+ - if (get_listening_ip() == "" ) then
+ - all local IPs will be listened on
+ - blocks until clear() is called or an error occurs
+ throws
+ - dlib::socket_error
+ start() will throw this exception if there is some problem binding
+ ports and/or starting the server or if there is a problem
+ accepting new connections while it's running.
+ If this happens then
+ - All open connection objects passed to on_connect() are shutdown()
+ and the exception will not be thrown until all on_connect() calls
+ have terminated.
+ - The server will be cleared and returned to its initial value.
+ - dlib::thread_error
+ start() will throw this exception if there is a problem
+ creating new threads. Or it may throw this exception if there
+ is a problem creating threading objects.
+ If this happens then
+ - All open connection objects passed to on_connect() are shutdown()
+ and the exception will not be thrown until all on_connect() calls
+ have terminated.
+ - The server will be cleared and returned to its initial value.
+ - std::bad_alloc
+ start() may throw this exception and if it does then the object
+ will be unusable until clear() is called and succeeds
+ !*/
+
+ void start_async (
+ );
+ /*!
+ ensures
+ - starts listening on the port and ip specified by get_listening_ip()
+ and #get_listening_port() for new connections.
+ - if (get_listening_port() == 0) then
+ - a port to listen on will be automatically selected
+ - #get_listening_port() == the selected port being used
+ - if (get_listening_ip() == "" ) then
+ - all local IPs will be listened on
+ - does NOT block. That is, this function will return right away and
+ the server will run on a background thread until clear() or this
+ object's destructor is called (or until some kind of fatal error
+ occurs).
+ - if an error occurs in the background thread while the server is
+ running then it will shut itself down, set is_running() to false, and
+ log the error to a dlib::logger object.
+ - calling start_async() on a running server has no effect.
+ throws
+ - dlib::socket_error
+ start_async() will throw this exception if there is some problem binding
+ ports and/or starting the server.
+ If this happens then
+ - The server will be cleared and returned to its initial value.
+ !*/
+
+ bool is_running (
+ ) const;
+ /*!
+ ensures
+ - returns true if start() is running
+ - returns false if start() is not running or has released all
+ its resources and is about to terminate
+ throws
+ - std::bad_alloc
+ !*/
+
+ int get_max_connections (
+ ) const;
+ /*!
+ ensures
+ - returns the maximum number of connections the server will accept
+ at a time.
+ - returns 0 if the server will accept any number of connections
+ throws
+ - std::bad_alloc
+ !*/
+
+
+ const std::string get_listening_ip (
+ ) const;
+ /*!
+ ensures
+ - returns the local ip to listen for new connections on
+ - returns "" if ALL local ips are to be listened on
+ throws
+ - std::bad_alloc
+ !*/
+
+ int get_listening_port (
+ ) const;
+ /*!
+ ensures
+ - returns the local port number to listen for new connections on
+ - returns 0 if the local port number has not yet been set
+ throws
+ - std::bad_alloc
+ !*/
+
+ void set_listening_port (
+ int port
+ );
+ /*!
+ requires
+ - port >= 0
+ - is_running() == false
+ ensures
+ - #get_listening_port() == port
+ throws
+ - std::bad_alloc
+ !*/
+
+ void set_listening_ip (
+ const std::string& ip
+ );
+ /*!
+ requires
+ - is_ip_address(ip) == true or ip == ""
+ - is_running() == false
+ ensures
+ - #get_listening_ip() == ip
+ throws
+ - std::bad_alloc
+ !*/
+
+ void set_max_connections (
+ int max
+ );
+ /*!
+ requires
+ - max >= 0
+ ensures
+ - #get_max_connections() == max
+ throws
+ - std::bad_alloc
+ !*/
+
+ void set_graceful_close_timeout (
+ unsigned long timeout
+ );
+ /*!
+ ensures
+ - #get_graceful_close_timeout() == timeout
+ !*/
+
+ unsigned long get_graceful_close_timeout (
+ ) const;
+ /*!
+ ensures
+ - When on_connect() terminates, it will close the connection using
+ close_gracefully(). This is done so that any data still in the
+ operating system's output buffers gets a chance to be properly
+ transmitted to the remote host. Part of this involves waiting for
+ the remote host to close their end of the connection. Therefore,
+ get_graceful_close_timeout() returns the timeout, in milliseconds,
+ that we wait for the remote host to close their end of the
+ connection. This is the timeout value given to close_gracefully().
+ !*/
+
+ private:
+
+ virtual void on_connect (
+ connection& new_connection
+ )=0;
+ /*!
+ requires
+ - on_connect() is run in its own thread
+ - is_running() == true
+ - the number of current connections < get_max_connection()
+ - new_connection == the new connection to the server which is
+ to be serviced by this call to on_connect()
+ ensures
+ - when new_connection is shutdown() on_connect() will terminate
+ - this function will not call clear()
+ throws
+ - does not throw any exceptions
+ !*/
+
+ // do nothing by default
+ virtual void on_listening_port_assigned (
+ ) {}
+ /*!
+ requires
+ - is called if a listening port of zero was specified and
+ an actual port number has just been assigned to the server
+ ensures
+ - this function will not block
+ - this function will not call clear()
+ throws
+ - does not throw any exceptions
+ !*/
+
+
+ // restricted functions
+ server(server&); // copy constructor
+ server& operator=(server&); // assignment operator
+ };
+
+}
+
+#endif // DLIB_SERVER_KERNEL_ABSTRACT_
+