🌐 English

    Web API#

    Oasis supports HTTP, HTTPS servers and clients.

    Header File#

    OasisWeb.h

    HttpUpStreamDelegate Interface#

    Oasis Web servers and clients process HTTP requests and responses through user-defined objects derived from the HttpUpStreamDelegate interface.

    class HttpUpStreamDelegate : public std::enable_shared_from_this<HttpUpStreamDelegate>
    {
    public:
        HttpUpStreamDelegate();
        virtual ~HttpUpStreamDelegate();
    
    public: 
        virtual void onRequest(const NetMessageRef &msg);
        virtual void onEarlyResponse(const NetMessageRef &msg); //when the response header is ready
        virtual void onResponse(const NetMessageRef &msg); //when the response header and contents ready
        virtual void onGatewayResponse(const struct sockaddr_in &local_addr, const struct sockaddr_in &remote_addr, int32_t status_code, const char *status_reason, const char *content_type, const char *content, size_t content_length, key_value_map_t &parameters);
    };
    
    void onRequest ( const NetMessageRef & msg )
    OasisWeb.h
    Called when a request NetMessage is received from a client.
    Parameters
    msg  The request NetMessage object.
    void onEarlyResponse ( const NetMessageRef & msg )
    OasisWeb.h
    Called when a response header NetMessage is received from a web server. Content is not yet received.
    Parameters
    msg  The response NetMessage object.
    void onResponse ( const NetMessageRef & msg )
    OasisWeb.h
    Called when a complete response NetMessage is received from a web server.
    Parameters
    msg  The response NetMessage object.
    void onGatewayResponse ( const struct sockaddr_in & local_addr , const struct sockaddr_in & remote_addr , int32_t status_code , const char * status_reason , const char * content_type , const char * content , size_t content_length , key_value_map_t & parameters )
    OasisWeb.h
    A callback function for relaying used in the DDS gateway web server. Not used in general web servers.
    Parameters
    local_addr  The local socket address bound when the local web server connects with a remote client.
    remote_addr  The socket address of the remote client.
    status_code  The response code.
    status_reason  The explanation of the status.
    content_type  The Content-Type of the response message. If there is no content, specify nullptr.
    content  The content of the response message. If there is no content, specify nullptr.
    content_length  The content size of the response message, measured in bytes. If there is no content, specify 0.
    parameters  Not used.

    Web Server Functions#

    HttpServerRef createHttpServer ( key_value_map_t & parameters , const std::shared_ptr<HttpUpStreamDelegate> & delegate )
    OasisWeb.h
    Creates an HTTP server object.
    Parameters
    parameters  The key-value map required to create the HTTP server object.
    delegate  The user-defined upstream delegate object for handling HTTP requests.
    Return Value
    Returns an HTTP server object on success. Returns nullptr on failure.
    Key
    Default
    M
    Description
    port
    80
     
    The port number of the web server.
    root-dir
    The root directory path where web server documents are located.
    remote-pool-size
    10
     
    The maximum number of allowed connections. Unlimited if "0".
    scheduler-size
    10
     
    The number of server scheduler tasks.
    upstreamer-size
    10
     
    The number of upstream tasks to deliver to the Delegate.
    transmitter-size
    10
     
    The number of transmission tasks.
    timer-size
    3
     
    The number of timer tasks.
    enable-stun
    0
     
    Enables STUN. Not used.
    stun-server-addr
     
    The STUN server address. Not used.
    stun-server-port
     
    The STUN server port. Not used.
    content-length-max
    1048576
     
    The maximum allowed length of HTTP content, measured in bytes.
    linger-time-max
    1000
     
    The maximum retention time after closing the connection. Measured in milliseconds.
    local-connection-only
    0
     
    Allows connections only from clients on the local network if "1".
    cache-dir
     
    The cache directory.
    cached-content-types
     
    The list of Content-Types to cache. A list of MIME type strings separated by commas (,).

    To enable HTTPS, the following key-value map is required:

    Key
    Default
    M
    Description
    tls-enabled
    0
    Specifies whether to enable TLS. It is enabled if "1", and TLS is not used if "0". When using it, key values such as certificate paths must be valid.
    tls-version
    1.2
    The TLS version. Enter "1.2".
    tls-client-verify
    0
     
    Specifies whether to validate the certificate of the connecting client. If "1", connections are allowed only for names registered in the tls-certificate-request-authority-names key.
    tls-certificate-request-authority-names
     
    The list of authority names (CN) separated by \0xa. For example, it is formatted like "MediaStek-Root-CA\x0aMediaStek Inc.".
    ca-certs-file-path
     
    The path to the ROOT CA certificate files in PEM format separated by \0xa. For example, it is formatted like "/data/certs/cacerts.pem\x0a/data/certs/RootCA.pem".
    cert-file-path
    The path to the public certificate file in PEM format such as RSA, ECDSA, DSS, etc.
    pkey-file-path
    The path to the private key file in PEM format.
    int32_t destroyHttpServer ( HttpServerRef & server )
    OasisWeb.h
    Releases the HttpServer.
    Parameters
    server  The HttpServer object created via createHttpServer.
    Return Value
    • 0: Success
    • -1: Failure
    int32_t startHttpServer ( const HttpServerRef & server )
    OasisWeb.h
    Starts the HttpServer.
    Parameters
    server  The HttpServer object created via createHttpServer.
    Return Value
    • 0: Success
    • -1: Failure
    int32_t stopHttpServer ( const HttpServerRef & server )
    OasisWeb.h
    Stops the HttpServer.
    Parameters
    server  The HttpServer object created via createHttpServer.
    Return Value
    • 0: Success
    • -1: Failure
    int32_t createHttpWsService ( const HttpServerRef & http_server , const char * url , key_value_map_t & parameters , const std::shared_ptr<WsEventDelegate> & delegate )
    OasisWeb.h
    Enables a websocket service at a specified URL of the HttpServer.
    Parameters
    http_server  The HttpServer object created via createHttpServer.
    url  The URL path of the websocket service.
    parameters  The key-value map required to create the websocket service.
    delegate  The user-defined object derived from WsEventDelegate for handling websocket events.
    Return Value
    • 0: Success
    • -1: Failure

    Below is the list of key-value maps required to create a websocket service.

    Key
    Default
    M
    Description
    websocket-protocols
     
    The protocol name defined by the user. If this field is not empty, the websocket connection must match this field value for the connection to be established.
    websocket-fragment-size-max
    65535
     
    The maximum fragment size.
    int32_t httpInterpretServerPage ( NetMessageRef & response , const char * url , const char * server_page_path , http_interpret_callback_t interpret_callback , void * callback_data , key_value_map_t * callback_arguments )
    OasisWeb.h
    Called when executing a server script callback in the HttpUpStreamDelegate::onRequest API, which is the request message handler of the web server. If there is a server script tagging like &lt;?=&lt;key_name>?> within a web page file, a user function defined as http_interpret_callback_t is called to obtain the value of &lt;key_name>. If the server_page_path file does not exist, it transmits a "400 Bad Request" response. On success, it transmits the content of the file along with a "200 OK" response code.
    Parameters
    response  The response NetMessage object.
    url  The URL path of this web document.
    server_page_path  The absolute path of this web document within the server file system.
    interpret_callback  The web server script callback function.
    callback_data  User-defined data to be passed to the web server script callback function.
    callback_arguments  The pointer to the key-value map to be passed to the web server script callback function.
    Return Value
    • 0: Success
    • -1: Failure

    The code below is an example of executing a web server script when the web document extension is osp.

    static int32_t httpInterpretCallback(const char *url, void *callback_data, key_value_map_t *callback_arguments, const char *key_name, std::string &output)
    {
        // Obtains the value corresponding to key_name and returns it to output.
        return 0;
    }
    
    void MyHttpConfigUpStreamDelegate::onRequest(const NetMessageRef &msg)
    {
        bool reply_now = true;
        std::string url;
        netMessageUriGetPath(msg, url);
    
        ASSERT(netMessageIsRequest(msg));
        if(!netMessageIsRequest(msg)) {
            return;
        }
    
        NetMessageRef res = netMessageCreate(msg, false);
        if(res == nullptr) {
            netMessageAbortConnection(msg);
            return;
        }
    
        netMessageSetStatusCodeAndReasonString(res, 200, "OK");
        netMessageSetHeaderField(res, "Connection", "close");
    
        if(url == "/") {
    
            std::string index_path = app().httpRootPath();
            index_path += "/index.osp";
            err = httpInterpretServerPage(res, url.c_str(), index_path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
            if(err < 0) {
                //do nothing
                netMessageAbortConnection(msg);
                return;
            }
        } else {
    
            std::string path = app().httpRootPath();
            path += url;
            if(access(path.c_str(), F_OK) == 0) {
                //get mime type based on extension
                const char *mime_type = get_file_mime_type(path.c_str());   
                if(mime_type == nullptr) {
                    //not supported
                    netMessageSetStatusCodeAndReasonString(res, 403, "Forbidden");
                } else {
                    std::string ext = get_file_ext(path);
                    if(ext == "osp") {
                        err = httpInterpretServerPage(res, url.c_str(), path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
                        if(err < 0) {
                            //do nothing
                            netMessageAbortConnection(msg);
                            return;
                        }
                    } else {
                        ssize_t ret = netMessageSendFile(res, path.c_str(), mime_type);
                        reply_now = false;
                    }
                }
            } else {
                netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
            }
        }
    
    done:
        if(reply_now) {
            netMessagePost(res);    
        }
    }
    

    Script Processing Callback#

    The Oasis web server can define elements for server scripts in the format of <?=<key_name>?> within web documents.

    The httpInterpretServerPage function parses this element from the web document and obtains the actual value corresponding to <key_name> by calling the http_interpret_callback_t callback function. It delivers the dynamically modified web document content to the client in this manner.

    typedef  int32_t  (*http_interpret_callback_t) ( const char * url , void * callback_data , key_value_map_t * callback_arguments , const char * key_name , std::string & output )
    OasisWeb.h
    Parameters
    url  The URL of the web document.
    callback_data  The user-defined data passed as a parameter when calling httpInterpretCallback.
    callback_arguments  The pointer to the key-value map passed as a parameter when calling httpInterpretCallback.
    key_name  The key name.
    output  OUT Returns the key value.
    Return Value
    • 0: Success
    • -1: Failure

    Below is an implementation example of http_interpret_callback_t.

    <p>
        The product name is <?=printProductName?>.
    </p>  
    <p>
        The IP address of the connected server is <?=printIPAddress?>.
    </p>
    
    static int32_t httpInterpretCallback(const char *url, void *callback_data, key_value_map_t *callback_arguments, const char *key_name, std::string &output)
    {
        std::string  _url(url);
        char value[256];
        int32_t err;
    
        output.clear();
    
        if(strcmp(key_name, "printProductName") == 0) {
            output = "Oasis";
        } else if(strcmp(key_name, "printIPAddress") == 0) {
            output = "127.0.0.1";
        }
    
        return 0;
    }
    

    Web Client Functions#

    HttpConnectionRef httpOpenConnection ( const char * url , int32_t timeout , const char * cache_directory = nullptr )
    OasisWeb.h
    Creates an HttpConnection object and connects to a web server.
    Parameters
    url  The URL of the web server to connect to.
    timeout  The maximum time to wait until connected. Measured in milliseconds.
    cache_directory  The path to the cache directory where temporary files will be saved.
    Return Value
    Returns an HttpConnection object on success and returns nullptr on failure.
    HttpConnectionRef httpOpenConnectionWithParameters ( const char * url , int32_t timeout , key_value_map_t & parameters , const char * cache_directory = nullptr )
    OasisWeb.h
    Creates an HttpConnection object and connects to a web server using a key-value map.
    Parameters
    url  The URL of the web server to connect to.
    timeout  The maximum time to wait until connected. Measured in milliseconds.
    parameters  The key-value map required for the connection.
    cache_directory  The path to the cache directory where temporary files will be saved.
    Return Value
    Returns an HttpConnection object on success and returns nullptr on failure.

    The key-value map required for the connection is as follows:

    Key
    Default
    M
    Description
    ca-certs-file-path
     
    The path to the ROOT CA certificate files in PEM format separated by \0xa. For example, it is formatted like "/data/certs/cacerts.pem\x0a/data/certs/RootCA.pem".
    cert-file-path
     
    The path to the public certificate file in PEM format such as RSA, ECDSA, DSS, etc.
    pkey-file-path
     
    The path to the private key file in PEM format.
    tls-disable-certificate-validation
    0
     
    Does not validate the certificate of the web server.
    tls-use-sni
    0
     
    Includes the FQDN of the server in the Hello Handshake when connecting to the server.
    int32_t httpOpenConnection ( const HttpConnectionRef & connection , const char * url , int32_t timeout )
    OasisWeb.h
    Connects to a web server. Verify the connection status using httpConnectionIsClosed, and if it is already in a connected state, call httpCloseConnection before using httpOpenConnection.
    Parameters
    connection  The HttpConnection object.
    url  The URL of the web server to connect to.
    timeout  The maximum time to wait until connected. Measured in milliseconds.
    Return Value
    • 0: Success
    • -1: Failure
    int32_t httpCloseConnection ( const HttpConnectionRef & connection , bool is_shutdown = false )
    OasisWeb.h
    Closes the connection.
    Parameters
    connection  The HttpConnection object.
    is_shutdown  Closes the socket completely if true.
    Return Value
    • 0: Success
    • -1: Failure
    int32_t httpConnectionIsClosed ( const HttpConnectionRef & connection )
    OasisWeb.h
    Verifies whether the current connection is closed.
    Parameters
    connection  The HttpConnection object.
    Return Value
    • 1: The connection is closed.
    • 0: Connecting.
    NetMessageRef httpConnectionGetRequestMessage ( const HttpConnectionRef & connection )
    OasisWeb.h
    Obtains the request type NetMessage object created upon successful connection.
    Parameters
    connection  The HttpConnection object.
    Return Value
    Returns the request type NetMessage object. Returns nullptr if invalid.
    NetMessageRef httpConnectionGetResponseHeaderMessage ( const HttpConnectionRef & connection , int32_t timeout )
    OasisWeb.h
    Obtains the response header NetMessage. Used to reference the header first when receiving a complete response message takes a long time.
    Parameters
    connection  The HttpConnection object.
    timeout  The time to wait until receiving the response header. Measured in milliseconds.
    Return Value
    Returns the response header NetMessage. Returns nullptr if there is an error, if a response message has not yet been received from the web server, or if a timeout occurs.
    NetMessageRef httpConnectionGetResponseMessage ( const HttpConnectionRef & connection , int32_t timeout )
    OasisWeb.h
    Obtains the complete response NetMessage.
    Parameters
    connection  The HttpConnection object.
    timeout  The time to wait until receiving the complete response message. Measured in milliseconds.
    Return Value
    Returns the complete response NetMessage. Returns nullptr if a response is not completely received or if a timeout occurs.

    Example#

    Server#

    The server in the example below reads the index.osp file when accessing the home page, calls the Script callback function to substitute variable values inside index.osp with actual application values, and transmits it to the client. When accessing other files, it transmits the file content if allowed.

    Additionally, it connects a websocket server to /ws/echo, so websocket communication takes place when a websocket client accesses the /ws/echo URL.

    The application name is webserver and the usage is as follows:

    USAGE: ./webserver  [-p <port number>] [-s] <documnet root dir>
    $ ./webserver -p 8080 -s /mnt/sd
    
    • The p option specifies the port number.
    • The s option runs the secure server (HTTPS). It is assumed that the certificate and private key are located at /mnt/sd/webserver-cert.pem and /mnt/sd/webserver-key.pem, respectively.
    • document root dir is a required argument representing the root directory path of the HTTP document files.

    Explaining the code.

    Define the script callback function in the server.

    static int32_t httpInterpretCallback(const char *url, void *callback_data, key_value_map_t *callback_arguments, const char *key_name, std::string &output)
    {
        std::string  _url(url);
        char value[256];
        int32_t err;
    
        output.clear();
    
        if(strcmp(key_name, "printModelName") == 0) {
            output = "OASIS";
        }  
    
        return 0;
    }
    

    Define a user-defined Delegate class derived from HttpUpStreamDelegate for the HTTP server.

    class MyHttpUpStreamDelegate : public HttpUpStreamDelegate 
    {
    public:
        MyHttpUpStreamDelegate() { }
        virtual ~MyHttpUpStreamDelegate() { }
    
    public:
        virtual void onRequest(const NetMessageRef &msg) {
    
            std::string url;
            netMessageUriGetPath(msg, url);
    
            ASSERT(netMessageIsRequest(msg));
            if(!netMessageIsRequest(msg)) {
                return;
            }
    
            NetMessageRef res = netMessageCreate(msg, false);
            if(res == nullptr) {
                netMessageAbortConnection(msg);
                return;
            }    
    
            fprintf(stdout, "onRequest: %s\n", url.c_str());
    
            netMessageSetStatusCodeAndReasonString(res, 200, "OK");
            //netMessageSetHeaderField(res, "Connection", "close");
    
            if(url == "/") {
                // If it is the HOME url, it reads the index.osp file from the root directory of HTTP documents, executes the script, and transmits it.
                std::string index_path = http_root_dir_path + "/index.osp";
                int32_t err = httpInterpretServerPage(res, url.c_str(), index_path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
                if(err < 0) {
                    netMessageAbortConnection(msg);
                    return;
                } else {
                    netMessagePost(res);    
                }
    
            } else {
                std::string path = http_root_dir_path + url;
                if(access(path.c_str(), F_OK) == 0) {
                    //get mime type based on extension
                    const char *mime_type = get_file_mime_type(path.c_str());   
                    if(mime_type == nullptr) {
                        //not supported
                        netMessageSetStatusCodeAndReasonString(res, 403, "Forbidden");
                        netMessagePost(res);    
                    } else {
                        std::string ext = get_file_ext(path);
                        if(ext == "osp") {
                            //oasis server script file extension
                            int32_t err = httpInterpretServerPage(res, url.c_str(), path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
                            if(err < 0) {
                                netMessageAbortConnection(msg);
                                return;
                            }
                        } else {
                            netMessageSendFile(res, path.c_str(), mime_type);
                        }
                    }
                } else {
                    netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
                    netMessagePost(res);    
                }
            }
        }
    
        virtual void onResponse(const NetMessageRef &msg) {
    
        }
    };
    

    The index.osp document is as follows:

    <html>
    <head>
    <meta charset="utf-8">
    <title>Oasis Web Server</title>
    </head>
    <body>
    <h1> Hello, World!</h1>
    <h2> My model is <?=printModelName?>.</h2>
    </body>
    </html>
    

    Define a user-defined Delegate class derived from WsEventDelegate for the websocket server. It is a simple function that responds with the received message as is.

    class EchoWsEventDelegate : public WsEventDelegate
    {
    public:
        EchoWsEventDelegate() {}
        virtual ~EchoWsEventDelegate() {}
    
    public: 
        virtual void onOpen(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Open\n");
        }
    
        virtual void onMessage(const WsEventRef &evt)  {
            fprintf(stdout, "Echo: Message\n");
    
            if(wsEventMessageType(evt) == kWsMessageText) {
                std::vector<uint8_t> data;
                if(wsEventData(evt, data) > 0) {
                    std::string str = "Echo: ";
                    str += std::string(data.data(), data.data()+data.size());
                    wsSendText(evt, str.c_str());
                }
            }
        }
    
        virtual void onError(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Error\n");
        }
    
        virtual void onClose(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Close\n");
        }
    };
    

    Initialize Oasis. The Oasis file system is not used.

        key_value_map_t parameters;
    
        parameters["offs-disable"] = "1";
        if(oasis::initialize(parameters) < 0) {
            fprintf(stderr,  "Oasis init failed\n");
            return -1;
        }
    

    Create and start the HTTP server object.

        parameters.clear();
    
        parameters["port"] = std::to_string(http_port);
        parameters["root-dir"] = http_root_dir_path.c_str();
        parameters["content-length-max"] = std::to_string(3*1024*1024);
    
        parameters["tls-disable-certificate-validation"] = "1";
    
        if(is_secure) {
            parameters["tls-enabled"] = "1";
            parameters["tls-version"] = "1.2";
    
            parameters["cert-file-path"] = "/mnt/sd/webserver-cert.pem";
            parameters["pkey-file-path"] = "/mnt/sd/webserver-key.pem";
        }
    
        http_delegate = std::make_shared<MyHttpUpStreamDelegate>();
    
        http_server = oasis::createHttpServer(parameters, http_delegate);
        if (http_server) {
            err = oasis::startHttpServer(http_server);
            ASSERT(err == 0);
            if(err < 0) {
                fprintf(stderr,  "HTTPS server failed\n");
                goto done;
            }
            fprintf(stdout, "HTTP%s running at %d...\n", is_secure?"S":"", http_port);
    
            // Allow the websocket service to be accessed via the /ws/echo URL.
            parameters.clear();
            std::shared_ptr<EchoWsEventDelegate> echo_delegate = std::make_shared<EchoWsEventDelegate>();
            if(createHttpWsService(http_server, "/ws/echo", parameters, echo_delegate) >= 0) {
            }
        }
    

    Releases the HTTP server.

        if(http_server) {
            oasis::destroyHttpServer(http_server);
        }
    

    The complete code is as follows:

    #include "OasisAPI.h"
    #include "OasisWeb.h"
    #include "OasisLog.h"
    #include "OasisUtil.h"
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <dirent.h>
    
    #include <signal.h>
    
    #include <thread>
    #include <mutex>
    #include <memory>
    #include <condition_variable>
    
    #define DLOG_THIS   0x00080000
    
    #undef DLOG_FLAGS
    #define  DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_THIS/**/)
    
    #undef DLOG_TAG
    #define DLOG_TAG "HTTP-SERVER"
    
    
    using namespace oasis;
    
    static bool isDirectory(const char *path)
    {
        struct stat path_stat;
        stat(path, &path_stat);
        return S_ISDIR(path_stat.st_mode);
    }
    
    static std::string http_root_dir_path;
    
    
    static int32_t httpInterpretCallback(const char *url, void *callback_data, key_value_map_t *callback_arguments, const char *key_name, std::string &output)
    {
        std::string  _url(url);
        char value[256];
        int32_t err;
    
        output.clear();
    
        if(strcmp(key_name, "printModelName") == 0) {
            output = "OASIS";
        }  
    
        return 0;
    }
    
    
    class MyHttpUpStreamDelegate : public HttpUpStreamDelegate 
    {
    public:
        MyHttpUpStreamDelegate() { }
        virtual ~MyHttpUpStreamDelegate() { }
    
    public:
        virtual void onRequest(const NetMessageRef &msg) {
    
            std::string url;
            netMessageUriGetPath(msg, url);
    
            ASSERT(netMessageIsRequest(msg));
            if(!netMessageIsRequest(msg)) {
                return;
            }
    
            NetMessageRef res = netMessageCreate(msg, false);
            if(res == nullptr) {
                netMessageAbortConnection(msg);
                return;
            }    
    
            fprintf(stdout, "onRequest: %s\n", url.c_str());
    
            netMessageSetStatusCodeAndReasonString(res, 200, "OK");
            //netMessageSetHeaderField(res, "Connection", "close");
    
            if(url == "/") {
    
                std::string index_path = http_root_dir_path + "/index.osp";
                int32_t err = httpInterpretServerPage(res, url.c_str(), index_path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
                if(err < 0) {
                    netMessageAbortConnection(msg);
                    return;
                } else {
                    netMessagePost(res);    
                }
    
            } else {
                std::string path = http_root_dir_path + url;
                if(access(path.c_str(), F_OK) == 0) {
                    //get mime type based on extension
                    const char *mime_type = get_file_mime_type(path.c_str());   
                    if(mime_type == nullptr) {
                        //not supported
                        netMessageSetStatusCodeAndReasonString(res, 403, "Forbidden");
                        netMessagePost(res);    
                    } else {
                        std::string ext = get_file_ext(path);
                        if(ext == "osp") {
                            //oasis server script file extension
                            int32_t err = httpInterpretServerPage(res, url.c_str(), path.c_str(), &httpInterpretCallback, (void*)this, nullptr);
                            if(err < 0) {
                                netMessageAbortConnection(msg);
                                return;
                            }
                        } else {
                            netMessageSendFile(res, path.c_str(), mime_type);
                        }
                    }
                } else {
                    netMessageSetStatusCodeAndReasonString(res, 400, "Bad Request");
                    netMessagePost(res);    
                }
            }
        }
    
        virtual void onResponse(const NetMessageRef &msg) {
    
        }
    };
    
    class EchoWsEventDelegate : public WsEventDelegate
    {
    public:
        EchoWsEventDelegate() {}
        virtual ~EchoWsEventDelegate() {}
    
    public: 
        virtual void onOpen(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Open\n");
        }
    
        virtual void onMessage(const WsEventRef &evt)  {
            fprintf(stdout, "Echo: Message\n");
    
            if(wsEventMessageType(evt) == kWsMessageText) {
                std::vector<uint8_t> data;
                if(wsEventData(evt, data) > 0) {
                    std::string str = "Echo: ";
                    str += std::string(data.data(), data.data()+data.size());
                    wsSendText(evt, str.c_str());
                }
            }
        }
    
        virtual void onError(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Error\n");
        }
    
        virtual void onClose(const WsEventRef &evt) {
            fprintf(stdout, "Echo: Close\n");
        }
    };
    
    
    static bool continue_running = true;
    
    void cancel_handler(int signum)
    {
        continue_running = false;
    }
    
    
    void printUsage(const char *pgname)
    {
        fprintf(stderr, "USAGE: %s [-p <port number>] [-s] <documnet root dir>\n", pgname);
    }
    
    using namespace oasis;
    
    int main(int argc, char* argv[])
    {
        int32_t err;
        int c;
        int32_t http_port = 80;
        bool is_secure = false;
    
        std::shared_ptr<MyHttpUpStreamDelegate> http_delegate;
        HttpServerRef http_server;
    
        opterr = 0;
        while ((c = getopt(argc, argv, "p:sh")) != -1) {
            switch (c) {
            case 'p':
                if(optarg == nullptr) {
                    fprintf(stderr, "error: bad option argument '%c'\n", optopt);
                    return -1;
                }
                http_port = atoi(optarg);
                if(http_port <= 0 || http_port > 65535) {
                    fprintf(stderr, "bad port number: %d\n", http_port);
                    return -1;
                }
                break;
            case 's':
                is_secure = true;
                break;
            case 'h':
            default:
                printUsage(argv[0]);
                return -1;
            }
        }
    
        if(argc-optind < 1) {
            fprintf(stderr, "error: invalid or insufficient arguments (%d, %d)\n", argc, optind);
            printUsage(argv[0]);
            return -1;
        }
    
        http_root_dir_path = argv[optind];
        if(!isDirectory(http_root_dir_path.c_str())) {
            fprintf(stderr, "error: invalid root directory: %s\n", http_root_dir_path.c_str());
            printUsage(argv[0]);
            return -1;
        }
    
        key_value_map_t parameters;
    
        parameters["offs-disable"] = "1";
        if(oasis::initialize(parameters) < 0) {
            fprintf(stderr,  "Oasis init failed\n");
            return -1;
        }
    
        parameters.clear();
    
        parameters["port"] = std::to_string(http_port);
        parameters["root-dir"] = http_root_dir_path.c_str();
        parameters["content-length-max"] = std::to_string(3*1024*1024);
    
        parameters["tls-disable-certificate-validation"] = "1";
    
        if(is_secure) {
            parameters["tls-enabled"] = "1";
            parameters["tls-version"] = "1.2";
    
            parameters["cert-file-path"] = "/mnt/sd/webserver-cert.pem";
            parameters["pkey-file-path"] = "/mnt/sd/webserver-key.pem";
        }
    
        http_delegate = std::make_shared<MyHttpUpStreamDelegate>();
    
        http_server = oasis::createHttpServer(parameters, http_delegate);
        if (http_server) {
            err = oasis::startHttpServer(http_server);
            ASSERT(err == 0);
            if(err < 0) {
                fprintf(stderr,  "HTTPS server failed\n");
                goto done;
            }
            fprintf(stdout, "HTTP%s running at %d...\n", is_secure?"S":"", http_port);
    
            //serverless websocket handler
            parameters.clear();
            std::shared_ptr<EchoWsEventDelegate> echo_delegate = std::make_shared<EchoWsEventDelegate>();
            if(createHttpWsService(http_server, "/ws/echo", parameters, echo_delegate) >= 0) {
            }
        }
    
        do {
            usleep(100000);
        } while(continue_running);
    
    done:
        if(http_server) {
            oasis::destroyHttpServer(http_server);
        }
    
        oasis::finalize();
    
        return 0;
    }
    

    Client#

    The following is an example of a client that connects to a website and downloads the homepage HTML document.

    #include "OasisAPI.h"
    #include "OasisWeb.h"
    
    #include "OasisLog.h"
    
    #include <thread>
    #include <mutex>
    #include <memory>
    #include <condition_variable>
    
    #define DLOG_THIS   0x00080000
    
    #undef DLOG_FLAGS
    #define  DLOG_FLAGS (DLOG_FLAGS_DEFAULT|DLOG_THIS/**/)
    
    #undef DLOG_TAG
    #define DLOG_TAG "HTTPCLI"
    
    using namespace oasis;
    
    int main(int argc, char* argv[])
    {
        int32_t err;
    
        int32_t status_code;
        std::string reason_string;
        std::string content_type; 
        std::vector<char> res_content;
        size_t content_length;
    
        const char *url = "https://google.com/";
    
        HttpConnectionRef conn;
        NetMessageRef msg, res_msg;
        int32_t wait_count = 0;
    
        key_value_map_t parameters, fields;
    
    
        parameters["offs-disable"] = "1";
        parameters["media-cache-size"] = "4096";
        if(oasis::initialize(parameters) < 0) {
            DLOG(DLOG_ERROR, "Oasis init failed\n");
            return -1;
        }
    
        parameters.clear();
    
        // Set if client certificate is required.
        // parameters["ca-certs-file-path"] = "/data/certs/cacerts.pem\x0a/data/certs/RootCA.pem";
        // parameters["cert-file-path"] = "/data/certs/client_fullchain.pem";
        // parameters["pkey-file-path"] = "/data/certs/client_privkey.pem";
    
        parameters["tls-disable-certificate-validation"] = "1";
    
        conn = httpOpenConnectionWithParameters(url, 30000, parameters);
        if(!conn) {
            DLOG(DLOG_THIS, "connection failed!\n");
            goto done;
        }
    
        msg = httpConnectionGetRequestMessage(conn);
        ASSERT(msg);
        if(!msg) {
            return -1;
        }
    
        netMessageSetMethod(msg, "GET");
    
        if(netMessagePost(msg) < 0) {
            DLOG(DLOG_ERROR, "post error\n");
            goto done;
        }
    
        wait_count = 0;
        do {
            res_msg = httpConnectionGetResponseMessage(conn, 1000);
            wait_count++;
        } while(res_msg == nullptr && wait_count*1000 < 30000); //30seconds
    
        if(res_msg == nullptr) {
            //either timeout or aborted
            DLOG(DLOG_ERROR, "res timeout\n");
            goto done;
        }
    
    
        status_code = netMessageGetStatusCodeAndReasonString(res_msg, reason_string);
        DLOG(DLOG_THIS, "Response: %d %s\n", status_code, reason_string.c_str());
    
        parameters.clear();
        netMessageUriGetQueryParameters(res_msg, parameters);
        if(parameters.empty() == false) {
            DLOG(DLOG_THIS, "Parameters:\n");
            for(auto p : parameters) {
                DLOG(DLOG_THIS, "    [%s] = %s\n", p.first.c_str(), p.second.c_str());
            }
        }
    
        DLOG(DLOG_THIS, "Headers:\n");
        fields.clear();
        netMessageGetHeaderFields(res_msg, fields);
        for(auto p : fields) {
            DLOG(DLOG_THIS, "    [%s] = %s\n", p.first.c_str(), p.second.c_str());
        }
    
    
        content_length = netMessageGetContentLength(res_msg);
        DLOG(DLOG_THIS, "Content length: %d\n", content_length);
        netMessageGetContentType(res_msg, content_type);
        DLOG(DLOG_THIS, "Content type: %s\n", content_type.c_str());
    
        netMessageGetContent(res_msg, res_content);
        //DUMP("REG_RES", res_content.data(), res_content.size());
    
        if(content_length > 0) {
            char path[PATH_MAX];
            sprintf(path, "/tmp/%u.html", (uint32_t)time(NULL));
            FILE *fp = fopen(path, "w");
            if(fp) {
                fwrite(res_content.data(), 1, res_content.size(), fp);
                fclose(fp);
                DLOG(DLOG_THIS, "saved to %s, %d bytes\n", path, content_length);
            }       
        }
    
        httpCloseConnection(conn);
        conn = nullptr;
    
    done:
    
        oasis::finalize();
    
        return 0;
    }