/*
 * Copyright (c) 2014, Peter Thorson. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the WebSocket++ Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#ifndef WEBSOCKETPP_CONNECTION_HPP
#define WEBSOCKETPP_CONNECTION_HPP

#include <websocketpp/close.hpp>
#include <websocketpp/error.hpp>
#include <websocketpp/frame.hpp>

#include <websocketpp/logger/levels.hpp>
#include <websocketpp/processors/processor.hpp>
#include <websocketpp/transport/base/connection.hpp>
#include <websocketpp/http/constants.hpp>

#include <websocketpp/common/connection_hdl.hpp>
#include <websocketpp/common/cpp11.hpp>
#include <websocketpp/common/functional.hpp>

#include <queue>
#include <sstream>
#include <string>
#include <vector>

namespace websocketpp {

/// The type and function signature of an open handler
/**
 * The open handler is called once for every successful WebSocket connection
 * attempt. Either the fail handler or the open handler will be called for each
 * WebSocket connection attempt. HTTP Connections that did not attempt to
 * upgrade the connection to the WebSocket protocol will trigger the http
 * handler instead of fail/open.
 */
typedef lib::function<void(connection_hdl)> open_handler;

/// The type and function signature of a close handler
/**
 * The close handler is called once for every successfully established
 * connection after it is no longer capable of sending or receiving new messages
 *
 * The close handler will be called exactly once for every connection for which
 * the open handler was called.
 */
typedef lib::function<void(connection_hdl)> close_handler;

/// The type and function signature of a fail handler
/**
 * The fail handler is called once for every unsuccessful WebSocket connection
 * attempt. Either the fail handler or the open handler will be called for each
 * WebSocket connection attempt. HTTP Connections that did not attempt to
 * upgrade the connection to the WebSocket protocol will trigger the http
 * handler instead of fail/open.
 */
typedef lib::function<void(connection_hdl)> fail_handler;

/// The type and function signature of an interrupt handler
/**
 * The interrupt handler is called when a connection receives an interrupt
 * request from the application. Interrupts allow the application to trigger a
 * handler to be run in the absense of a WebSocket level handler trigger (like
 * a new message).
 *
 * This is typically used by another application thread to schedule some tasks
 * that can only be run from within the handler chain for thread safety reasons.
 */
typedef lib::function<void(connection_hdl)> interrupt_handler;

/// The type and function signature of a ping handler
/**
 * The ping handler is called when the connection receives a WebSocket ping
 * control frame. The string argument contains the ping payload. The payload is
 * a binary string up to 126 bytes in length. The ping handler returns a bool,
 * true if a pong response should be sent, false if the pong response should be
 * suppressed.
 */
typedef lib::function<bool(connection_hdl,std::string)> ping_handler;

/// The type and function signature of a pong handler
/**
 * The pong handler is called when the connection receives a WebSocket pong
 * control frame. The string argument contains the pong payload. The payload is
 * a binary string up to 126 bytes in length.
 */
typedef lib::function<void(connection_hdl,std::string)> pong_handler;

/// The type and function signature of a pong timeout handler
/**
 * The pong timeout handler is called when a ping goes unanswered by a pong for
 * longer than the locally specified timeout period.
 */
typedef lib::function<void(connection_hdl,std::string)> pong_timeout_handler;

/// The type and function signature of a validate handler
/**
 * The validate handler is called after a WebSocket handshake has been received
 * and processed but before it has been accepted. This gives the application a
 * chance to implement connection details specific policies for accepting
 * connections and the ability to negotiate extensions and subprotocols.
 *
 * The validate handler return value indicates whether or not the connection
 * should be accepted. Additional methods may be called during the function to
 * set response headers, set HTTP return/error codes, etc.
 */
typedef lib::function<bool(connection_hdl)> validate_handler;

/// The type and function signature of a http handler
/**
 * The http handler is called when an HTTP connection is made that does not
 * attempt to upgrade the connection to the WebSocket protocol. This allows
 * WebSocket++ servers to respond to these requests with regular HTTP responses.
 *
 * This can be used to deliver error pages & dashboards and to deliver static
 * files such as the base HTML & JavaScript for an otherwise single page
 * WebSocket application.
 *
 * Note: WebSocket++ is designed to be a high performance WebSocket server. It
 * is not tuned to provide a full featured, high performance, HTTP web server
 * solution. The HTTP handler is appropriate only for low volume HTTP traffic.
 * If you expect to serve high volumes of HTTP traffic a dedicated HTTP web
 * server is strongly recommended.
 *
 * The default HTTP handler will return a 426 Upgrade Required error. Custom
 * handlers may override the response status code to deliver any type of
 * response.
 */
typedef lib::function<void(connection_hdl)> http_handler;

//
typedef lib::function<void(lib::error_code const & ec, size_t bytes_transferred)> read_handler;
typedef lib::function<void(lib::error_code const & ec)> write_frame_handler;

// constants related to the default WebSocket protocol versions available
#ifdef _WEBSOCKETPP_INITIALIZER_LISTS_ // simplified C++11 version
    /// Container that stores the list of protocol versions supported
    /**
     * @todo Move this to configs to allow compile/runtime disabling or enabling
     * of protocol versions
     */
    static std::vector<int> const versions_supported = {0,7,8,13};
#else
    /// Helper array to get around lack of initializer lists pre C++11
    static int const helper[] = {0,7,8,13};
    /// Container that stores the list of protocol versions supported
    /**
     * @todo Move this to configs to allow compile/runtime disabling or enabling
     * of protocol versions
     */
    static std::vector<int> const versions_supported(helper,helper+4);
#endif

namespace session {
namespace state {
    // externally visible session state (states based on the RFC)
    enum value {
        connecting = 0,
        open = 1,
        closing = 2,
        closed = 3
    };
} // namespace state


namespace fail {
namespace status {
    enum value {
        GOOD = 0,           // no failure yet!
        SYSTEM = 1,         // system call returned error, check that code
        WEBSOCKET = 2,      // websocket close codes contain error
        UNKNOWN = 3,        // No failure information is available
        TIMEOUT_TLS = 4,    // TLS handshake timed out
        TIMEOUT_WS = 5      // WS handshake timed out
    };
} // namespace status
} // namespace fail

namespace internal_state {
    // More granular internal states. These are used for multi-threaded
    // connection synchronization and preventing values that are not yet or no
    // longer available from being used.

    enum value {
        USER_INIT = 0,
        TRANSPORT_INIT = 1,
        READ_HTTP_REQUEST = 2,
        WRITE_HTTP_REQUEST = 3,
        READ_HTTP_RESPONSE = 4,
        WRITE_HTTP_RESPONSE = 5,
        PROCESS_HTTP_REQUEST = 6,
        PROCESS_CONNECTION = 7
    };
} // namespace internal_state


namespace http_state {
    // states to keep track of the progress of http connections

    enum value {
        init = 0,
        deferred = 1,
        headers_written = 2,
        body_written = 3,
        closed = 4
    };
} // namespace http_state

} // namespace session

/// Represents an individual WebSocket connection
template <typename config>
class connection
 : public config::transport_type::transport_con_type
 , public config::connection_base
{
public:
    /// Type of this connection
    typedef connection<config> type;
    /// Type of a shared pointer to this connection
    typedef lib::shared_ptr<type> ptr;
    /// Type of a weak pointer to this connection
    typedef lib::weak_ptr<type> weak_ptr;

    /// Type of the concurrency component of this connection
    typedef typename config::concurrency_type concurrency_type;
    /// Type of the access logging policy
    typedef typename config::alog_type alog_type;
    /// Type of the error logging policy
    typedef typename config::elog_type elog_type;

    /// Type of the transport component of this connection
    typedef typename config::transport_type::transport_con_type
        transport_con_type;
    /// Type of a shared pointer to the transport component of this connection
    typedef typename transport_con_type::ptr transport_con_ptr;

    typedef lib::function<void(ptr)> termination_handler;

    typedef typename concurrency_type::scoped_lock_type scoped_lock_type;
    typedef typename concurrency_type::mutex_type mutex_type;

    typedef typename config::request_type request_type;
    typedef typename config::response_type response_type;

    typedef typename config::message_type message_type;
    typedef typename message_type::ptr message_ptr;

    typedef typename config::con_msg_manager_type con_msg_manager_type;
    typedef typename con_msg_manager_type::ptr con_msg_manager_ptr;

    /// Type of RNG
    typedef typename config::rng_type rng_type;

    typedef processor::processor<config> processor_type;
    typedef lib::shared_ptr<processor_type> processor_ptr;

    // Message handler (needs to know message type)
    typedef lib::function<void(connection_hdl,message_ptr)> message_handler;

    /// Type of a pointer to a transport timer handle
    typedef typename transport_con_type::timer_ptr timer_ptr;

    // Misc Convenience Types
    typedef session::internal_state::value istate_type;

private:
    enum terminate_status {
        failed = 1,
        closed,
        unknown
    };
public:

    explicit connection(bool p_is_server, std::string const & ua, alog_type& alog,
        elog_type& elog, rng_type & rng)
      : transport_con_type(p_is_server, alog, elog)
      , m_handle_read_frame(lib::bind(
            &type::handle_read_frame,
            this,
            lib::placeholders::_1,
            lib::placeholders::_2
        ))
      , m_write_frame_handler(lib::bind(
            &type::handle_write_frame,
            this,
            lib::placeholders::_1
        ))
      , m_user_agent(ua)
      , m_open_handshake_timeout_dur(config::timeout_open_handshake)
      , m_close_handshake_timeout_dur(config::timeout_close_handshake)
      , m_pong_timeout_dur(config::timeout_pong)
      , m_max_message_size(config::max_message_size)
      , m_state(session::state::connecting)
      , m_internal_state(session::internal_state::USER_INIT)
      , m_msg_manager(new con_msg_manager_type())
      , m_send_buffer_size(0)
      , m_write_flag(false)
      , m_read_flag(true)
      , m_is_server(p_is_server)
      , m_alog(alog)
      , m_elog(elog)
      , m_rng(rng)
      , m_local_close_code(close::status::abnormal_close)
      , m_remote_close_code(close::status::abnormal_close)
      , m_is_http(false)
      , m_http_state(session::http_state::init)
      , m_was_clean(false)
    {
        m_alog.write(log::alevel::devel,"connection constructor");
    }

    /// Get a shared pointer to this component
    ptr get_shared() {
        return lib::static_pointer_cast<type>(transport_con_type::get_shared());
    }

    ///////////////////////////
    // Set Handler Callbacks //
    ///////////////////////////

    /// Set open handler
    /**
     * The open handler is called after the WebSocket handshake is complete and
     * the connection is considered OPEN.
     *
     * @param h The new open_handler
     */
    void set_open_handler(open_handler h) {
        m_open_handler = h;
    }

    /// Set close handler
    /**
     * The close handler is called immediately after the connection is closed.
     *
     * @param h The new close_handler
     */
    void set_close_handler(close_handler h) {
        m_close_handler = h;
    }

    /// Set fail handler
    /**
     * The fail handler is called whenever the connection fails while the
     * handshake is bring processed.
     *
     * @param h The new fail_handler
     */
    void set_fail_handler(fail_handler h) {
        m_fail_handler = h;
    }

    /// Set ping handler
    /**
     * The ping handler is called whenever the connection receives a ping
     * control frame. The ping payload is included.
     *
     * The ping handler's return time controls whether or not a pong is
     * sent in response to this ping. Returning false will suppress the
     * return pong. If no ping handler is set a pong will be sent.
     *
     * @param h The new ping_handler
     */
    void set_ping_handler(ping_handler h) {
        m_ping_handler = h;
    }

    /// Set pong handler
    /**
     * The pong handler is called whenever the connection receives a pong
     * control frame. The pong payload is included.
     *
     * @param h The new pong_handler
     */
    void set_pong_handler(pong_handler h) {
        m_pong_handler = h;
    }

    /// Set pong timeout handler
    /**
     * If the transport component being used supports timers, the pong timeout
     * handler is called whenever a pong control frame is not received with the
     * configured timeout period after the application sends a ping.
     *
     * The config setting `timeout_pong` controls the length of the timeout
     * period. It is specified in milliseconds.
     *
     * This can be used to probe the health of the remote endpoint's WebSocket
     * implementation. This does not guarantee that the remote application
     * itself is still healthy but can be a useful diagnostic.
     *
     * Note: receipt of this callback doesn't mean the pong will never come.
     * This functionality will not suppress delivery of the pong in question
     * should it arrive after the timeout.
     *
     * @param h The new pong_timeout_handler
     */
    void set_pong_timeout_handler(pong_timeout_handler h) {
        m_pong_timeout_handler = h;
    }

    /// Set interrupt handler
    /**
     * The interrupt handler is called whenever the connection is manually
     * interrupted by the application.
     *
     * @param h The new interrupt_handler
     */
    void set_interrupt_handler(interrupt_handler h) {
        m_interrupt_handler = h;
    }

    /// Set http handler
    /**
     * The http handler is called after an HTTP request other than a WebSocket
     * upgrade request is received. It allows a WebSocket++ server to respond
     * to regular HTTP requests on the same port as it processes WebSocket
     * connections. This can be useful for hosting error messages, flash
     * policy files, status pages, and other simple HTTP responses. It is not
     * intended to be used as a primary web server.
     *
     * @param h The new http_handler
     */
    void set_http_handler(http_handler h) {
        m_http_handler = h;
    }

    /// Set validate handler
    /**
     * The validate handler is called after a WebSocket handshake has been
     * parsed but before a response is returned. It provides the application
     * a chance to examine the request and determine whether or not it wants
     * to accept the connection.
     *
     * Returning false from the validate handler will reject the connection.
     * If no validate handler is present, all connections will be allowed.
     *
     * @param h The new validate_handler
     */
    void set_validate_handler(validate_handler h) {
        m_validate_handler = h;
    }

    /// Set message handler
    /**
     * The message handler is called after a new message has been received.
     *
     * @param h The new message_handler
     */
    void set_message_handler(message_handler h) {
        m_message_handler = h;
    }

    //////////////////////////////////////////
    // Connection timeouts and other limits //
    //////////////////////////////////////////

    /// Set open handshake timeout
    /**
     * Sets the length of time the library will wait after an opening handshake
     * has been initiated before cancelling it. This can be used to prevent
     * excessive wait times for outgoing clients or excessive resource usage
     * from broken clients or DoS attacks on servers.
     *
     * Connections that time out will have their fail handlers called with the
     * open_handshake_timeout error code.
     *
     * The default value is specified via the compile time config value
     * 'timeout_open_handshake'. The default value in the core config
     * is 5000ms. A value of 0 will disable the timer entirely.
     *
     * To be effective, the transport you are using must support timers. See
     * the documentation for your transport policy for details about its
     * timer support.
     *
     * @param dur The length of the open handshake timeout in ms
     */
    void set_open_handshake_timeout(long dur) {
        m_open_handshake_timeout_dur = dur;
    }

    /// Set close handshake timeout
    /**
     * Sets the length of time the library will wait after a closing handshake
     * has been initiated before cancelling it. This can be used to prevent
     * excessive wait times for outgoing clients or excessive resource usage
     * from broken clients or DoS attacks on servers.
     *
     * Connections that time out will have their close handlers called with the
     * close_handshake_timeout error code.
     *
     * The default value is specified via the compile time config value
     * 'timeout_close_handshake'. The default value in the core config
     * is 5000ms. A value of 0 will disable the timer entirely.
     *
     * To be effective, the transport you are using must support timers. See
     * the documentation for your transport policy for details about its
     * timer support.
     *
     * @param dur The length of the close handshake timeout in ms
     */
    void set_close_handshake_timeout(long dur) {
        m_close_handshake_timeout_dur = dur;
    }

    /// Set pong timeout
    /**
     * Sets the length of time the library will wait for a pong response to a
     * ping. This can be used as a keepalive or to detect broken  connections.
     *
     * Pong responses that time out will have the pong timeout handler called.
     *
     * The default value is specified via the compile time config value
     * 'timeout_pong'. The default value in the core config
     * is 5000ms. A value of 0 will disable the timer entirely.
     *
     * To be effective, the transport you are using must support timers. See
     * the documentation for your transport policy for details about its
     * timer support.
     *
     * @param dur The length of the pong timeout in ms
     */
    void set_pong_timeout(long dur) {
        m_pong_timeout_dur = dur;
    }

    /// Get maximum message size
    /**
     * Get maximum message size. Maximum message size determines the point at
     * which the connection will fail with the message_too_big protocol error.
     *
     * The default is set by the endpoint that creates the connection.
     *
     * @since 0.3.0
     */
    size_t get_max_message_size() const {
        return m_max_message_size;
    }

    /// Set maximum message size
    /**
     * Set maximum message size. Maximum message size determines the point at
     * which the connection will fail with the message_too_big protocol error.
     * This value may be changed during the connection.
     *
     * The default is set by the endpoint that creates the connection.
     *
     * @since 0.3.0
     *
     * @param new_value The value to set as the maximum message size.
     */
    void set_max_message_size(size_t new_value) {
        m_max_message_size = new_value;
        if (m_processor) {
            m_processor->set_max_message_size(new_value);
        }
    }

    /// Get maximum HTTP message body size
    /**
     * Get maximum HTTP message body size. Maximum message body size determines
     * the point at which the connection will stop reading an HTTP request whose
     * body is too large.
     *
     * The default is set by the endpoint that creates the connection.
     *
     * @since 0.5.0
     *
     * @return The maximum HTTP message body size
     */
    size_t get_max_http_body_size() const {
        return m_request.get_max_body_size();
    }

    /// Set maximum HTTP message body size
    /**
     * Set maximum HTTP message body size. Maximum message body size determines
     * the point at which the connection will stop reading an HTTP request whose
     * body is too large.
     *
     * The default is set by the endpoint that creates the connection.
     *
     * @since 0.5.0
     *
     * @param new_value The value to set as the maximum message size.
     */
    void set_max_http_body_size(size_t new_value) {
        m_request.set_max_body_size(new_value);
    }

    //////////////////////////////////
    // Uncategorized public methods //
    //////////////////////////////////

    /// Get the size of the outgoing write buffer (in payload bytes)
    /**
     * Retrieves the number of bytes in the outgoing write buffer that have not
     * already been dispatched to the transport layer. This represents the bytes
     * that are presently cancelable without uncleanly ending the websocket
     * connection
     *
     * This method invokes the m_write_lock mutex
     *
     * @return The current number of bytes in the outgoing send buffer.
     */
    size_t get_buffered_amount() const;

    /// Get the size of the outgoing write buffer (in payload bytes)
    /**
     * @deprecated use `get_buffered_amount` instead
     */
    size_t buffered_amount() const {
        return get_buffered_amount();
    }

    ////////////////////
    // Action Methods //
    ////////////////////

    /// Create a message and then add it to the outgoing send queue
    /**
     * Convenience method to send a message given a payload string and
     * optionally an opcode. Default opcode is utf8 text.
     *
     * This method locks the m_write_lock mutex
     *
     * @param payload The payload string to generated the message with
     *
     * @param op The opcode to generated the message with. Default is
     * frame::opcode::text
     */
    lib::error_code send(std::string const & payload, frame::opcode::value op =
        frame::opcode::text);

    /// Send a message (raw array overload)
    /**
     * Convenience method to send a message given a raw array and optionally an
     * opcode. Default opcode is binary.
     *
     * This method locks the m_write_lock mutex
     *
     * @param payload A pointer to the array containing the bytes to send.
     *
     * @param len Length of the array.
     *
     * @param op The opcode to generated the message with. Default is
     * frame::opcode::binary
     */
    lib::error_code send(void const * payload, size_t len, frame::opcode::value
        op = frame::opcode::binary);

    /// Add a message to the outgoing send queue
    /**
     * If presented with a prepared message it is added without validation or
     * framing. If presented with an unprepared message it is validated, framed,
     * and then added
     *
     * Errors are returned via an exception
     * \todo make exception system_error rather than error_code
     *
     * This method invokes the m_write_lock mutex
     *
     * @param msg A message_ptr to the message to send.
     */
    lib::error_code send(message_ptr msg);

    /// Asyncronously invoke handler::on_inturrupt
    /**
     * Signals to the connection to asyncronously invoke the on_inturrupt
     * callback for this connection's handler once it is safe to do so.
     *
     * When the on_inturrupt handler callback is called it will be from
     * within the transport event loop with all the thread safety features
     * guaranteed by the transport to regular handlers
     *
     * Multiple inturrupt signals can be active at once on the same connection
     *
     * @return An error code
     */
    lib::error_code interrupt();

    /// Transport inturrupt callback
    void handle_interrupt();

    /// Pause reading of new data
    /**
     * Signals to the connection to halt reading of new data. While reading is paused,
     * the connection will stop reading from its associated socket. In turn this will
     * result in TCP based flow control kicking in and slowing data flow from the remote
     * endpoint.
     *
     * This is useful for applications that push new requests to a queue to be processed
     * by another thread and need a way to signal when their request queue is full without
     * blocking the network processing thread.
     *
     * Use `resume_reading()` to resume.
     *
     * If supported by the transport this is done asynchronously. As such reading may not
     * stop until the current read operation completes. Typically you can expect to
     * receive no more bytes after initiating a read pause than the size of the read
     * buffer.
     *
     * If reading is paused for this connection already nothing is changed.
     */
    lib::error_code pause_reading();

    /// Pause reading callback
    void handle_pause_reading();

    /// Resume reading of new data
    /**
     * Signals to the connection to resume reading of new data after it was paused by
     * `pause_reading()`.
     *
     * If reading is not paused for this connection already nothing is changed.
     */
    lib::error_code resume_reading();

    /// Resume reading callback
    void handle_resume_reading();

    /// Send a ping
    /**
     * Initiates a ping with the given payload/
     *
     * There is no feedback directly from ping except in cases of immediately
     * detectable errors. Feedback will be provided via on_pong or
     * on_pong_timeout callbacks.
     *
     * Ping locks the m_write_lock mutex
     *
     * @param payload Payload to be used for the ping
     */
    void ping(std::string const & payload);

    /// exception free variant of ping
    void ping(std::string const & payload, lib::error_code & ec);

    /// Utility method that gets called back when the ping timer expires
    void handle_pong_timeout(std::string payload, lib::error_code const & ec);

    /// Send a pong
    /**
     * Initiates a pong with the given payload.
     *
     * There is no feedback from a pong once sent.
     *
     * Pong locks the m_write_lock mutex
     *
     * @param payload Payload to be used for the pong
     */
    void pong(std::string const & payload);

    /// exception free variant of pong
    void pong(std::string const & payload, lib::error_code & ec);

    /// Close the connection
    /**
     * Initiates the close handshake process.
     *
     * If close returns successfully the connection will be in the closing
     * state and no additional messages may be sent. All messages sent prior
     * to calling close will be written out before the connection is closed.
     *
     * If no reason is specified none will be sent. If no code is specified
     * then no code will be sent.
     *
     * The handler's on_close callback will be called once the close handshake
     * is complete.
     *
     * Reasons will be automatically truncated to the maximum length (123 bytes)
     * if necessary.
     *
     * @param code The close code to send
     * @param reason The close reason to send
     */
    void close(close::status::value const code, std::string const & reason);

    /// exception free variant of close
    void close(close::status::value const code, std::string const & reason,
        lib::error_code & ec);

    ////////////////////////////////////////////////
    // Pass-through access to the uri information //
    ////////////////////////////////////////////////

    /// Returns the secure flag from the connection URI
    /**
     * This value is available after the HTTP request has been fully read and
     * may be called from any thread.
     *
     * @return Whether or not the connection URI is flagged secure.
     */
    bool get_secure() const;

    /// Returns the host component of the connection URI
    /**
     * This value is available after the HTTP request has been fully read and
     * may be called from any thread.
     *
     * @return The host component of the connection URI
     */
    std::string const & get_host() const;

    /// Returns the resource component of the connection URI
    /**
     * This value is available after the HTTP request has been fully read and
     * may be called from any thread.
     *
     * @return The resource component of the connection URI
     */
    std::string const & get_resource() const;

    /// Returns the port component of the connection URI
    /**
     * This value is available after the HTTP request has been fully read and
     * may be called from any thread.
     *
     * @return The port component of the connection URI
     */
    uint16_t get_port() const;

    /// Gets the connection URI
    /**
     * This should really only be called by internal library methods unless you
     * really know what you are doing.
     *
     * @return A pointer to the connection's URI
     */
    uri_ptr get_uri() const;

    /// Sets the connection URI
    /**
     * This should really only be called by internal library methods unless you
     * really know what you are doing.
     *
     * @param uri The new URI to set
     */
    void set_uri(uri_ptr uri);

    /////////////////////////////
    // Subprotocol negotiation //
    /////////////////////////////

    /// Gets the negotated subprotocol
    /**
     * Retrieves the subprotocol that was negotiated during the handshake. This
     * method is valid in the open handler and later.
     *
     * @return The negotiated subprotocol
     */
    std::string const & get_subprotocol() const;

    /// Gets all of the subprotocols requested by the client
    /**
     * Retrieves the subprotocols that were requested during the handshake. This
     * method is valid in the validate handler and later.
     *
     * @return A vector of the requested subprotocol
     */
    std::vector<std::string> const & get_requested_subprotocols() const;

    /// Adds the given subprotocol string to the request list (exception free)
    /**
     * Adds a subprotocol to the list to send with the opening handshake. This
     * may be called multiple times to request more than one. If the server
     * supports one of these, it may choose one. If so, it will return it
     * in it's handshake reponse and the value will be available via
     * get_subprotocol(). Subprotocol requests should be added in order of
     * preference.
     *
     * @param request The subprotocol to request
     * @param ec A reference to an error code that will be filled in the case of
     * errors
     */
    void add_subprotocol(std::string const & request, lib::error_code & ec);

    /// Adds the given subprotocol string to the request list
    /**
     * Adds a subprotocol to the list to send with the opening handshake. This
     * may be called multiple times to request more than one. If the server
     * supports one of these, it may choose one. If so, it will return it
     * in it's handshake reponse and the value will be available via
     * get_subprotocol(). Subprotocol requests should be added in order of
     * preference.
     *
     * @param request The subprotocol to request
     */
    void add_subprotocol(std::string const & request);

    /// Select a subprotocol to use (exception free)
    /**
     * Indicates which subprotocol should be used for this connection. Valid
     * only during the validate handler callback. Subprotocol selected must have
     * been requested by the client. Consult get_requested_subprotocols() for a
     * list of valid subprotocols.
     *
     * This member function is valid on server endpoints/connections only
     *
     * @param value The subprotocol to select
     * @param ec A reference to an error code that will be filled in the case of
     * errors
     */
    void select_subprotocol(std::string const & value, lib::error_code & ec);

    /// Select a subprotocol to use
    /**
     * Indicates which subprotocol should be used for this connection. Valid
     * only during the validate handler callback. Subprotocol selected must have
     * been requested by the client. Consult get_requested_subprotocols() for a
     * list of valid subprotocols.
     *
     * This member function is valid on server endpoints/connections only
     *
     * @param value The subprotocol to select
     */
    void select_subprotocol(std::string const & value);

    /////////////////////////////////////////////////////////////
    // Pass-through access to the request and response objects //
    /////////////////////////////////////////////////////////////

    /// Retrieve a request header
    /**
     * Retrieve the value of a header from the handshake HTTP request.
     *
     * @param key Name of the header to get
     * @return The value of the header
     */
    std::string const & get_request_header(std::string const & key) const;

    /// Retrieve a request body
    /**
     * Retrieve the value of the request body. This value is typically used with
     * PUT and POST requests to upload files or other data. Only HTTP
     * connections will ever have bodies. WebSocket connection's will always
     * have blank bodies.
     *
     * @return The value of the request body.
     */
    std::string const & get_request_body() const;

    /// Retrieve a response header
    /**
     * Retrieve the value of a header from the handshake HTTP request.
     *
     * @param key Name of the header to get
     * @return The value of the header
     */
    std::string const & get_response_header(std::string const & key) const;

    /// Get response HTTP status code
    /**
     * Gets the response status code
     *
     * @since 0.7.0
     *
     * @return The response status code sent
     */
    http::status_code::value get_response_code() const {
        return m_response.get_status_code();
    }

    /// Get response HTTP status message
    /**
     * Gets the response status message
     *
     * @since 0.7.0
     *
     * @return The response status message sent
     */
    std::string const & get_response_msg() const {
        return m_response.get_status_msg();
    }

    /// Set response status code and message
    /**
     * Sets the response status code to `code` and looks up the corresponding
     * message for standard codes. Non-standard codes will be entered as Unknown
     * use set_status(status_code::value,std::string) overload to set both
     * values explicitly.
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks.
     *
     * @param code Code to set
     * @param msg Message to set
     * @see websocketpp::http::response::set_status
     */
    void set_status(http::status_code::value code);

    /// Set response status code and message
    /**
     * Sets the response status code and message to independent custom values.
     * use set_status(status_code::value) to set the code and have the standard
     * message be automatically set.
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks.
     *
     * @param code Code to set
     * @param msg Message to set
     * @see websocketpp::http::response::set_status
     */
    void set_status(http::status_code::value code, std::string const & msg);

    /// Set response body content
    /**
     * Set the body content of the HTTP response to the parameter string. Note
     * set_body will also set the Content-Length HTTP header to the appropriate
     * value. If you want the Content-Length header to be something else set it
     * to something else after calling set_body
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks.
     *
     * @param value String data to include as the body content.
     * @see websocketpp::http::response::set_body
     */
    void set_body(std::string const & value);

    /// Append a header
    /**
     * If a header with this name already exists the value will be appended to
     * the existing header to form a comma separated list of values. Use
     * `connection::replace_header` to overwrite existing values.
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks, or to a client connection before connect has been called.
     *
     * @param key Name of the header to set
     * @param val Value to add
     * @see replace_header
     * @see websocketpp::http::parser::append_header
     */
    void append_header(std::string const & key, std::string const & val);

    /// Replace a header
    /**
     * If a header with this name already exists the old value will be replaced
     * Use `connection::append_header` to append to a list of existing values.
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks, or to a client connection before connect has been called.
     *
     * @param key Name of the header to set
     * @param val Value to set
     * @see append_header
     * @see websocketpp::http::parser::replace_header
     */
    void replace_header(std::string const & key, std::string const & val);

    /// Remove a header
    /**
     * Removes a header from the response.
     *
     * This member function is valid only from the http() and validate() handler
     * callbacks, or to a client connection before connect has been called.
     *
     * @param key The name of the header to remove
     * @see websocketpp::http::parser::remove_header
     */
    void remove_header(std::string const & key);

    /// Get request object
    /**
     * Direct access to request object. This can be used to call methods of the
     * request object that are not part of the standard request API that
     * connection wraps.
     *
     * Note use of this method involves using behavior specific to the
     * configured HTTP policy. Such behavior may not work with alternate HTTP
     * policies.
     *
     * @since 0.3.0-alpha3
     *
     * @return A const reference to the raw request object
     */
    request_type const & get_request() const {
        return m_request;
    }

    /// Get response object
    /**
     * Direct access to the HTTP response sent or received as a part of the
     * opening handshake. This can be used to call methods of the response
     * object that are not part of the standard request API that connection
     * wraps.
     *
     * Note use of this method involves using behavior specific to the
     * configured HTTP policy. Such behavior may not work with alternate HTTP
     * policies.
     *
     * @since 0.7.0
     *
     * @return A const reference to the raw response object
     */
    response_type const & get_response() const {
        return m_response;
    }

    /// Defer HTTP Response until later (Exception free)
    /**
     * Used in the http handler to defer the HTTP response for this connection
     * until later. Handshake timers will be canceled and the connection will be
     * left open until `send_http_response` or an equivalent is called.
     *
     * Warning: deferred connections won't time out and as a result can tie up
     * resources.
     *
     * @since 0.6.0
     *
     * @return A status code, zero on success, non-zero otherwise
     */
    lib::error_code defer_http_response();

    /// Send deferred HTTP Response (exception free)
    /**
     * Sends an http response to an HTTP connection that was deferred. This will
     * send a complete response including all headers, status line, and body
     * text. The connection will be closed afterwards.
     *
     * @since 0.6.0
     *
     * @param ec A status code, zero on success, non-zero otherwise
     */
    void send_http_response(lib::error_code & ec);

    /// Send deferred HTTP Response
    void send_http_response();

    // TODO HTTPNBIO: write_headers
    // function that processes headers + status so far and writes it to the wire
    // beginning the HTTP response body state. This method will ignore anything
    // in the response body.

    // TODO HTTPNBIO: write_body_message
    // queues the specified message_buffer for async writing

    // TODO HTTPNBIO: finish connection
    //

    // TODO HTTPNBIO: write_response
    // Writes the whole response, headers + body and closes the connection



    /////////////////////////////////////////////////////////////
    // Pass-through access to the other connection information //
    /////////////////////////////////////////////////////////////

    /// Get Connection Handle
    /**
     * The connection handle is a token that can be shared outside the
     * WebSocket++ core for the purposes of identifying a connection and
     * sending it messages.
     *
     * @return A handle to the connection
     */
    connection_hdl get_handle() const {
        return m_connection_hdl;
    }

    /// Get whether or not this connection is part of a server or client
    /**
     * @return whether or not the connection is attached to a server endpoint
     */
    bool is_server() const {
        return m_is_server;
    }

    /// Return the same origin policy origin value from the opening request.
    /**
     * This value is available after the HTTP request has been fully read and
     * may be called from any thread.
     *
     * @return The connection's origin value from the opening handshake.
     */
    std::string const & get_origin() const;

    /// Return the connection state.
    /**
     * Values can be connecting, open, closing, and closed
     *
     * @return The connection's current state.
     */
    session::state::value get_state() const;


    /// Get the WebSocket close code sent by this endpoint.
    /**
     * @return The WebSocket close code sent by this endpoint.
     */
    close::status::value get_local_close_code() const {
        return m_local_close_code;
    }

    /// Get the WebSocket close reason sent by this endpoint.
    /**
     * @return The WebSocket close reason sent by this endpoint.
     */
    std::string const & get_local_close_reason() const {
        return m_local_close_reason;
    }

    /// Get the WebSocket close code sent by the remote endpoint.
    /**
     * @return The WebSocket close code sent by the remote endpoint.
     */
    close::status::value get_remote_close_code() const {
        return m_remote_close_code;
    }

    /// Get the WebSocket close reason sent by the remote endpoint.
    /**
     * @return The WebSocket close reason sent by the remote endpoint.
     */
    std::string const & get_remote_close_reason() const {
        return m_remote_close_reason;
    }

    /// Get the internal error code for a closed/failed connection
    /**
     * Retrieves a machine readable detailed error code indicating the reason
     * that the connection was closed or failed. Valid only after the close or
     * fail handler is called.
     *
     * @return Error code indicating the reason the connection was closed or
     * failed
     */
    lib::error_code get_ec() const {
        return m_ec;
    }

    /// Get a message buffer
    /**
     * Warning: The API related to directly sending message buffers may change
     * before the 1.0 release. If you plan to use it, please keep an eye on any
     * breaking changes notifications in future release notes. Also if you have
     * any feedback about usage and capabilities now is a great time to provide
     * it.
     *
     * Message buffers are used to store message payloads and other message
     * metadata.
     *
     * The size parameter is a hint only. Your final payload does not need to
     * match it. There may be some performance benefits if the initial size
     * guess is equal to or slightly higher than the final payload size.
     *
     * @param op The opcode for the new message
     * @param size A hint to optimize the initial allocation of payload space.
     * @return A new message buffer
     */
    message_ptr get_message(websocketpp::frame::opcode::value op, size_t size)
        const
    {
        return m_msg_manager->get_message(op, size);
    }

    ////////////////////////////////////////////////////////////////////////
    // The remaining public member functions are for internal/policy use  //
    // only. Do not call from application code unless you understand what //
    // you are doing.                                                     //
    ////////////////////////////////////////////////////////////////////////



    void read_handshake(size_t num_bytes);

    void handle_read_handshake(lib::error_code const & ec,
        size_t bytes_transferred);
    void handle_read_http_response(lib::error_code const & ec,
        size_t bytes_transferred);


    void handle_write_http_response(lib::error_code const & ec);
    void handle_send_http_request(lib::error_code const & ec);

    void handle_open_handshake_timeout(lib::error_code const & ec);
    void handle_close_handshake_timeout(lib::error_code const & ec);

    void handle_read_frame(lib::error_code const & ec, size_t bytes_transferred);
    void read_frame();

    /// Get array of WebSocket protocol versions that this connection supports.
    std::vector<int> const & get_supported_versions() const;

    /// Sets the handler for a terminating connection. Should only be used
    /// internally by the endpoint class.
    void set_termination_handler(termination_handler new_handler);

    void terminate(lib::error_code const & ec);
    void handle_terminate(terminate_status tstat, lib::error_code const & ec);

    /// Checks if there are frames in the send queue and if there are sends one
    /**
     * \todo unit tests
     *
     * This method locks the m_write_lock mutex
     */
    void write_frame();

    /// Process the results of a frame write operation and start the next write
    /**
     * \todo unit tests
     *
     * This method locks the m_write_lock mutex
     *
     * @param terminate Whether or not to terminate the connection upon
     * completion of this write.
     *
     * @param ec A status code from the transport layer, zero on success,
     * non-zero otherwise.
     */
    void handle_write_frame(lib::error_code const & ec);
// protected:
    // This set of methods would really like to be protected, but doing so
    // requires that the endpoint be able to friend the connection. This is
    // allowed with C++11, but not prior versions

    /// Start the connection state machine
    void start();

    /// Set Connection Handle
    /**
     * The connection handle is a token that can be shared outside the
     * WebSocket++ core for the purposes of identifying a connection and
     * sending it messages.
     *
     * @param hdl A connection_hdl that the connection will use to refer
     * to itself.
     */
    void set_handle(connection_hdl hdl) {
        m_connection_hdl = hdl;
        transport_con_type::set_handle(hdl);
    }
protected:
    void handle_transport_init(lib::error_code const & ec);

    /// Set m_processor based on information in m_request. Set m_response
    /// status and return an error code indicating status.
    lib::error_code initialize_processor();

    /// Perform WebSocket handshake validation of m_request using m_processor.
    /// set m_response and return an error code indicating status.
    lib::error_code process_handshake_request();
private:


    /// Completes m_response, serializes it, and sends it out on the wire.
    void write_http_response(lib::error_code const & ec);

    /// Sends an opening WebSocket connect request
    void send_http_request();

    /// Alternate path for write_http_response in error conditions
    void write_http_response_error(lib::error_code const & ec);

    /// Process control message
    /**
     *
     */
    void process_control_frame(message_ptr msg);

    /// Send close acknowledgement
    /**
     * If no arguments are present no close code/reason will be specified.
     *
     * Note: the close code/reason values provided here may be overrided by
     * other settings (such as silent close).
     *
     * @param code The close code to send
     * @param reason The close reason to send
     * @return A status code, zero on success, non-zero otherwise
     */
    lib::error_code send_close_ack(close::status::value code =
        close::status::blank, std::string const & reason = std::string());

    /// Send close frame
    /**
     * If no arguments are present no close code/reason will be specified.
     *
     * Note: the close code/reason values provided here may be overrided by
     * other settings (such as silent close).
     *
     * The ack flag determines what to do in the case of a blank status and
     * whether or not to terminate the TCP connection after sending it.
     *
     * @param code The close code to send
     * @param reason The close reason to send
     * @param ack Whether or not this is an acknowledgement close frame
     * @return A status code, zero on success, non-zero otherwise
     */
    lib::error_code send_close_frame(close::status::value code =
        close::status::blank, std::string const & reason = std::string(), bool ack = false,
        bool terminal = false);

    /// Get a pointer to a new WebSocket protocol processor for a given version
    /**
     * @param version Version number of the WebSocket protocol to get a
     * processor for. Negative values indicate invalid/unknown versions and will
     * always return a null ptr
     *
     * @return A shared_ptr to a new instance of the appropriate processor or a
     * null ptr if there is no installed processor that matches the version
     * number.
     */
    processor_ptr get_processor(int version) const;

    /// Add a message to the write queue
    /**
     * Adds a message to the write queue and updates any associated shared state
     *
     * Must be called while holding m_write_lock
     *
     * @todo unit tests
     *
     * @param msg The message to push
     */
    void write_push(message_ptr msg);

    /// Pop a message from the write queue
    /**
     * Removes and returns a message from the write queue and updates any
     * associated shared state.
     *
     * Must be called while holding m_write_lock
     *
     * @todo unit tests
     *
     * @return the message_ptr at the front of the queue
     */
    message_ptr write_pop();

    /// Prints information about the incoming connection to the access log
    /**
     * Prints information about the incoming connection to the access log.
     * Includes: connection type, websocket version, remote endpoint, user agent
     * path, status code.
     */
    void log_open_result();

    /// Prints information about a connection being closed to the access log
    /**
     * Includes: local and remote close codes and reasons
     */
    void log_close_result();

    /// Prints information about a connection being failed to the access log
    /**
     * Includes: error code and message for why it was failed
     */
    void log_fail_result();

    /// Prints information about HTTP connections
    /**
     * Includes: TODO
     */
    void log_http_result();

    /// Prints information about an arbitrary error code on the specified channel
    template <typename error_type>
    void log_err(log::level l, char const * msg, error_type const & ec) {
        std::stringstream s;
        s << msg << " error: " << ec << " (" << ec.message() << ")";
        m_elog.write(l, s.str());
    }

    // internal handler functions
    read_handler            m_handle_read_frame;
    write_frame_handler     m_write_frame_handler;

    // static settings
    std::string const       m_user_agent;

    /// Pointer to the connection handle
    connection_hdl          m_connection_hdl;

    /// Handler objects
    open_handler            m_open_handler;
    close_handler           m_close_handler;
    fail_handler            m_fail_handler;
    ping_handler            m_ping_handler;
    pong_handler            m_pong_handler;
    pong_timeout_handler    m_pong_timeout_handler;
    interrupt_handler       m_interrupt_handler;
    http_handler            m_http_handler;
    validate_handler        m_validate_handler;
    message_handler         m_message_handler;

    /// constant values
    long                    m_open_handshake_timeout_dur;
    long                    m_close_handshake_timeout_dur;
    long                    m_pong_timeout_dur;
    size_t                  m_max_message_size;

    /// External connection state
    /**
     * Lock: m_connection_state_lock
     */
    session::state::value   m_state;

    /// Internal connection state
    /**
     * Lock: m_connection_state_lock
     */
    istate_type             m_internal_state;

    mutable mutex_type      m_connection_state_lock;

    /// The lock used to protect the message queue
    /**
     * Serializes access to the write queue as well as shared state within the
     * processor.
     */
    mutex_type              m_write_lock;

    // connection resources
    char                    m_buf[config::connection_read_buffer_size];
    size_t                  m_buf_cursor;
    termination_handler     m_termination_handler;
    con_msg_manager_ptr     m_msg_manager;
    timer_ptr               m_handshake_timer;
    timer_ptr               m_ping_timer;

    /// @todo this is not memory efficient. this value is not used after the
    /// handshake.
    std::string m_handshake_buffer;

    /// Pointer to the processor object for this connection
    /**
     * The processor provides functionality that is specific to the WebSocket
     * protocol version that the client has negotiated. It also contains all of
     * the state necessary to encode and decode the incoming and outgoing
     * WebSocket byte streams
     *
     * Use of the prepare_data_frame method requires lock: m_write_lock
     */
    processor_ptr           m_processor;

    /// Queue of unsent outgoing messages
    /**
     * Lock: m_write_lock
     */
    std::queue<message_ptr> m_send_queue;

    /// Size in bytes of the outstanding payloads in the write queue
    /**
     * Lock: m_write_lock
     */
    size_t m_send_buffer_size;

    /// buffer holding the various parts of the current message being writen
    /**
     * Lock m_write_lock
     */
    std::vector<transport::buffer> m_send_buffer;

    /// a list of pointers to hold on to the messages being written to keep them
    /// from going out of scope before the write is complete.
    std::vector<message_ptr> m_current_msgs;

    /// True if there is currently an outstanding transport write
    /**
     * Lock m_write_lock
     */
    bool m_write_flag;

    /// True if this connection is presently reading new data
    bool m_read_flag;

    // connection data
    request_type            m_request;
    response_type           m_response;
    uri_ptr                 m_uri;
    std::string             m_subprotocol;

    // connection data that might not be necessary to keep around for the life
    // of the whole connection.
    std::vector<std::string> m_requested_subprotocols;

    bool const              m_is_server;
    alog_type& m_alog;
    elog_type& m_elog;

    rng_type & m_rng;

    // Close state
    /// Close code that was sent on the wire by this endpoint
    close::status::value    m_local_close_code;

    /// Close reason that was sent on the wire by this endpoint
    std::string             m_local_close_reason;

    /// Close code that was received on the wire from the remote endpoint
    close::status::value    m_remote_close_code;

    /// Close reason that was received on the wire from the remote endpoint
    std::string             m_remote_close_reason;

    /// Detailed internal error code
    lib::error_code m_ec;

    /// A flag that gets set once it is determined that the connection is an
    /// HTTP connection and not a WebSocket one.
    bool m_is_http;

    /// A flag that gets set when the completion of an http connection is
    /// deferred until later.
    session::http_state::value m_http_state;

    bool m_was_clean;

    /// Whether or not this endpoint initiated the closing handshake.
    bool                    m_closed_by_me;

    /// ???
    bool                    m_failed_by_me;

    /// Whether or not this endpoint initiated the drop of the TCP connection
    bool                    m_dropped_by_me;
};

} // namespace websocketpp

#include <websocketpp/impl/connection_impl.hpp>

#endif // WEBSOCKETPP_CONNECTION_HPP
