zaphoyd / websocketpp

C++ websocket client/server library
http://www.zaphoyd.com/websocketpp
Other
7.08k stars 1.98k forks source link

HTTP passthrough to another http server #266

Open pettno opened 11 years ago

pettno commented 11 years ago

We have an application which:

This enables us to serve both websockets and http on the same port.

Here is some working incomplete code:

void OurServerThing::on_http( websocketpp::connection_hdl Handler )
{
    // HTTP proxy to another web server
    using namespace std;
    using namespace boost;
    using namespace boost::asio::ip;
    try {
        char Buffer[1024];
        server::connection_ptr Connection = _WsppServer.get_con_from_hdl( Handler );
        const string & Host = Connection->get_host();
        const string & Resource = Connection->get_resource();
        const string & Auth = Connection->get_request_header( "Authorization" );
        tcp::socket & ServerSocket = Connection->get_socket();

        // Connect the HTTP client
        asio::io_service IoService;
        tcp::resolver Resolver( IoService );
        tcp::resolver::query Query( tcp::v4(), "127.0.0.1", "8080" );
        tcp::resolver::iterator Iterator = Resolver.resolve( Query );
        tcp::socket ClientSocket( IoService );
        asio::connect( ClientSocket, Iterator );

        // Request the resource
        string Request = "GET " + Resource + " HTTP/1.1\r\n";
        Request += "Host: " + Host + "\r\n";
        if( !Auth.empty() ) {
            Request += "Authorization: " + Auth + "\r\n";
        }
        Request += "User-Agent: WebSocket++ http proxy\r\n\r\n";
        ClientSocket.write_some( asio::buffer( Request, Request.length() ) );

        // Read response until an error occurs
        size_t Length;
        boost::system::error_code Error;
        do {
            Length = ClientSocket.read_some( asio::buffer( Buffer, sizeof(Buffer) ), Error );
            if( !Error ) {
                ServerSocket.write_some( asio::buffer( Buffer, Length ), Error );
            }
        } while( !Error );

        // Close the socket now to avoid prepended HTTP headers from WebSocket++
        ServerSocket.close();
    }
    catch( std::exception& e ) {
        cerr << "Exception: " << e.what() << endl;
    }
}

Suggestions or feature requests for WebSocket++ (if it is not possible to solve differently):

This issue is related to #152 and #164.

zaphoyd commented 11 years ago

This is a somewhat complicated issue. Here are my thoughts, both short and long term.

Short term: I've pushed an update that adds an accessor for the HTTP request object that WebSocket++ builds when it receives requests. This object is of a type defined by the HTTP policy set in the endpoint's config parameter.

When using the reference HTTP parser policy included with WebSocket++ the procedure for getting the raw request is now: con->get_request()->raw(). Note this will only include the headers. As noted in #181 the reference HTTP parser does not parse request bodies and the library itself is not set up to read request bytes beyond the headers.

Long term: Supporting HTTP pass through to another server is something that probably would benefit from better internal support. WebSocket++ core already supports forward HTTP proxies on the client role. Reverse proxy support on the server role would allow trivial forwarding of un-upgraded HTTP connections to other servers while not blocking the endpoint the way an http handler based solution would. This would also get around the need to manually close the socket in the handler (which the library is not expecting) and the issue of not being able to deal with request bodies.

I think this would be a better option long term than adding more HTTP circuitry to allow handlers to read bodies and close sockets, etc. Thoughts?

pettno commented 11 years ago

The short term solution is part of what we need for now. To access the request body, we would also need something like the data in m_buf and "size" in m_buf_cursor? This feels more or less like hacking into the library to steal data from its internals, though.

Your long term solution is indeed what we would prefer. This would allow us to listen on port 80 by the websocket serving application and forward the rest to the web server. IMHO: It would be a great feature to be able to add handling of websockets in front of (and transparent to) any type of "regular" web server like this.

pettno commented 10 years ago

This might be a duplicate of #181.

zaphoyd commented 9 years ago

Full support for this feature is still in the works. In the meantime some smaller improvements to the library have been made to make manually implementing it a little easier. Specifically access to HTTP request bodies and the ability to cleanly terminate the connection after an http handler without writing an http response.

See #181 for further discussion.

void on_http( websocketpp::connection_hdl hdl )
    server::connection_ptr con = endpoint.get_con_from_hdl( hdl );
    tcp::socket & server_socket = con->get_socket();
    // write what you need to to server_socket here
    con->terminate(websocketpp::error::make_error_code(websocketpp::error::http_connection_ended));
}