http 1.0 keep-alive服务器和curl

时间:2013-01-09 20:31:15

标签: http curl keep-alive

我在嵌入式平台上忙于自己的http服务器实现。从技术上讲,服务器是HTTP 1.0兼容的,因此它希望客户端发送标题“Connection:Keep-Alive”以保持连接打开。

实现看起来像这样。我删除了解析HTTP标头并执行请求的代码,以使帖子尽可能短:

int Service_Request(int conn) {

    struct ReqInfo reqinfo;
    volatile int resource = 0;
    int retval = 0;
    Req_Result req_result = GOT_REQ;

    InitReqInfo(&reqinfo);

    /* while we still have HTTP requests to process */
    while (req_result == GOT_REQ)
    {

        /*  Get HTTP request, there are 3 different return values:
         * GOT_REQ: we got a valid HTTP request
         * TIMEOUT_REQ we timed out waiting for a request
         * ERROR_REQ there was some error receiving from the socket
         * usually because the connection was closed by the peer*/
        req_result = Get_Request(conn, &reqinfo);
        if ( req_result == TIMEOUT_REQ)
        {
            /* timed out waiting for the client, exit */
            retval = 0;
            break;
        }
        else if (req_result == ERROR_REQ)
        {
            /* some error, exit */
            retval = -1;
            break;
        }
        /* Process the request GET, PUT and POST is supported*/
        if (reqinfo.method == GET) 
        {
            /* code to handle GET*/
        } 
        /* PUT and POST are handled in the same way */
        else if ((reqinfo.method == PUT) || (reqinfo.method == POST) )
        {
            /* Code to handle PUT and POST*/        
        } 
        else 
        {
            /* not supported, code should never get here */
            reqinfo.status = 501;
            Return_Error_Msg(conn, &reqinfo);
        }
        /*Diag_Msg("Client Request: \r\n");
        Diag_Msg(reqinfo.clientRequest);*/

        /*
         * the reqinfo.keep_alive flag will be set to 1 if the 
         * "Connection: Keep-Alive" header was sent by the client
         */
        if(reqinfo.keep_alive == 0)
        {
            break;
        }

        reqinfo.keep_alive_max--;
        if(reqinfo.keep_alive_max <= 0 )
        {
            /*
             * the connection has been reused for the maxmum amount of times, stop
             */
            break;
        }
        /*
         * If we get here, we will clear the memory used for the client request
         * and go to the beginning of the while loop to receive another request
         */
        Writeline(conn,"\r\n",2);
        FreeReqInfo(&reqinfo);

    }
    FreeReqInfo(&reqinfo);
    return (retval);
}

int Service_Request(int conn) { struct ReqInfo reqinfo; volatile int resource = 0; int retval = 0; Req_Result req_result = GOT_REQ; InitReqInfo(&reqinfo); /* while we still have HTTP requests to process */ while (req_result == GOT_REQ) { /* Get HTTP request, there are 3 different return values: * GOT_REQ: we got a valid HTTP request * TIMEOUT_REQ we timed out waiting for a request * ERROR_REQ there was some error receiving from the socket * usually because the connection was closed by the peer*/ req_result = Get_Request(conn, &reqinfo); if ( req_result == TIMEOUT_REQ) { /* timed out waiting for the client, exit */ retval = 0; break; } else if (req_result == ERROR_REQ) { /* some error, exit */ retval = -1; break; } /* Process the request GET, PUT and POST is supported*/ if (reqinfo.method == GET) { /* code to handle GET*/ } /* PUT and POST are handled in the same way */ else if ((reqinfo.method == PUT) || (reqinfo.method == POST) ) { /* Code to handle PUT and POST*/ } else { /* not supported, code should never get here */ reqinfo.status = 501; Return_Error_Msg(conn, &reqinfo); } /*Diag_Msg("Client Request: \r\n"); Diag_Msg(reqinfo.clientRequest);*/ /* * the reqinfo.keep_alive flag will be set to 1 if the * "Connection: Keep-Alive" header was sent by the client */ if(reqinfo.keep_alive == 0) { break; } reqinfo.keep_alive_max--; if(reqinfo.keep_alive_max <= 0 ) { /* * the connection has been reused for the maxmum amount of times, stop */ break; } /* * If we get here, we will clear the memory used for the client request * and go to the beginning of the while loop to receive another request */ Writeline(conn,"\r\n",2); FreeReqInfo(&reqinfo); } FreeReqInfo(&reqinfo); return (retval); }

Get_Request函数如下所示:

Req_Result Get_Request(int conn, struct ReqInfo * reqinfo) {

    char   buffer[MAX_REQ_LINE] = {0};
    int    rval;
    fd_set fds;
    struct timeval tv;


    /*  Set timeout to 5 seconds if this is the first request since the client connected, wait 5 seconds
     * Otherwise, wait 5ms */
    if(reqinfo->first_request == 1)
    {
        tv.tv_sec  = 5;
        tv.tv_usec = 0;
        reqinfo->first_request = 0;
    }
    else
    {
        tv.tv_sec  = reqinfo->keep_alive_timeout;
        tv.tv_usec = 0;
    }

    /*  Loop through request headers. If we have a simple request,
    then we will loop only once. Otherwise, we will loop until
    we receive a blank line which signifies the end of the headers,
    or until select() times out, whichever is sooner.                */
    do {

    /*  Reset file descriptor set  */

    FD_ZERO(&fds);
    FD_SET (conn, &fds);


    /*  Wait until the timeout to see if input is ready  */

    rval = select(conn + 1, &fds, NULL, NULL, &tv);


    /*  Take appropriate action based on return from select()  */

    if ( rval < 0 ) 
    {
        Diag_Msg("Error calling select() in get_request()");
        return (ERROR_REQ);
    }
    else if ( rval == 0 ) {

        /*  input not ready after timeout  */

        return (TIMEOUT_REQ);

    }
    else {

        /*  We have an input line waiting, so retrieve it  */
        memset(buffer,0,MAX_REQ_LINE - 1);
        if(Readline(conn, buffer, MAX_REQ_LINE - 1) == -1)
        {
            return (ERROR_REQ);
        }
        if(reqinfo->clientRequest == NULL)
        { 
            reqinfo->clientRequest = calloc(MAX_REQ_LINE - 1, sizeof(char));
            strncpy(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
        }
        else
        {
            strncat(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1);
        }
        Trim(buffer);

        if ( buffer[0] == '\0' )
        break;

        if ( Parse_HTTP_Header(buffer, reqinfo) )
        break;
    }
    } while ( reqinfo->type != SIMPLE );

    return (GOT_REQ);
}

用英语描述此服务器的工作方式:服务器收到第一个请求。它解析标题,如果它找到“Connection:Keep-Alive”标题,它会设置一个标志。服务器继续处理此请求。一旦完成,它会检查保持活动标志。如果清除,服务器将关闭连接。如果设置,则服务器执行清理操作,并继续通过同一连接等待另一个请求。等等。

我用curl测试了这个:

Req_Result Get_Request(int conn, struct ReqInfo * reqinfo) { char buffer[MAX_REQ_LINE] = {0}; int rval; fd_set fds; struct timeval tv; /* Set timeout to 5 seconds if this is the first request since the client connected, wait 5 seconds * Otherwise, wait 5ms */ if(reqinfo->first_request == 1) { tv.tv_sec = 5; tv.tv_usec = 0; reqinfo->first_request = 0; } else { tv.tv_sec = reqinfo->keep_alive_timeout; tv.tv_usec = 0; } /* Loop through request headers. If we have a simple request, then we will loop only once. Otherwise, we will loop until we receive a blank line which signifies the end of the headers, or until select() times out, whichever is sooner. */ do { /* Reset file descriptor set */ FD_ZERO(&fds); FD_SET (conn, &fds); /* Wait until the timeout to see if input is ready */ rval = select(conn + 1, &fds, NULL, NULL, &tv); /* Take appropriate action based on return from select() */ if ( rval < 0 ) { Diag_Msg("Error calling select() in get_request()"); return (ERROR_REQ); } else if ( rval == 0 ) { /* input not ready after timeout */ return (TIMEOUT_REQ); } else { /* We have an input line waiting, so retrieve it */ memset(buffer,0,MAX_REQ_LINE - 1); if(Readline(conn, buffer, MAX_REQ_LINE - 1) == -1) { return (ERROR_REQ); } if(reqinfo->clientRequest == NULL) { reqinfo->clientRequest = calloc(MAX_REQ_LINE - 1, sizeof(char)); strncpy(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1); } else { strncat(reqinfo->clientRequest,buffer,MAX_REQ_LINE - 1); } Trim(buffer); if ( buffer[0] == '\0' ) break; if ( Parse_HTTP_Header(buffer, reqinfo) ) break; } } while ( reqinfo->type != SIMPLE ); return (GOT_REQ); }

正如你所看到的,curl说:连接#0似乎已经死了!在第一次请求完成后。然后它继续关闭连接并打开一个新连接。我确定我正确实现了HTTP 1.0 keep-alive功能。所以我的问题是:第一次请求完成后curl对连接的期望是什么?为什么它决定连接已经死了?

PS以上代码改编自http://www.paulgriffiths.net/program/c/webserv.php

1 个答案:

答案 0 :(得分:4)

我解决了。如果服务器回复HTTP / 1.1和“Content-Length:0”,curl将重用该连接。我的服务器回复看起来像这样

< HTTP/1.1 100 Continue
< HTTP/1.1 200 OK
< Server: DTSVU v0.1
< Content-Type: text/html
< Connection: Keep-Alive
< Keep-Alive: timeout=1, max=95
< Content-Length: 0

在第5次重用连接后。