    // 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_