add simple test + set default http handler
This commit is contained in:
		@@ -97,7 +97,6 @@ namespace ix
 | 
			
		||||
        return std::make_tuple(true, "", httpRequest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // FIXME: Write a mime type
 | 
			
		||||
    bool Http::sendResponse(HttpResponsePtr response, std::shared_ptr<Socket> socket)
 | 
			
		||||
    {
 | 
			
		||||
        // Write the response to the socket
 | 
			
		||||
@@ -127,6 +126,8 @@ namespace ix
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return socket->writeBytes(response->payload, nullptr);
 | 
			
		||||
        return response->payload.empty() 
 | 
			
		||||
            ? true
 | 
			
		||||
            : socket->writeBytes(response->payload, nullptr);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,22 +11,45 @@
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace
 | 
			
		||||
{
 | 
			
		||||
    std::pair<bool, std::vector<uint8_t>> load(const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        std::vector<uint8_t> memblock;
 | 
			
		||||
 | 
			
		||||
        std::ifstream file(path);
 | 
			
		||||
        if (!file.is_open()) return std::make_pair(false, memblock);
 | 
			
		||||
 | 
			
		||||
        file.seekg(0, file.end);
 | 
			
		||||
        std::streamoff size = file.tellg();
 | 
			
		||||
        file.seekg(0, file.beg);
 | 
			
		||||
 | 
			
		||||
        memblock.resize((size_t) size);
 | 
			
		||||
        file.read((char*)&memblock.front(), static_cast<std::streamsize>(size));
 | 
			
		||||
 | 
			
		||||
        return std::make_pair(true, memblock);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::pair<bool, std::string> readAsString(const std::string& path)
 | 
			
		||||
    {
 | 
			
		||||
        auto res = load(path);
 | 
			
		||||
        auto vec = res.second;
 | 
			
		||||
        return std::make_pair(res.first, std::string(vec.begin(), vec.end()));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    const int HttpServer::kDefaultHandShakeTimeoutSecs(3); // 3 seconds
 | 
			
		||||
 | 
			
		||||
    HttpServer::HttpServer(int port,
 | 
			
		||||
                           const std::string& host,
 | 
			
		||||
                           int backlog,
 | 
			
		||||
                           size_t maxConnections,
 | 
			
		||||
                           int handshakeTimeoutSecs) : SocketServer(port, host, backlog, maxConnections),
 | 
			
		||||
        _handshakeTimeoutSecs(handshakeTimeoutSecs),
 | 
			
		||||
                           size_t maxConnections) : SocketServer(port, host, backlog, maxConnections),
 | 
			
		||||
        _connectedClientsCount(0)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        setDefaultConnectionCallback();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    HttpServer::~HttpServer()
 | 
			
		||||
@@ -56,16 +79,21 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
        std::string errorMsg;
 | 
			
		||||
        auto socket = createSocket(fd, errorMsg);
 | 
			
		||||
 | 
			
		||||
        // Set the socket to non blocking mode + other tweaks
 | 
			
		||||
        SocketConnect::configure(fd);
 | 
			
		||||
 | 
			
		||||
        auto ret = Http::parseRequest(socket);
 | 
			
		||||
        // FIXME: handle errors in parseRequest
 | 
			
		||||
 | 
			
		||||
        auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
 | 
			
		||||
        Http::sendResponse(response, socket);
 | 
			
		||||
 | 
			
		||||
        logInfo("HttpServer::handleConnection() done");
 | 
			
		||||
        if (std::get<0>(ret))
 | 
			
		||||
        {
 | 
			
		||||
            auto response = _onConnectionCallback(std::get<2>(ret), connectionState);
 | 
			
		||||
            if (!Http::sendResponse(response, socket))
 | 
			
		||||
            {
 | 
			
		||||
                logError("Cannot send response");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        connectionState->setTerminated();
 | 
			
		||||
 | 
			
		||||
        _connectedClientsCount--;
 | 
			
		||||
@@ -75,4 +103,49 @@ namespace ix
 | 
			
		||||
    {
 | 
			
		||||
        return _connectedClientsCount;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void HttpServer::setDefaultConnectionCallback()
 | 
			
		||||
    {
 | 
			
		||||
        setOnConnectionCallback(
 | 
			
		||||
            [this](HttpRequestPtr request,
 | 
			
		||||
                   std::shared_ptr<ConnectionState> /*connectionState*/) -> HttpResponsePtr
 | 
			
		||||
            {
 | 
			
		||||
                std::string path("." + request->uri);
 | 
			
		||||
                auto res = readAsString(path);
 | 
			
		||||
                bool found = res.first;
 | 
			
		||||
                if (!found)
 | 
			
		||||
                {
 | 
			
		||||
                    return std::make_shared<HttpResponse>(404, "Not Found",
 | 
			
		||||
                                                          HttpErrorCode::Ok,
 | 
			
		||||
                                                          WebSocketHttpHeaders(),
 | 
			
		||||
                                                          std::string());
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                std::string content = res.second;
 | 
			
		||||
 | 
			
		||||
                // Log request
 | 
			
		||||
                std::stringstream ss;
 | 
			
		||||
                ss << request->method 
 | 
			
		||||
                   << " "
 | 
			
		||||
                   << request->uri
 | 
			
		||||
                   << " "
 | 
			
		||||
                   << content.size();
 | 
			
		||||
                logInfo(ss.str());
 | 
			
		||||
 | 
			
		||||
                WebSocketHttpHeaders headers;
 | 
			
		||||
                headers["Content-Type"] = "application/octet-stream";
 | 
			
		||||
                headers["Accept-Ranges"] = "none";
 | 
			
		||||
 | 
			
		||||
                for (auto&& it : request->headers)
 | 
			
		||||
                {
 | 
			
		||||
                    headers[it.first] = it.second;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return std::make_shared<HttpResponse>(200, "OK",
 | 
			
		||||
                                                      HttpErrorCode::Ok,
 | 
			
		||||
                                                      WebSocketHttpHeaders(),
 | 
			
		||||
                                                      content);
 | 
			
		||||
            }
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,17 +19,16 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    using OnConnectionCallback =
 | 
			
		||||
        std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
    class HttpServer final : public SocketServer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        using OnConnectionCallback =
 | 
			
		||||
            std::function<HttpResponsePtr(HttpRequestPtr, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
        HttpServer(int port = SocketServer::kDefaultPort,
 | 
			
		||||
                   const std::string& host = SocketServer::kDefaultHost,
 | 
			
		||||
                   int backlog = SocketServer::kDefaultTcpBacklog,
 | 
			
		||||
                   size_t maxConnections = SocketServer::kDefaultMaxConnections,
 | 
			
		||||
                   int handshakeTimeoutSecs = HttpServer::kDefaultHandShakeTimeoutSecs);
 | 
			
		||||
                   size_t maxConnections = SocketServer::kDefaultMaxConnections);
 | 
			
		||||
        virtual ~HttpServer();
 | 
			
		||||
        virtual void stop() final;
 | 
			
		||||
 | 
			
		||||
@@ -37,15 +36,15 @@ namespace ix
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
        // Member variables
 | 
			
		||||
        int _handshakeTimeoutSecs;
 | 
			
		||||
        OnConnectionCallback _onConnectionCallback;
 | 
			
		||||
        const static int kDefaultHandShakeTimeoutSecs;
 | 
			
		||||
        std::atomic<int> _connectedClientsCount;
 | 
			
		||||
 | 
			
		||||
        // Methods
 | 
			
		||||
        virtual void handleConnection(int fd,
 | 
			
		||||
                                      std::shared_ptr<ConnectionState> connectionState) final;
 | 
			
		||||
        virtual size_t getConnectedClientsCount() final;
 | 
			
		||||
 | 
			
		||||
        void setDefaultConnectionCallback();
 | 
			
		||||
    };
 | 
			
		||||
} // namespace ix
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -232,19 +232,28 @@ namespace ix
 | 
			
		||||
    bool Socket::writeBytes(const std::string& str,
 | 
			
		||||
                            const CancellationRequest& isCancellationRequested)
 | 
			
		||||
    {
 | 
			
		||||
        char* buffer = const_cast<char*>(str.c_str());
 | 
			
		||||
        int len = (int) str.size();
 | 
			
		||||
 | 
			
		||||
        while (true)
 | 
			
		||||
        {
 | 
			
		||||
            if (isCancellationRequested && isCancellationRequested()) return false;
 | 
			
		||||
 | 
			
		||||
            char* buffer = const_cast<char*>(str.c_str());
 | 
			
		||||
            int len = (int) str.size();
 | 
			
		||||
 | 
			
		||||
            ssize_t ret = send(buffer, len);
 | 
			
		||||
 | 
			
		||||
            // We wrote some bytes, as needed, all good.
 | 
			
		||||
            if (ret > 0)
 | 
			
		||||
            {
 | 
			
		||||
                return ret == len;
 | 
			
		||||
                if (ret == len)
 | 
			
		||||
                {
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    buffer += ret;
 | 
			
		||||
                    len -= ret; 
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // There is possibly something to be writen, try again
 | 
			
		||||
            else if (ret < 0 && Socket::isWaitNeeded())
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,6 @@
 | 
			
		||||
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,12 +19,12 @@
 | 
			
		||||
 | 
			
		||||
namespace ix
 | 
			
		||||
{
 | 
			
		||||
    using OnConnectionCallback =
 | 
			
		||||
        std::function<void(std::shared_ptr<WebSocket>, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
    class WebSocketServer final : public SocketServer
 | 
			
		||||
    {
 | 
			
		||||
    public:
 | 
			
		||||
        using OnConnectionCallback =
 | 
			
		||||
            std::function<void(std::shared_ptr<WebSocket>, std::shared_ptr<ConnectionState>)>;
 | 
			
		||||
 | 
			
		||||
        WebSocketServer(int port = SocketServer::kDefaultPort,
 | 
			
		||||
                        const std::string& host = SocketServer::kDefaultHost,
 | 
			
		||||
                        int backlog = SocketServer::kDefaultTcpBacklog,
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user