cURL / Mailing Lists / curl-users / Single Mail

curl-users

How does curl implement http 1.1 / http1.0 keep alive?

From: Johan Moraal <johan.moraal_at_gmail.com>
Date: Wed, 9 Jan 2013 11:43:22 +0200

Dear curl users.

I'm busy with my own http server implementation on an embedded platform.
Technically the server is HTTP 1.0 compliant, and therefore it expects the
the client to send the header "Connection: Keep-Alive" to keep the
connection open.

The implementation looks like this. I removed the code that parses the HTTP
header and performs the request, to keep the email as short as possible:
    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 removed for simplicity */
        }
        /* PUT and POST are handled in the same way */
        else if ((reqinfo.method == PUT) || (reqinfo.method == POST) )
        {
            /* code removed for simplicity */
        }
        else
        {
            /* not supported, tell the client */
            reqinfo.status = 501;
            Return_Error_Msg(conn, &reqinfo);
        }

        /*
         * 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;
        }
        /*
         * 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
         */
        FreeReqInfo(&reqinfo);

    }

The Get_Request function looks like this:

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 x seconds (TBD) */
    if(reqinfo->first_request == 1)
    {
        tv.tv_sec = 5;
        tv.tv_usec = 0;
        reqinfo->first_request = 0;
    }
    else
    {
        tv.tv_sec = 5; /* TODO: this must not be hardcoded */
        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 and parse it */
        /* The rest of the code removed for simplicity

    }
    } while ( reqinfo->type != SIMPLE );

    return (GOT_REQ);
}

To describe the workings of this server in English: The server receives the
first request. It parses the headers, if it finds the "Connection:
Keep-Alive" header, it sets a flag. The server proceeds to process this
request. WHen it is done it checks the keep-alive flag. If it is cleared,
the server closes the connection. If set the server performs a clean-up
operation and the proceeds to wait for another request over the same
connection. And so on.

I tested this with curl:

C:\curl>curl -v -H "Connection: Keep-Alive" --data-binary @vid1.bin
10.84.67.129/s1p0:1/vid[1-2].bin

[1/2]: 10.84.67.129/s1p0:1/vid1.bin --> <stdout>
--_curl_--10.84.67.129/s1p0:1/vid1.bin
* About to connect() to 10.84.67.129 port 80 (#0)
* Trying 10.84.67.129...
* connected
* Connected to 10.84.67.129 (10.84.67.129) port 80 (#0)
> POST /s1p0:1/vid1.bin HTTP/1.1
> User-Agent: curl/7.28.1
> Host: 10.84.67.129
> Accept: */*
> Connection: Keep-Alive
> Content-Length: 51200
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* HTTP 1.0, assume close after body
< HTTP/1.0 100 Continue
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: DTSVU v0.1
< Content-Type: text/html
* HTTP/1.0 connection set to keep alive!
< Connection: Keep-Alive
< Keep-Alive: timeout=5, max=10
<
* Connection #0 to host 10.84.67.129 left intact

[2/2]: 10.84.67.129/s1p0:1/vid2.bin --> <stdout>
--_curl_--10.84.67.129/s1p0:1/vid2.bin
* Connection #0 seems to be dead!
* Closing connection #0
* About to connect() to 10.84.67.129 port 80 (#0)
* Trying 10.84.67.129...
* connected
* Connected to 10.84.67.129 (10.84.67.129) port 80 (#0)
> POST /s1p0:1/vid2.bin HTTP/1.1
> User-Agent: curl/7.28.1
> Host: 10.84.67.129
> Accept: */*
> Connection: Keep-Alive
> Content-Length: 51200
> Content-Type: application/x-www-form-urlencoded
> Expect: 100-continue
>
* HTTP 1.0, assume close after body
< HTTP/1.0 100 Continue
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: DTSVU v0.1
< Content-Type: text/html
* HTTP/1.0 connection set to keep alive!
< Connection: Keep-Alive
< Keep-Alive: timeout=5, max=10
<
* Connection #0 to host 10.84.67.129 left intact
* Closing connection #0

C:\curl>

As you can see, curl says: Connection #0 seems to be dead! after the first
request is completed. It then proceeds to close the connection and opens a
new one. I'm sure I implemented the HTTP 1.0 keep-alive functionality
correctly. SO my question is: what does curl expect over the connection
after the first request is completed? Why does it decide the connection is
dead?

-- 
Johan

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-users
FAQ: http://curl.haxx.se/docs/faq.html
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-01-09