adds HTTP request sending to connections

This commit is contained in:
Peter Thorson
2013-03-30 19:55:40 -05:00
parent cb6c396a6b
commit 67c740e264
3 changed files with 180 additions and 11 deletions

View File

@@ -30,9 +30,13 @@
#include <iostream>
#include <websocketpp/random/random_device.hpp>
#include <websocketpp/config/core.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/http/request.hpp>
struct stub_config : public websocketpp::config::core {
typedef core::concurrency_type concurrency_type;
@@ -46,7 +50,8 @@ struct stub_config : public websocketpp::config::core {
typedef core::alog_type alog_type;
typedef core::elog_type elog_type;
typedef core::rng_type rng_type;
//typedef core::rng_type rng_type;
typedef websocketpp::random::random_device::int_generator<uint32_t,concurrency_type> rng_type;
typedef core::transport_type transport_type;
@@ -86,3 +91,52 @@ BOOST_AUTO_TEST_CASE( get_connection ) {
BOOST_CHECK_EQUAL( con->get_secure() , false );
BOOST_CHECK_EQUAL( con->get_resource() , "/" );
}
BOOST_AUTO_TEST_CASE( connect_con ) {
client c;
websocketpp::lib::error_code ec;
std::stringstream out;
std::string o;
c.register_ostream(&out);
connection_ptr con = c.get_connection("ws://localhost/", ec);
c.connect(con);
o = out.str();
websocketpp::http::parser::request r;
r.consume(o.data(),o.size());
BOOST_CHECK( r.ready() );
BOOST_CHECK_EQUAL( r.get_method(), "GET");
BOOST_CHECK_EQUAL( r.get_version(), "HTTP/1.1");
BOOST_CHECK_EQUAL( r.get_uri(), "/");
BOOST_CHECK_EQUAL( r.get_header("Host"), "localhost");
BOOST_CHECK_EQUAL( r.get_header("Sec-WebSocket-Version"), "13");
BOOST_CHECK_EQUAL( r.get_header("Connection"), "Upgrade");
BOOST_CHECK_EQUAL( r.get_header("Upgrade"), "websocket");
// Key is randomly generated & User-Agent will change so just check that
// they are not empty.
BOOST_CHECK_NE( r.get_header("Sec-WebSocket-Key"), "");
BOOST_CHECK_NE( r.get_header("User-Agent"), "" );
// connection should have written out an opening handshake request and be in
// the read response internal state
std::cout << "output: " << out.str() << std::endl;
}
// test cases
// - adding headers
// - adding Upgrade header
// - adding Connection header
// - adding Sec-WebSocket-Version, Sec-WebSocket-Key, or Host header
// - other Sec* headers?
// - User Agent header?
// Origin support
// Subprotocol requests

View File

@@ -157,6 +157,7 @@ public:
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;
@@ -672,6 +673,7 @@ public:
size_t bytes_transferred);
void handle_send_http_response(const lib::error_code& ec);
void handle_send_http_request(const lib::error_code& ec);
/// Get array of WebSocket protocol versions that this connection supports.
@@ -757,6 +759,9 @@ private:
/// Completes m_response, serializes it, and sends it out on the wire.
void send_http_response();
/// Sends an opening WebSocket connect request
void send_http_request();
/// Alternate path for send_http_response in error conditions
void send_http_response_error();
@@ -893,7 +898,11 @@ private:
size_t m_buf_cursor;
termination_handler m_termination_handler;
con_msg_manager_ptr m_msg_manager;
// 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

View File

@@ -465,7 +465,10 @@ void connection<config>::handle_transport_init(const lib::error_code& ec) {
if (m_is_server) {
this->read(1);
} else {
// call prepare HTTP request
// We are a client. Set the processor to the version specified in the
// config file and send a handshake request.
m_processor = get_processor(config::client_version);
this->send_http_request();
}
// TODO: Begin websocket handshake
@@ -929,25 +932,23 @@ void connection<config>::send_http_response() {
// Set some common headers
m_response.replace_header("Server",m_user_agent);
std::string raw;
// have the processor generate the raw bytes for the wire (if it exists)
if (m_processor) {
raw = m_processor->get_raw(m_response);
m_handshake_buffer = m_processor->get_raw(m_response);
} else {
// a processor wont exist for raw HTTP responses.
raw = m_response.raw();
m_handshake_buffer = m_response.raw();
}
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+raw);
m_alog.write(log::alevel::devel,"Raw Handshake response:\n"+m_handshake_buffer);
}
// write raw bytes
transport_con_type::async_write(
raw.c_str(),
raw.size(),
m_handshake_buffer.data(),
m_handshake_buffer.size(),
lib::bind(
&type::handle_send_http_response,
type::shared_from_this(),
@@ -1009,6 +1010,111 @@ void connection<config>::handle_send_http_response(
this->handle_read_frame(lib::error_code(), m_buf_cursor);
}
template <typename config>
void connection<config>::send_http_request() {
m_alog.write(log::alevel::devel,"connection send_http_request");
// TODO: origin header
// TODO: subprotocol requests
// Generate client key
// set client key header
// Set User-Agent header
// Have the protocol processor fill in the appropriate fields based on the
// selected client version
if (m_processor) {
lib::error_code ec;
ec = m_processor->handshake_request(m_request,m_uri);
if (ec) {
m_elog.write(log::elevel::fatal,
"Internal library error: processor error: "+ec.message());
return;
}
} else {
m_elog.write(log::elevel::fatal,
"Internal library error: missing processor");
return;
}
// Unless the user has overridden the user agent, send generic WS++
if (m_request.get_header("User-Agent") == "") {
m_request.replace_header("User-Agent",m_user_agent);
}
m_handshake_buffer = m_request.raw();
if (m_alog.static_test(log::alevel::devel)) {
m_alog.write(log::alevel::devel,
"Raw Handshake response:\n"+m_handshake_buffer);
}
transport_con_type::async_write(
m_handshake_buffer.data(),
m_handshake_buffer.size(),
lib::bind(
&type::handle_send_http_request,
type::shared_from_this(),
lib::placeholders::_1
)
);
}
template <typename config>
void connection<config>::handle_send_http_request(const lib::error_code& ec) {
m_alog.write(log::alevel::devel,"handle_send_http_request");
/*this->atomic_state_check(
istate::PROCESS_HTTP_REQUEST,
"handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
);
if (ec) {
m_elog.write(log::elevel::rerror,
"error in handle_send_http_response: "+ec.message());
this->terminate();
return;
}
this->log_open_result();
if (m_response.get_status_code() != http::status_code::SWITCHING_PROTOCOLS)
{
if (m_processor) {
// if this was not a websocket connection, we have written
// the expected response and the connection can be closed.
} else {
// this was a websocket connection that ended in an error
std::stringstream s;
s << "Handshake ended with HTTP error: "
<< m_response.get_status_code();
m_elog.write(log::elevel::rerror,s.str());
}
this->terminate();
return;
}
// TODO: cancel handshake timer
this->atomic_state_change(
istate::PROCESS_HTTP_REQUEST,
istate::PROCESS_CONNECTION,
session::state::CONNECTING,
session::state::OPEN,
"handle_send_http_response must be called from PROCESS_HTTP_REQUEST state"
);
if (m_open_handler) {
m_open_handler(m_connection_hdl);
}
this->handle_read_frame(lib::error_code(), m_buf_cursor);*/
}
template <typename config>
void connection<config>::terminate() {
try {