当前位置: 首页 > 工具软件 > Libevhtp > 使用案例 >

libevhtp初探

袁霍英
2023-12-01

libevent的evhttp不适合多线程,libevhtp重新设计了libevent的http API,采用了和memcached类似的多线程模型。

worker线程的管道读事件的回调函数为htp__run_in_thread_:

#ifndef EVHTP_DISABLE_EVTHR
static void
htp__run_in_thread_(evthr_t * thr, void * arg, void * shared)
{
    evhtp_t            * htp        = shared;
    evhtp_connection_t * connection = arg;

    connection->evbase = evthr_get_base(thr);
    connection->thread = thr;

    if (htp__connection_accept_(connection->evbase, connection) < 0)
    {
        evhtp_connection_free(connection);

        return;
    }

    if (htp__run_post_accept_(htp, connection) < 0)
    {
        evhtp_connection_free(connection);

        return;
    }
}

#endif

htp__connection_accept_函数如下:

static int
htp__connection_accept_(struct event_base * evbase, evhtp_connection_t * connection)
{
    struct timeval * c_recv_timeo;
    struct timeval * c_send_timeo;

    if (htp__run_pre_accept_(connection->htp, connection) < 0)
    {
        evutil_closesocket(connection->sock);

        return -1;
    }

#ifndef EVHTP_DISABLE_SSL
    if (connection->htp->ssl_ctx != NULL)
    {
        connection->ssl = SSL_new(connection->htp->ssl_ctx);
        connection->bev = bufferevent_openssl_socket_new(evbase,
                                                         connection->sock,
                                                         connection->ssl,
                                                         BUFFEREVENT_SSL_ACCEPTING,
                                                         connection->htp->bev_flags);
        SSL_set_app_data(connection->ssl, connection);
        goto end;
    }
#endif

    connection->bev = bufferevent_socket_new(evbase,
                                             connection->sock,
                                             connection->htp->bev_flags);

    htp_log_debug("enter sock=%d\n", connection->sock);

#ifndef EVHTP_DISABLE_SSL
end:
#endif

    if (connection->recv_timeo.tv_sec || connection->recv_timeo.tv_usec)
    {
        c_recv_timeo = &connection->recv_timeo;
    } else if (connection->htp->recv_timeo.tv_sec ||
               connection->htp->recv_timeo.tv_usec)
    {
        c_recv_timeo = &connection->htp->recv_timeo;
    } else {
        c_recv_timeo = NULL;
    }

    if (connection->send_timeo.tv_sec || connection->send_timeo.tv_usec)
    {
        c_send_timeo = &connection->send_timeo;
    } else if (connection->htp->send_timeo.tv_sec ||
               connection->htp->send_timeo.tv_usec)
    {
        c_send_timeo = &connection->htp->send_timeo;
    } else {
        c_send_timeo = NULL;
    }

    evhtp_connection_set_timeouts(connection, c_recv_timeo, c_send_timeo);

    connection->resume_ev = event_new(evbase, -1, EV_READ | EV_PERSIST,
                                      htp__connection_resumecb_, connection);
    event_add(connection->resume_ev, NULL);

    bufferevent_enable(connection->bev, EV_READ);
    bufferevent_setcb(connection->bev,
                      htp__connection_readcb_,
                      htp__connection_writecb_,
                      htp__connection_eventcb_, connection);

    return 0;
} 

此时,conn_fd的读事件已经添加到worker线程的event_base中。

 

当读事件发生后,最终会运行htp__connection_readcb_,然后调用htparser_run,再调用htp__request_parse_path_:

static int
htp__request_parse_path_(htparser * p, const char * data, size_t len)
{
    evhtp_connection_t * c = htparser_get_userdata(p);
    evhtp_path_t       * path;

    if (htp__require_uri_(c) != 0)
    {
        return -1;
    }

    if (evhtp_unlikely(!(path = htp__path_new_(data, len))))
    {
        c->request->status = EVHTP_RES_FATAL;

        return -1;
    }

    c->request->uri->path   = path;
    c->request->uri->scheme = htparser_get_scheme(p);
    c->request->method      = htparser_get_method(p);

    htp__lock_(c->htp);
    {
        htp__request_set_callbacks_(c->request);
    }
    htp__unlock_(c->htp);

    if ((c->request->status = htp__hook_path_(c->request, path)) != EVHTP_RES_OK)
    {
        return -1;
    }

    return 0;
}

通过htp__request_set_callbacks_,我们可以设置每个请求的回调函数为evhtp_s结构中的回调函数。

htparser_run后续会调用htp__request_parse_fini_:

static int
htp__request_parse_fini_(htparser * p)
{
    evhtp_connection_t * c = htparser_get_userdata(p);

    if (c->flags & EVHTP_CONN_FLAG_PAUSED)
    {
        return -1;
    }

    /* check to see if we should use the body of the request as the query
     * arguments.
     */
    if (htp__should_parse_query_body_(c->request) == 1)
    {
        const char      * body;
        size_t            body_len;
        evhtp_uri_t     * uri;
        struct evbuffer * buf_in;

        uri            = c->request->uri;
        buf_in         = c->request->buffer_in;

        body_len       = evbuffer_get_length(buf_in);
        body           = (const char *)evbuffer_pullup(buf_in, body_len);

        uri->query_raw = calloc(body_len + 1, 1);
        evhtp_alloc_assert(uri->query_raw);

        memcpy(uri->query_raw, body, body_len);

        uri->query     = evhtp_parse_query(body, body_len);
    }


    /*
     * XXX c->request should never be NULL, but we have found some path of
     * execution where this actually happens. We will check for now, but the bug
     * path needs to be tracked down.
     *
     */
    if (c->request && c->request->cb)
    {
        (c->request->cb)(c->request, c->request->cbarg);
    }

    if (c->flags & EVHTP_CONN_FLAG_PAUSED)
    {
        return -1;
    }

    return 0;
}

可以看到,最终request的回调函数被执行。

 

转载于:https://www.cnblogs.com/gattaca/p/6929601.html

 类似资料: