title: http 1.0 keep-alive server and curl
body: <p>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 &quot;Connection: Keep-Alive&quot; to keep the connection open.</p>
  <p>The implementation looks like this. I removed the code that parses the HTTP header and performs the request, to keep the post as short as possible:</p>
  <pre class="lang-c prettyprint-override"><code>int Service_Request(int conn) {
  
      struct ReqInfo reqinfo;
      volatile int resource = 0;
      int retval = 0;
      Req_Result req_result = GOT_REQ;
      
      InitReqInfo(&amp;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, &amp;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, &amp;reqinfo);
          }
          /*Diag_Msg(&quot;Client Request: \r\n&quot;);
          Diag_Msg(reqinfo.clientRequest);*/
          
          /*
           * the reqinfo.keep_alive flag will be set to 1 if the 
           * &quot;Connection: Keep-Alive&quot; header was sent by the client
           */
          if(reqinfo.keep_alive == 0)
          {
              break;
          }
  
          reqinfo.keep_alive_max--;
          if(reqinfo.keep_alive_max &lt;= 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,&quot;\r\n&quot;,2);
          FreeReqInfo(&amp;reqinfo);
          
      }
      FreeReqInfo(&amp;reqinfo);
      return (retval);
  }
  </code></pre>
  <p>The Get_Request function looks like this:</p>
  <pre class="lang-c prettyprint-override"><code>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-&gt;first_request == 1)
      {
          tv.tv_sec  = 5;
          tv.tv_usec = 0;
          reqinfo-&gt;first_request = 0;
      }
      else
      {
          tv.tv_sec  = reqinfo-&gt;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(&amp;fds);
      FD_SET (conn, &amp;fds);
  
  
      /*  Wait until the timeout to see if input is ready  */
  
      rval = select(conn + 1, &amp;fds, NULL, NULL, &amp;tv);
  
  
      /*  Take appropriate action based on return from select()  */
  
      if ( rval &lt; 0 ) 
      {
          Diag_Msg(&quot;Error calling select() in get_request()&quot;);
          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-&gt;clientRequest == NULL)
          { 
              reqinfo-&gt;clientRequest = calloc(MAX_REQ_LINE - 1, sizeof(char));
              strncpy(reqinfo-&gt;clientRequest,buffer,MAX_REQ_LINE - 1);
          }
          else
          {
              strncat(reqinfo-&gt;clientRequest,buffer,MAX_REQ_LINE - 1);
          }
          Trim(buffer);
  
          if ( buffer[0] == '\0' )
          break;
  
          if ( Parse_HTTP_Header(buffer, reqinfo) )
          break;
      }
      } while ( reqinfo-&gt;type != SIMPLE );
  
      return (GOT_REQ);
  }
  </code></pre>
  <p>To describe the workings of this server in English: The server receives the first request. It parses the headers, if it finds the &quot;Connection: Keep-Alive&quot; 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.</p>
  <p>I tested this with curl:</p>
  <pre><code>C:\curl&gt;curl -v -H &quot;Connection: Keep-Alive&quot; --data-binary @vid1.bin 10.84.67.129/s1p0:1/vid[1-2].bin
  
  [1/2]: 10.84.67.129/s1p0:1/vid1.bin --&gt; &lt;stdout&gt;
  --_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)
  &gt; POST /s1p0:1/vid1.bin HTTP/1.1
  &gt; User-Agent: curl/7.28.1
  &gt; Host: 10.84.67.129
  &gt; Accept: */*
  &gt; Connection: Keep-Alive
  &gt; Content-Length: 51200
  &gt; Content-Type: application/x-www-form-urlencoded
  &gt; Expect: 100-continue
  &gt;
  * HTTP 1.0, assume close after body
  &lt; HTTP/1.0 100 Continue
  * HTTP 1.0, assume close after body
  &lt; HTTP/1.0 200 OK
  &lt; Server: DTSVU v0.1
  &lt; Content-Type: text/html
  * HTTP/1.0 connection set to keep alive!
  &lt; Connection: Keep-Alive
  &lt; Keep-Alive: timeout=5, max=10
  &lt;
  * Connection #0 to host 10.84.67.129 left intact
  
  [2/2]: 10.84.67.129/s1p0:1/vid2.bin --&gt; &lt;stdout&gt;
  --_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)
  &gt; POST /s1p0:1/vid2.bin HTTP/1.1
  &gt; User-Agent: curl/7.28.1
  &gt; Host: 10.84.67.129
  &gt; Accept: */*
  &gt; Connection: Keep-Alive
  &gt; Content-Length: 51200
  &gt; Content-Type: application/x-www-form-urlencoded
  &gt; Expect: 100-continue
  &gt;
  * HTTP 1.0, assume close after body
  &lt; HTTP/1.0 100 Continue
  * HTTP 1.0, assume close after body
  &lt; HTTP/1.0 200 OK
  &lt; Server: DTSVU v0.1
  &lt; Content-Type: text/html
  * HTTP/1.0 connection set to keep alive!
  &lt; Connection: Keep-Alive
  &lt; Keep-Alive: timeout=5, max=10
  &lt;
  * Connection #0 to host 10.84.67.129 left intact
  * Closing connection #0
  </code></pre>
  <p>As you can see, curl says: Connection #0 seems to be dead! afterthefirstrequest 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?</p>
  <p>PS the above code was adapted from <a href="http://www.paulgriffiths.net/program/c/webserv.php" rel="nofollow noreferrer">http://www.paulgriffiths.net/program/c/webserv.php</a></p>