diff --git a/changelog.md b/changelog.md index 2e1e913388..ef53f53b28 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,7 @@ HEAD - Adds new 1012 and 1013 close codes per IANA registry +- Add `set_remote_endpoint` method to iostream transport. +- Add `set_secure` method to iostream transport. - Fix typo in .gitattributes file. Thank you jstarasov for reporting this. #280 - Add missing locale include. Thank you Toninoso for reporting this. #281 - Refactors `asio_transport` endpoint and adds full documentation and exception diff --git a/websocketpp/transport/base/connection.hpp b/websocketpp/transport/base/connection.hpp index cbc52a0419..1db6310d23 100644 --- a/websocketpp/transport/base/connection.hpp +++ b/websocketpp/transport/base/connection.hpp @@ -41,8 +41,16 @@ namespace websocketpp { * * Transport connection components needs to provide: * - * *Warning: This documentation section and the transport connection interface - * are not complete.* + * **init**\n + * `void init(init_handler handler)`\n + * Called once shortly after construction to give the policy the chance to + * perform one time initialization. When complete, the policy must call the + * supplied `init_handler` to continue setup. The handler takes one argument + * with the error code if any. If an error is returned here setup will fail and + * the connection will be aborted or terminated. + * + * WebSocket++ will call init only once. The transport must call `handler` + * exactly once. * * **async_read_at_least**\n * `void async_read_at_least(size_t num_bytes, char *buf, size_t len, @@ -52,7 +60,7 @@ namespace websocketpp { * * WebSocket++ promises to have only one async_read_at_least in flight at a * time. The transport must promise to only call read_handler once per async - * read + * read. * * **async_write**\n * `void async_write(const char* buf, size_t len, write_handler handler)`\n @@ -65,8 +73,28 @@ namespace websocketpp { * The transport must promise to only call the write_handler once per async * write * - * **remote_endpoint**\n - * `std::string remote_endpoint()`\n + * **set_handle**\n + * `void set_handle(connection_hdl hdl)`\n + * Called by WebSocket++ to let this policy know the hdl to the connection. It + * may be stored for later use or ignored/discarded. This handle should be used + * if the policy adds any connection handlers. Connection handlers must be + * called with the handle as the first argument so that the handler code knows + * which connection generated the callback. + * + * **set_timer**\n + * `timer_ptr set_timer(long duration, timer_handler handler)`\n + * WebSocket++ uses the timers provided by the transport policy as the + * implementation of timers is often highly coupled with the implementation of + * the networking event loops. + * + * Transport timer support is an optional feature. A transport method may elect + * to implement a dummy timer object and have this method return an empty + * pointer. If so, all timer related features of WebSocket++ core will be + * disabled. This includes many security features designed to prevent denial of + * service attacks. Use timer-free transport policies with caution. + * + * **get_remote_endpoint**\n + * `std::string get_remote_endpoint()`\n * retrieve address of remote endpoint * * **is_secure**\n @@ -75,7 +103,12 @@ namespace websocketpp { * * **dispatch**\n * `lib::error_code dispatch(dispatch_handler handler)`: invoke handler within - * the transport's event system if it uses one. + * the transport's event system if it uses one. Otherwise, this method should + * simply call `handler` immediately. + * + * **async_shutdown**\n + * `void async_shutdown(shutdown_handler handler)`\n + * Perform any cleanup necessary (if any). Call `handler` when complete. */ namespace transport { diff --git a/websocketpp/transport/base/endpoint.hpp b/websocketpp/transport/base/endpoint.hpp index 118c85d00c..df1dc45a59 100644 --- a/websocketpp/transport/base/endpoint.hpp +++ b/websocketpp/transport/base/endpoint.hpp @@ -49,6 +49,24 @@ namespace websocketpp { * It's purpose is to give the transport policy the chance to perform any * transport specific initialization that couldn't be done via the default * constructor. + * + * **is_secure**\n + * `bool is_secure() const`\n + * Test whether the transport component of this endpoint is capable of secure + * connections. + * + * **async_connect**\n + * `void async_connect(transport_con_ptr tcon, uri_ptr location, + * connect_handler handler)`\n + * Initiate a connection to `location` using the given connection `tcon`. `tcon` + * is a pointer to the transport connection component of the connection. When + * complete, `handler` should be called with the the connection's + * `connection_hdl` and any error that occurred. + * + * **init_logging** + * `void init_logging(alog_type * a, elog_type * e)`\n + * Called once after construction to provide pointers to the endpoint's access + * and error loggers. These may be stored and used to log messages or ignored. */ namespace transport { diff --git a/websocketpp/transport/iostream/connection.hpp b/websocketpp/transport/iostream/connection.hpp index 69faf49eb5..98443b4390 100644 --- a/websocketpp/transport/iostream/connection.hpp +++ b/websocketpp/transport/iostream/connection.hpp @@ -69,12 +69,14 @@ public: typedef lib::shared_ptr timer_ptr; - explicit connection(bool is_server, alog_type& alog, elog_type& elog) + explicit connection(bool is_server, alog_type & alog, elog_type & elog) : m_output_stream(NULL) , m_reading(false) , m_is_server(is_server) + , m_is_secure(false) , m_alog(alog) , m_elog(elog) + , m_remote_endpoint("iostream transport") { m_alog.write(log::alevel::devel,"iostream con transport constructor"); } @@ -86,7 +88,7 @@ public: * * @param o A pointer to the ostream to use for output. */ - void register_ostream(std::ostream* o) { + void register_ostream(std::ostream * o) { // TODO: lock transport state? scoped_lock_type lock(m_read_mutex); m_output_stream = o; @@ -111,7 +113,7 @@ public: * If there is no pending read operation when the input method is called, it * will return immediately and tellg() will not have changed. */ - friend std::istream& operator>> (std::istream &in, type &t) { + friend std::istream & operator>> (std::istream & in, type & t) { // this serializes calls to external read. scoped_lock_type lock(t.m_read_mutex); @@ -128,43 +130,82 @@ public: * pending reads readsome will return immediately. Not all of the bytes may * be able to be read in one call */ - size_t readsome(const char *buf, size_t len) { + size_t readsome(char const * buf, size_t len) { // this serializes calls to external read. scoped_lock_type lock(m_read_mutex); return this->readsome_impl(buf,len); } + /// Set whether or not this connection is secure + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not this connection is secure so that users of the WebSocket++ + * API will get more accurate information. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not this connection is secure. + */ + void set_secure(bool value) { + m_is_secure = value; + } /// Tests whether or not the underlying transport is secure /** * iostream transport will return false always because it has no information * about the ultimate remote endpoint. This may or may not be accurate - * depending on the real source of bytes being input. - * - * TODO: allow user settable is_secure flag if this seems useful + * depending on the real source of bytes being input. The `set_secure` + * method may be used to flag connections that are secured by an external + * API * * @return Whether or not the underlying transport is secure */ bool is_secure() const { - return false; + return m_is_secure; } - /// Get the remote endpoint address + /// Set human readable remote endpoint address + /** + * Sets the remote endpoint address returned by `get_remote_endpoint`. This + * value should be a human readable string that describes the remote + * endpoint. Typically an IP address or hostname, perhaps with a port. But + * may be something else depending on the nature of the underlying + * transport. + * + * If none is set the default is "iostream transport". + * + * @since 0.3.0-alpha4 + * + * @param value The remote endpoint address to set. + */ + void set_remote_endpoint(std::string value) { + m_remote_endpoint = value; + } + + /// Get human readable remote endpoint address /** * The iostream transport has no information about the ultimate remote - * endpoint. It will return the string "iostream transport". To indicate - * this. + * endpoint. It will return the string "iostream transport". The + * `set_remote_endpoint` method may be used by external network code to set + * a more accurate value. * - * TODO: allow user settable remote endpoint addresses if this seems useful + * This value is used in access and error logs and is available to the end + * application for including in user facing interfaces and messages. * * @return A string identifying the address of the remote endpoint */ std::string get_remote_endpoint() const { - return "iostream transport"; + return m_remote_endpoint; } /// Get the connection handle + /** + * @return The handle for this connection. + */ connection_hdl get_handle() const { return m_connection_hdl; } @@ -175,19 +216,23 @@ public: * always be empty. The handler will never be called. * * @param duration Length of time to wait in milliseconds - * * @param callback The function to call back when the timer has expired - * * @return A handle that can be used to cancel the timer if it is no longer * needed. */ - timer_ptr set_timer(long duration, timer_handler callback) { + timer_ptr set_timer(long duration, timer_handler handler) { return timer_ptr(); } protected: - void init(init_handler callback) { + /// Initialize the connection transport + /** + * Initialize the connection's transport component. + * + * @param handler The `init_handler` to call when initialization is done + */ + void init(init_handler handler) { m_alog.write(log::alevel::devel,"iostream connection init"); - callback(lib::error_code()); + handler(lib::error_code()); } /// Initiate an async_read for at least num_bytes bytes into buf @@ -209,11 +254,8 @@ protected: * * @param num_bytes Don't call handler until at least this many bytes have * been read. - * * @param buf The buffer to read bytes into - * * @param len The size of buf. At maximum, this many bytes will be read. - * * @param handler The callback to invoke when the operation is complete or * ends in an error */ @@ -261,7 +303,7 @@ protected: * @param len number of bytes to write * @param handler Callback to invoke with operation status. */ - void async_write(const char* buf, size_t len, write_handler handler) { + void async_write(char const * buf, size_t len, write_handler handler) { m_alog.write(log::alevel::devel,"iostream_con async_write"); // TODO: lock transport state? @@ -292,7 +334,7 @@ protected: * @param bufs vector of buffers to write * @param handler Callback to invoke with operation status. */ - void async_write(const std::vector& bufs, write_handler handler) { + void async_write(std::vector const & bufs, write_handler handler) { m_alog.write(log::alevel::devel,"iostream_con async_write buffer list"); // TODO: lock transport state? @@ -337,8 +379,12 @@ protected: return lib::error_code(); } - void async_shutdown(shutdown_handler h) { - h(lib::error_code()); + /// Perform cleanup on socket shutdown_handler + /** + * @param h The `shutdown_handler` to call back when complete + */ + void async_shutdown(shutdown_handler handler) { + handler(lib::error_code()); } private: void read(std::istream &in) { @@ -372,7 +418,7 @@ private: } } - size_t readsome_impl(const char * buf, size_t len) { + size_t readsome_impl(char const * buf, size_t len) { m_alog.write(log::alevel::devel,"iostream_con readsome"); if (!m_reading) { @@ -395,20 +441,22 @@ private: } // Read space (Protected by m_read_mutex) - char* m_buf; + char * m_buf; size_t m_len; size_t m_bytes_needed; read_handler m_read_handler; size_t m_cursor; // transport resources - std::ostream* m_output_stream; + std::ostream * m_output_stream; connection_hdl m_connection_hdl; bool m_reading; - const bool m_is_server; - alog_type& m_alog; - elog_type& m_elog; + bool const m_is_server; + bool m_is_secure; + alog_type & m_alog; + elog_type & m_elog; + std::string m_remote_endpoint; // This lock ensures that only one thread can edit read data for this // connection. This is a very coarse lock that is basically locked all the diff --git a/websocketpp/transport/iostream/endpoint.hpp b/websocketpp/transport/iostream/endpoint.hpp index 5784256997..91ea6fcf67 100644 --- a/websocketpp/transport/iostream/endpoint.hpp +++ b/websocketpp/transport/iostream/endpoint.hpp @@ -63,28 +63,56 @@ public: typedef typename transport_con_type::ptr transport_con_ptr; // generate and manage our own io_service - explicit endpoint() : m_output_stream(NULL) + explicit endpoint() : m_output_stream(NULL), m_is_secure(false) { //std::cout << "transport::iostream::endpoint constructor" << std::endl; } - void register_ostream(std::ostream* o) { + /// Register a default output stream + /** + * The specified output stream will be assigned to future connections as the + * default output stream. + * + * @param o The ostream to use as the default output stream. + */ + void register_ostream(std::ostream * o) { m_alog->write(log::alevel::devel,"register_ostream"); m_output_stream = o; } + /// Set whether or not endpoint can create secure connections + /** + * The iostream transport does not provide any security features. As such + * it defaults to returning false when `is_secure` is called. However, the + * iostream transport may be used to wrap an external socket API that may + * provide secure transport. This method allows that external API to flag + * whether or not it can create secure connections so that users of the + * WebSocket++ API will get more accurate information. + * + * Setting this value only indicates whether or not the endpoint is capable + * of producing and managing secure connections. Connections produced by + * this endpoint must also be individually flagged as secure if they are. + * + * @since 0.3.0-alpha4 + * + * @param value Whether or not the endpoint can create secure connections. + */ + void set_secure(bool value) { + m_is_secure = value; + } + /// Tests whether or not the underlying transport is secure /** - * iostream transport will return false always because it has no information - * about the ultimate remote endpoint. This may or may not be accurate - * depending on the real source of bytes being input. - * - * TODO: allow user settable is_secure flag if this seems useful + * iostream transport will return false by default because it has no + * information about the ultimate remote endpoint. This may or may not be + * accurate depending on the real source of bytes being input. `set_secure` + * may be used by a wrapper API to correct the return value in the case that + * secure connections are in fact possible. * * @return Whether or not the underlying transport is secure */ bool is_secure() const { - return false; + return m_is_secure; } protected: /// Initialize logging @@ -96,15 +124,23 @@ protected: * In particular, they cannot be used in the transport constructor as they * haven't been constructed yet, and cannot be used in the transport * destructor as they will have been destroyed by then. + * + * @param a A pointer to the access logger to use. + * @param e A pointer to the error logger to use. */ - void init_logging(alog_type* a, elog_type* e) { + void init_logging(alog_type * a, elog_type * e) { m_elog = e; m_alog = a; } /// Initiate a new connection + /** + * @param tcon A pointer to the transport connection component of the + * connection to connect. + * @param u A URI pointer to the URI to connect to. + * @param cb The function to call back with the results when complete. + */ void async_connect(transport_con_ptr tcon, uri_ptr u, connect_handler cb) { - // Do we need to do anything here? cb(tcon->get_handle(),lib::error_code()); } @@ -116,7 +152,6 @@ protected: * constructor. * * @param tcon A pointer to the transport portion of the connection. - * * @return A status code indicating the success or failure of the operation */ lib::error_code init(transport_con_ptr tcon) { @@ -124,9 +159,10 @@ protected: return lib::error_code(); } private: - std::ostream* m_output_stream; - elog_type* m_elog; - alog_type* m_alog; + std::ostream * m_output_stream; + elog_type * m_elog; + alog_type * m_alog; + bool m_is_secure; };