// // Created by fred.nicolson on 01/10/18. // #include #include #include #include #include #include #include class SessionState { public: fr::HttpRequest partial_request; }; void process_complete_request(const std::shared_ptr &client, fr::HttpRequest request) { //Note: *NEVER* disconnect the client in the handler. Or it will never be removed from //the socket selector, and its opaque data will never be free'd. You're better off having a //disconnection queue which is processed by the listening thread, and added to here. fr::HttpResponse response; response.set_body("

Hello World!

"); client->send(response); } int main() { //Bind to port. Note that it is possible to fork/create new threads and then bind to the same //port multiple times. Each thread should have its own fr::SocketSelector, this will //spread connections over multiple workers. auto listener = std::make_shared(); if(listener->listen("8080") != fr::Socket::Status::Success) { std::cerr << "Failed to bind to port" << std::endl; return EXIT_FAILURE; } //Create a socket selector, and add the listener so we get notified on connection requests fr::SocketSelector listen_loop_selector; listen_loop_selector.add(listener, nullptr); bool running = true; while(running) { //Pass a timeout here, so that we can periodically check 'running' to see if we should exit auto ready_sockets = listen_loop_selector.wait(std::chrono::milliseconds(100)); for(auto &ready_socket : ready_sockets) { //If it's the listener, accept a new connection if(ready_socket.first->get_socket_descriptor() == listener->get_socket_descriptor()) { auto client = std::make_shared(); //Or fr::SSLSocket if(listener->accept(*client) == fr::Socket::Status::Success) { client->set_blocking(false); //This is important listen_loop_selector.add(client, new SessionState()); //We assign a 'SessionState' object for each connection as opaque data } continue; } //Else, we have new activity on a client socket auto client = std::static_pointer_cast(ready_socket.first); auto session = static_cast(ready_socket.second); //Try and receive data char data[0x1000]; size_t received = 0; auto recv_status = client->receive_raw(data, sizeof(data), received); if(recv_status == fr::Socket::Status::Success) { //We received data, so parse it using the partial HTTP request associated with this connection auto parse_status = session->partial_request.parse(data, received); if(parse_status == fr::Socket::Status::Success) { //The client has sent a full request, queue it for processing process_complete_request(client, std::move(session->partial_request)); session->partial_request = fr::HttpRequest(); } else if(parse_status != fr::Socket::Status::NotEnoughData) { //HTTP error, disconnect the client. Remove from socket selector, and delete opaque data. delete (SessionState*)listen_loop_selector.remove(client); } } else if(recv_status != fr::Socket::Status::WouldBlock) { //Error, disconnect it. Remove from socket selector, and delete opaque data. delete (SessionState*)listen_loop_selector.remove(client); } } } }