• libevhtp初探


    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
    ", 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的回调函数被执行。

  • 相关阅读:
    如何识别思科胖瘦AP
    路由器重置用户名密码
    python学习之路day09(黏包、md5和进程守护)
    python学习之路day02(容器类型和循环)
    python学习之路08(正则表达式和网络)
    python学习之路day06(模块+面向对象)
    python学习之路day05(迭代器和生成器)
    python学习之路day04(函数)
    【Linux】ssh远程时如何做到不用输入密码登入
    【中间件】GitLab安装
  • 原文地址:https://www.cnblogs.com/gattaca/p/6929601.html
Copyright © 2020-2023  润新知