1 /*
2  * Copyright (c) 2014, Peter Thorson. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *     * Redistributions of source code must retain the above copyright
7  *       notice, this list of conditions and the following disclaimer.
8  *     * Redistributions in binary form must reproduce the above copyright
9  *       notice, this list of conditions and the following disclaimer in the
10  *       documentation and/or other materials provided with the distribution.
11  *     * Neither the name of the WebSocket++ Project nor the
12  *       names of its contributors may be used to endorse or promote products
13  *       derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
19  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  */
27 
28 #ifndef WEBSOCKETPP_CLIENT_ENDPOINT_HPP
29 #define WEBSOCKETPP_CLIENT_ENDPOINT_HPP
30 
31 #include <websocketpp/endpoint.hpp>
32 #include <websocketpp/uri.hpp>
33 
34 #include <websocketpp/logger/levels.hpp>
35 
36 #include <websocketpp/common/system_error.hpp>
37 
38 #include <string>
39 
40 namespace websocketpp {
41 
42 /// Client endpoint role based on the given config
43 /**
44  *
45  */
46 template <typename config>
47 class client : public endpoint<connection<config>,config> {
48 public:
49     /// Type of this endpoint
50     typedef client<config> type;
51 
52     /// Type of the endpoint concurrency component
53     typedef typename config::concurrency_type concurrency_type;
54     /// Type of the endpoint transport component
55     typedef typename config::transport_type transport_type;
56 
57     /// Type of the connections this server will create
58     typedef connection<config> connection_type;
59     /// Type of a shared pointer to the connections this server will create
60     typedef typename connection_type::ptr connection_ptr;
61 
62     /// Type of the connection transport component
63     typedef typename transport_type::transport_con_type transport_con_type;
64     /// Type of a shared pointer to the connection transport component
65     typedef typename transport_con_type::ptr transport_con_ptr;
66 
67     /// Type of the endpoint component of this server
68     typedef endpoint<connection_type,config> endpoint_type;
69 
70     friend class connection<config>;
71 
client()72     explicit client() : endpoint_type(false)
73     {
74         endpoint_type::m_alog.write(log::alevel::devel, "client constructor");
75     }
76 
77     /// Get a new connection
78     /**
79      * Creates and returns a pointer to a new connection to the given URI
80      * suitable for passing to connect(connection_ptr). This method allows
81      * applying connection specific settings before performing the opening
82      * handshake.
83      *
84      * @param [in] location URI to open the connection to as a uri_ptr
85      * @param [out] ec An status code indicating failure reasons, if any
86      *
87      * @return A connection_ptr to the new connection
88      */
get_connection(uri_ptr location,lib::error_code & ec)89     connection_ptr get_connection(uri_ptr location, lib::error_code & ec) {
90         if (location->get_secure() && !transport_type::is_secure()) {
91             ec = error::make_error_code(error::endpoint_not_secure);
92             return connection_ptr();
93         }
94 
95         connection_ptr con = endpoint_type::create_connection();
96 
97         if (!con) {
98             ec = error::make_error_code(error::con_creation_failed);
99             return con;
100         }
101 
102         con->set_uri(location);
103 
104         ec = lib::error_code();
105         return con;
106     }
107 
108     /// Get a new connection (string version)
109     /**
110      * Creates and returns a pointer to a new connection to the given URI
111      * suitable for passing to connect(connection_ptr). This overload allows
112      * default construction of the uri_ptr from a standard string.
113      *
114      * @param [in] u URI to open the connection to as a string
115      * @param [out] ec An status code indicating failure reasons, if any
116      *
117      * @return A connection_ptr to the new connection
118      */
get_connection(std::string const & u,lib::error_code & ec)119     connection_ptr get_connection(std::string const & u, lib::error_code & ec) {
120         uri_ptr location = lib::make_shared<uri>(u);
121 
122         if (!location->get_valid()) {
123             ec = error::make_error_code(error::invalid_uri);
124             return connection_ptr();
125         }
126 
127         return get_connection(location, ec);
128     }
129 
130     /// Begin the connection process for the given connection
131     /**
132      * Initiates the opening connection handshake for connection con. Exact
133      * behavior depends on the underlying transport policy.
134      *
135      * @param con The connection to connect
136      *
137      * @return The pointer to the connection originally passed in.
138      */
connect(connection_ptr con)139     connection_ptr connect(connection_ptr con) {
140         // Ask transport to perform a connection
141         transport_type::async_connect(
142             lib::static_pointer_cast<transport_con_type>(con),
143             con->get_uri(),
144             lib::bind(
145                 &type::handle_connect,
146                 this,
147                 con,
148                 lib::placeholders::_1
149             )
150         );
151 
152         return con;
153     }
154 private:
155     // handle_connect
handle_connect(connection_ptr con,lib::error_code const & ec)156     void handle_connect(connection_ptr con, lib::error_code const & ec) {
157         if (ec) {
158             con->terminate(ec);
159 
160             endpoint_type::m_elog.write(log::elevel::rerror,
161                     "handle_connect error: "+ec.message());
162         } else {
163             endpoint_type::m_alog.write(log::alevel::connect,
164                 "Successful connection");
165 
166             con->start();
167         }
168     }
169 };
170 
171 } // namespace websocketpp
172 
173 #endif //WEBSOCKETPP_CLIENT_ENDPOINT_HPP
174