2 * Copyright (c) 2011, Peter Thorson. All rights reserved.
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.
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.
27 //#define BOOST_TEST_DYN_LINK
28 #define BOOST_TEST_MODULE connection
29 #include <boost/test/unit_test.hpp>
31 #include "connection_tu2.hpp"
33 // Include special debugging transport
34 //#include <websocketpp/config/minimal_client.hpp>
35 #include <websocketpp/transport/debug/endpoint.hpp>
37 // NOTE: these tests currently test against hardcoded output values. I am not
38 // sure how problematic this will be. If issues arise like order of headers the
39 // output should be parsed by http::response and have values checked directly
41 BOOST_AUTO_TEST_CASE( basic_http_request ) {
42 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
43 std::string output = "HTTP/1.1 426 Upgrade Required\r\nServer: " +
44 std::string(websocketpp::user_agent)+"\r\n\r\n";
46 std::string o2 = run_server_test(input);
48 BOOST_CHECK(o2 == output);
51 struct connection_extension {
52 connection_extension() : extension_value(5) {}
54 int extension_method() {
55 return extension_value;
58 bool is_server() const {
65 struct stub_config : public websocketpp::config::core {
66 typedef core::concurrency_type concurrency_type;
68 typedef core::request_type request_type;
69 typedef core::response_type response_type;
71 typedef core::message_type message_type;
72 typedef core::con_msg_manager_type con_msg_manager_type;
73 typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
75 typedef core::alog_type alog_type;
76 typedef core::elog_type elog_type;
78 typedef core::rng_type rng_type;
80 typedef core::transport_type transport_type;
82 typedef core::endpoint_base endpoint_base;
83 typedef connection_extension connection_base;
86 struct debug_config_client : public websocketpp::config::core {
87 typedef debug_config_client type;
89 typedef core::concurrency_type concurrency_type;
91 typedef core::request_type request_type;
92 typedef core::response_type response_type;
94 typedef core::message_type message_type;
95 typedef core::con_msg_manager_type con_msg_manager_type;
96 typedef core::endpoint_msg_manager_type endpoint_msg_manager_type;
98 typedef core::alog_type alog_type;
99 typedef core::elog_type elog_type;
101 typedef websocketpp::random::none::int_generator<uint32_t> rng_type;
103 struct transport_config {
104 typedef type::concurrency_type concurrency_type;
105 typedef type::elog_type elog_type;
106 typedef type::alog_type alog_type;
107 typedef type::request_type request_type;
108 typedef type::response_type response_type;
110 /// Controls compile time enabling/disabling of thread syncronization
111 /// code Disabling can provide a minor performance improvement to single
112 /// threaded applications
113 static bool const enable_multithreading = true;
115 /// Default timer values (in ms)
116 static const long timeout_socket_pre_init = 5000;
117 static const long timeout_proxy = 5000;
118 static const long timeout_socket_post_init = 5000;
119 static const long timeout_connect = 5000;
120 static const long timeout_socket_shutdown = 5000;
123 /// Transport Endpoint Component
124 typedef websocketpp::transport::debug::endpoint<transport_config>
127 typedef core::endpoint_base endpoint_base;
128 typedef connection_extension connection_base;
130 static const websocketpp::log::level elog_level = websocketpp::log::elevel::none;
131 static const websocketpp::log::level alog_level = websocketpp::log::alevel::none;
134 struct connection_setup {
135 connection_setup(bool p_is_server) : c(p_is_server, "", alog, elog, rng) {}
137 websocketpp::lib::error_code ec;
138 stub_config::alog_type alog;
139 stub_config::elog_type elog;
140 stub_config::rng_type rng;
141 websocketpp::connection<stub_config> c;
144 typedef websocketpp::client<debug_config_client> debug_client;
145 typedef websocketpp::server<debug_config_client> debug_server;
147 /*void echo_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
148 s->send(hdl, msg->get_payload(), msg->get_opcode());
151 void validate_func(server* s, websocketpp::connection_hdl hdl, message_ptr msg) {
152 s->send(hdl, msg->get_payload(), msg->get_opcode());
155 bool validate_set_ua(server* s, websocketpp::connection_hdl hdl) {
156 server::connection_ptr con = s->get_con_from_hdl(hdl);
157 con->replace_header("Server","foo");
161 void http_func(server* s, websocketpp::connection_hdl hdl) {
162 using namespace websocketpp::http;
164 server::connection_ptr con = s->get_con_from_hdl(hdl);
166 std::string res = con->get_resource();
169 con->set_status(status_code::ok);
171 BOOST_CHECK_EQUAL(con->get_response_code(), status_code::ok);
172 BOOST_CHECK_EQUAL(con->get_response_msg(), status_code::get_string(status_code::ok));
175 void defer_http_func(server* s, bool * deferred, websocketpp::connection_hdl hdl) {
178 server::connection_ptr con = s->get_con_from_hdl(hdl);
180 websocketpp::lib::error_code ec = con->defer_http_response();
181 BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code());
184 void check_on_fail(server* s, websocketpp::lib::error_code ec, bool & called,
185 websocketpp::connection_hdl hdl)
187 server::connection_ptr con = s->get_con_from_hdl(hdl);
189 BOOST_CHECK_EQUAL(ec, con->get_ec());
193 void on_open_print(server* s, websocketpp::connection_hdl hdl)
195 server::connection_ptr con = s->get_con_from_hdl(hdl);
197 std::cout << con->get_uri() << std::endl;
200 void fail_on_open(websocketpp::connection_hdl) {
203 void fail_on_http(websocketpp::connection_hdl) {
207 BOOST_AUTO_TEST_CASE( connection_extensions ) {
208 connection_setup env(true);
210 BOOST_CHECK( env.c.extension_value == 5 );
211 BOOST_CHECK( env.c.extension_method() == 5 );
213 BOOST_CHECK( env.c.is_server() == true );
216 BOOST_AUTO_TEST_CASE( basic_websocket_request ) {
217 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
218 std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: ";
219 output+=websocketpp::user_agent;
220 output+="\r\nUpgrade: websocket\r\n\r\n";
223 s.set_message_handler(bind(&echo_func,&s,::_1,::_2));
225 BOOST_CHECK(run_server_test(s,input) == output);
228 BOOST_AUTO_TEST_CASE( http_request ) {
229 std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
230 std::string output = "HTTP/1.1 200 OK\r\nContent-Length: 8\r\nServer: ";
231 output+=websocketpp::user_agent;
232 output+="\r\n\r\n/foo/bar";
235 s.set_http_handler(bind(&http_func,&s,::_1));
237 BOOST_CHECK_EQUAL(run_server_test(s,input), output);
240 BOOST_AUTO_TEST_CASE( deferred_http_request ) {
241 std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
242 std::string output = "HTTP/1.1 200 OK\r\nContent-Length: 8\r\nServer: ";
243 output+=websocketpp::user_agent;
244 output+="\r\n\r\n/foo/bar";
247 server::connection_ptr con;
248 bool deferred = false;
249 s.set_http_handler(bind(&defer_http_func,&s, &deferred,::_1));
251 s.clear_access_channels(websocketpp::log::alevel::all);
252 s.clear_error_channels(websocketpp::log::elevel::all);
254 std::stringstream ostream;
255 s.register_ostream(&ostream);
257 con = s.get_connection();
260 BOOST_CHECK(!deferred);
261 BOOST_CHECK_EQUAL(ostream.str(), "");
262 con->read_some(input.data(),input.size());
263 BOOST_CHECK(deferred);
264 BOOST_CHECK_EQUAL(ostream.str(), "");
266 con->set_body(con->get_resource());
267 con->set_status(websocketpp::http::status_code::ok);
269 websocketpp::lib::error_code ec;
270 s.send_http_response(con->get_handle(),ec);
271 BOOST_CHECK_EQUAL(ec, websocketpp::lib::error_code());
272 BOOST_CHECK_EQUAL(ostream.str(), output);
273 con->send_http_response(ec);
274 BOOST_CHECK_EQUAL(ec, make_error_code(websocketpp::error::invalid_state));
278 BOOST_AUTO_TEST_CASE( request_no_server_header ) {
279 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
280 std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nUpgrade: websocket\r\n\r\n";
283 s.set_user_agent("");
284 s.set_message_handler(bind(&echo_func,&s,::_1,::_2));
286 BOOST_CHECK_EQUAL(run_server_test(s,input), output);
289 BOOST_AUTO_TEST_CASE( request_no_server_header_override ) {
290 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
291 std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
294 s.set_user_agent("");
295 s.set_message_handler(bind(&echo_func,&s,::_1,::_2));
296 s.set_validate_handler(bind(&validate_set_ua,&s,::_1));
298 BOOST_CHECK_EQUAL(run_server_test(s,input), output);
301 BOOST_AUTO_TEST_CASE( basic_client_websocket ) {
302 std::string uri = "ws://localhost";
304 //std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
306 std::string ref = "GET / HTTP/1.1\r\nConnection: Upgrade\r\nFoo: Bar\r\nHost: localhost\r\nSec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==\r\nSec-WebSocket-Version: 13\r\nUpgrade: websocket\r\nUser-Agent: foo\r\n\r\n";
308 std::stringstream output;
311 e.set_access_channels(websocketpp::log::alevel::none);
312 e.set_error_channels(websocketpp::log::elevel::none);
313 e.set_user_agent("foo");
314 e.register_ostream(&output);
316 client::connection_ptr con;
317 websocketpp::lib::error_code ec;
318 con = e.get_connection(uri, ec);
319 con->append_header("Foo","Bar");
322 BOOST_CHECK_EQUAL(ref, output.str());
325 BOOST_AUTO_TEST_CASE( set_max_message_size ) {
326 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n\r\n";
328 // After the handshake, add a single frame with a message that is too long.
329 char frame0[10] = {char(0x82), char(0x83), 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01};
330 input.append(frame0, 10);
332 std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
334 // After the handshake, add a single frame with a close message with message too big
336 char frame1[4] = {char(0x88), 0x19, 0x03, char(0xf1)};
337 output.append(frame1, 4);
338 output.append("A message was too large");
341 s.set_user_agent("");
342 s.set_validate_handler(bind(&validate_set_ua,&s,::_1));
343 s.set_max_message_size(2);
345 BOOST_CHECK_EQUAL(run_server_test(s,input), output);
348 BOOST_AUTO_TEST_CASE( websocket_fail_parse_error ) {
349 std::string input = "asdf\r\n\r\n";
352 websocketpp::lib::error_code ec = make_error_code(websocketpp::error::http_parse_error);
354 s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
356 run_server_test(s,input,false);
360 BOOST_AUTO_TEST_CASE( websocket_fail_invalid_version ) {
361 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: foo\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
364 websocketpp::lib::error_code ec = make_error_code(websocketpp::error::invalid_version);
366 s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
368 run_server_test(s,input,false);
372 BOOST_AUTO_TEST_CASE( websocket_fail_unsupported_version ) {
373 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 12\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
376 websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
378 s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
380 run_server_test(s,input,false);
384 // BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri ) {
385 // std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
388 // websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
389 // bool called = false;
390 // s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
391 // s.set_open_handler(bind(&on_open_print,&s,::_1));
393 // std::cout << run_server_test(s,input,true) << std::endl;
394 // BOOST_CHECK(called);
397 // BOOST_AUTO_TEST_CASE( websocket_fail_invalid_uri_http ) {
398 // std::string input = "GET http://345.123.123.123/foo HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
401 // websocketpp::lib::error_code ec = make_error_code(websocketpp::error::unsupported_version);
402 // bool called = false;
403 // s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
404 // s.set_open_handler(bind(&on_open_print,&s,::_1));
406 // std::cout << run_server_test(s,input,true) << std::endl;
407 // BOOST_CHECK(called);
410 BOOST_AUTO_TEST_CASE( websocket_fail_upgrade_required ) {
411 std::string input = "GET /foo/bar HTTP/1.1\r\nHost: www.example.com\r\nOrigin: http://www.example.com\r\n\r\n";
414 websocketpp::lib::error_code ec = make_error_code(websocketpp::error::upgrade_required);
416 s.set_fail_handler(bind(&check_on_fail,&s,ec,websocketpp::lib::ref(called),::_1));
418 run_server_test(s,input,false);
422 // TODO: set max message size in client endpoint test case
423 // TODO: set max message size mid connection test case
424 // TODO: [maybe] set max message size in open handler
428 // BOOST_AUTO_TEST_CASE( user_reject_origin ) {
429 // std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example2.com\r\n\r\n";
430 // std::string output = "HTTP/1.1 403 Forbidden\r\nServer: "+websocketpp::USER_AGENT+"\r\n\r\n";
432 // BOOST_CHECK(run_server_test(input) == output);
435 // BOOST_AUTO_TEST_CASE( basic_text_message ) {
436 // std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\nOrigin: http://www.example.com\r\n\r\n";
438 // unsigned char frames[8] = {0x82,0x82,0xFF,0xFF,0xFF,0xFF,0xD5,0xD5};
439 // input.append(reinterpret_cast<char*>(frames),8);
441 // std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\nServer: "+websocketpp::USER_AGENT+"\r\nUpgrade: websocket\r\n\r\n**";
443 // BOOST_CHECK( run_server_test(input) == output);
451 BOOST_AUTO_TEST_CASE( client_handshake_timeout_race1 ) {
454 websocketpp::lib::error_code ec;
455 debug_client::connection_ptr con = c.get_connection("ws://localhost:9002", ec);
459 // This test the case where a handshake times out immediately before the
460 // handler that would have completed it gets invoked. This situation happens
461 // when clients are connecting to overloaded servers and on servers that are
465 con->expire_timer(websocketpp::lib::error_code());
466 // Fullfil the write to simulate the write completing immediately after
468 con->fullfil_write();
470 BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
473 BOOST_AUTO_TEST_CASE( client_handshake_timeout_race2 ) {
476 websocketpp::lib::error_code ec;
477 debug_client::connection_ptr con = c.get_connection("ws://localhost:9002", ec);
481 std::string output = "HTTP/1.1 101 Switching Protocols\r\nConnection: upgrade\r\nSec-WebSocket-Accept: ICX+Yqv66kxgM0FcWaLWlFLwTAI=\r\nServer: foo\r\nUpgrade: websocket\r\n\r\n";
483 // This test the case where a handshake times out immediately before the
484 // handler that would have completed it gets invoked. This situation happens
485 // when clients are connecting to overloaded servers and on servers that are
488 con->fullfil_write();
490 con->expire_timer(websocketpp::lib::error_code());
491 // Read valid handshake to simulate receiving the handshake response
492 // immediately after the timer expires
493 con->read_all(output.data(),output.size());
495 BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
498 BOOST_AUTO_TEST_CASE( server_handshake_timeout_race1 ) {
501 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==\r\n\r\n";
503 debug_server::connection_ptr con = s.get_connection();
506 con->expire_timer(websocketpp::lib::error_code());
507 // Read handshake immediately after timer expire
508 con->read_all(input.data(), input.size());
510 BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));
513 BOOST_AUTO_TEST_CASE( server_handshake_timeout_race2 ) {
516 std::string input = "GET / HTTP/1.1\r\nHost: www.example.com\r\nConnection: upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: AAAAAAAAAAAAAAAAAAAAAA==\r\n\r\n";
518 debug_server::connection_ptr con = s.get_connection();
521 con->read_all(input.data(), input.size());
523 con->expire_timer(websocketpp::lib::error_code());
524 // Complete write immediately after timer expire
525 con->fullfil_write();
527 BOOST_CHECK_EQUAL(con->get_ec(), make_error_code(websocketpp::error::open_handshake_timeout));