wolkykim / libasyncd

Libasyncd is an embeddable event-based asynchronous Message/HTTP server framework for C/C++.

Home Page:http://wolkykim.github.io/libasyncd/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to add get/post/put handlers without having to modify the lib source code and how to get the parameters in the handlers?

sukantag opened this issue · comments

commented

I am trying to see if I can use libasyncd as a library and write a high speed HTTPS server module allowing faster get/post/put operations.

I am trying to see if I write the app then someway hook into internal http handler and add the get/post/put connector. I was looking at the code and wanted to not added/modify the code of the library put in hooks to get the parameters being passed into the get/post handlers.

I can't find the good way to do that without having to modify the existing source code. Can you please let me know if that is doable (perhaps you have a sample example for the same that you can share, couldn't find anything on github).

If not and I have to modify the existing HTTP protocol handler then please let me know

If there are ways to add handlers to routes do I have to parse url and the post parameters or are there helpers for that?

Please let me know
Thx

Hi,

I think this is doable.

The default http handler doesn't handle actual requests but it's a helper library that you could use for http header parsing, header handling, response handling....

If you see the example of http server implementation.

ad_server_register_hook(server, ad_http_handler, NULL); // HTTP Parser is also a hook.
ad_server_register_hook_on_method(server, "GET", my_http_get_handler, NULL);
ad_server_register_hook(server, my_http_default_handler, NULL);

It inserts 3 hooks in the chain, the event will flow all 3 hooks as long as each hook returns AD_OK. So in this example, first one ad_http_handler provides request header parsing, it'll return AD_OK when parsing job is done, if only partial header has arrived then it will return AD_TAKEOVER which will stop escalating the hook to the next chain.

2nd and 3rd lines are actual your hooks, that handles actual response. So libasyncd doesn't implement this for you, this part is your business logic. So you could implement your logic in there.

You can also use ad_server_register_hook_on_method() if you'd like to put your hook on certain request methods.

ad_conn_set_userdata() can be used to attach connection oriented userdata to the stream.

Is this answering your question?

commented

I tried the below hooking for trapping the GET request as shown below

ad_server_register_hook_on_method(server, "GET", my_http_get_handler, NULL);

but I could not figure out to get the URI so that I can parse it and get the URL parameters.

Also since I need to check for POST as well, can POST handler be written in the same manner (would need to understand what mechanism do I apply to pick up the post body).

Thank you for your help

The request object has the URI information ready. The request object is attached as 'extra' info.
You can access the request object as below on your hook.

ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
printf("%s", http->request.uri);

The body part is stored in http->request.inbuf

Note that ad_conn_set_userdata() must be used if you want to attach your application defined user data. ad_conn_set_extra() is dedicated method for libasyncd to avoid the conflict.

Here's the request object internal.

struct ad_http_s {
    // HTTP Request
    struct {
        enum ad_http_request_status_e status;  /*!< request status. */
        struct evbuffer *inbuf;  /*!< input data buffer. */

        // request line - available on REQ_REQUESTLINE_DONE.
        char *method;   /*!< request method ex) GET */
        char *uri;      /*!< url+query ex) /data%20path?query=the%20value */
        char *httpver;  /*!< version ex) HTTP/1.1 */
        char *path;     /*!< decoded path ex) /data path */
        char *query;    /*!< query string ex) query=the%20value */

        // request header - available on REQ_HEADER_DONE.
        qlisttbl_t *headers;  /*!< parsed request header entries */
        char *host;           /*!< host ex) www.domain.com or www.domain.com:8080 */
        char *domain;         /*!< domain name ex) www.domain.com (no port number) */
        off_t contentlength;  /*!< value of Content-Length header.*/
        size_t bodyin;        /*!< bytes moved to in-buff */
    } request;

    // HTTP Response
    struct {
        struct evbuffer *outbuf;  /*!< output data buffer. */
        bool frozen_header;       /*!< indicator whether we sent header out or not */

        // response headers
        int code;               /*!< response status-code */
        char *reason;           /*!< reason-phrase */
        qlisttbl_t *headers;    /*!< response header entries */
        off_t contentlength;    /*!< content length in response */
        size_t bodyout;         /*!< bytes added to out-buffer */
    } response;
};

don't forget to give your star to this project :)

@sukantag I was also trying to access the POST data and had some trouble.

Here is how I got POST data. Found my answer by reviewing the ad_http_handler.c line 153.

#include "asyncd/asyncd.h"

int my_http_get_handler(short event, ad_conn_t *conn, void *userdata);
int my_http_default_handler(short event, ad_conn_t *conn, void *userdata);
int my_post_handler(short event, ad_conn_t *conn, void *userdata);

int main(int argc, char **argv) {
    ad_server_t *server = ad_server_new();
    ad_server_set_option(server, "server.port", "9191");
    ad_server_register_hook(server, ad_http_handler, NULL); // HTTP Parser is also a hook.
    ad_server_register_hook_on_method(server, "POST", my_post_handler, NULL);
    ad_server_register_hook_on_method(server, "GET", my_http_get_handler, NULL);
    ad_server_register_hook(server, my_http_default_handler, NULL);
    return ad_server_start(server);

}

int my_post_handler(short event, ad_conn_t *conn, void *userdata) {
    if (event & AD_EVENT_READ) {
        if (ad_http_get_status(conn) == AD_HTTP_REQ_DONE) {
            ad_http_response(conn, 200, "text/html", "HI\n", 4);
            ad_http_t *http = (ad_http_t *) ad_conn_get_extra(conn);
            const char *post;

            printf("[METHOD ] %s\n", http->request.method);
            printf("[CONLEN ] %i\n", http->request.contentlength);
            printf("[TEST   ] %s\n", http->request.query);
            printf("[VERSION] %s\n", http->request.httpver);

            post = ad_http_get_content(conn, http->request.contentlength, 0);
            printf("[POST   ] %s\n", post);
            printf("\n");
            return ad_http_is_keepalive_request(conn) ? AD_DONE : AD_CLOSE;
        }
    }
    return AD_OK;
}

int my_http_get_handler(short event, ad_conn_t *conn, void *userdata) {
    if (event & AD_EVENT_READ) {
        if (ad_http_get_status(conn) == AD_HTTP_REQ_DONE) {
            ad_http_response(conn, 200, "text/html", "go away\n", 9);
            return ad_http_is_keepalive_request(conn) ? AD_DONE : AD_CLOSE;
        }
    }
    return AD_OK;
}

int my_http_default_handler(short event, ad_conn_t *conn, void *userdata) {
    if (event & AD_EVENT_READ) {
        if (ad_http_get_status(conn) == AD_HTTP_REQ_DONE) {
            ad_http_response(conn, 501, "text/html", "Not implemented", 15);
            return AD_CLOSE; // Close connection.
        }
    }
    return AD_OK;
}

Thanks @wolkykim for this library :)