How to know when the HTTP headers part is ended?

c, http-headers, sockets

Solution

You are not parsing your input correctly. Here are a couple of things you are doing incorrectly:

- Your code seems to imply that your buffer will contain at most, one line of header data. However recv() does not operate on "lines" of data, but on blocks of binary data. So if you tell it that your buffer is 128 bytes in length, it will attempt to fill your buffer with 128 bytes of data if it is available (even if the 128 bytes of data contain multiple "lines").

- Your code does not take into consideration that the "\r\n" of the header break might be pulled into your buffer by two different calls to recv() which would prevent your code from recognizing the header break.

- If you do find the header break (which may happen if size of the headers is just right), you will end up pushing the last header with the terminating "\r\n" and the header break ("\r\n") into your binary data copy.

I've written a quick function which should find the end of the HTTP headers and write the rest of the server's response to a file stream:

void parse_http_headers(int s, FILE * fp)
{
   int       isnheader;
   ssize_t   readed;
   size_t    len;
   size_t    offset;
   size_t    pos;
   char      buffer[1024];
   char    * eol; // end of line
   char    * bol; // beginning of line

   isnheader = 0;
   len       = 0;

   // read next chunk from socket
   while((readed = read(s, &buffer[len], (1023-len))) > 0)
   {
      // write rest of data to FILE stream
      if (isnheader != 0)
         fwrite(buffer, 1, readed, fp);

      // process headers
      if (isnheader == 0)
      {
         // calculate combined length of unprocessed data and new data
         len += readed;

         // NULL terminate buffer for string functions
         buffer[len] = '\0';

         // checks if the header break happened to be the first line of the
         // buffer
         if (!(strncmp(buffer, "\r\n", 2)))
         {
            if (len > 2)
               fwrite(buffer, 1, (len-2), fp);
            continue;
         };
         if (!(strncmp(buffer, "\n", 1)))
         {
            if (len > 1)
               fwrite(buffer, 1, (len-1), fp);
            continue;
         };

         // process each line in buffer looking for header break
         bol = buffer;
         while((eol = index(bol, '\n')) != NULL)
         {
            // update bol based upon the value of eol
            bol = eol + 1; 

            // test if end of headers has been reached
            if ( (!(strncmp(bol, "\r\n", 2))) || (!(strncmp(bol, "\n", 1))) )
            {
               // note that end of headers has been reached
               isnheader = 1;

               // update the value of bol to reflect the beginning of the line
               // immediately after the headers
               if (bol[0] != '\n')
                  bol += 1;
               bol += 1;

               // calculate the amount of data remaining in the buffer
               len = len - (bol - buffer);

               // write remaining data to FILE stream
               if (len > 0)
                  fwrite(bol, 1, len, fp);

               // reset length of left over data to zero and continue processing
               // non-header information
               len = 0;
            };
         };

         if (isnheader == 0)
         { 
            // shift data remaining in buffer to beginning of buffer
            offset = (bol - buffer);
            for(pos = 0; pos < offset; pos++)
               buffer[pos] = buffer[offset + pos];

            // save amount of unprocessed data remaining in buffer
            len = offset;
         };
      };
   };

   return;
}

I've not tested the code, so it may have simple errors, however it should point you in the correct direction for parsing string data from a buffer in C.

Problem

The server returns the HTTP headers and binary file; something like this: ``` HTTP/1.1 200 OK Date: Thu, 28 Jun 2012 22:11:14 GMT Server: Apache/2.2.3 (Red Hat) Set-Cookie: JSESSIONID=blabla; Path=/ Pragma: no-cache Cache-Control: must-revalidate, no-store Expires: Thu, 01 Jan 1970 00:00:00 GMT Content-disposition: inline; filename="foo.pdf" Content-Length: 6231119 Connection: close Content-Type: application/pdf %PDF-1.6 %âãÏÓ 5989 0 obj <</Linearized 1/L 6231119/O 5992/E 371504/N 1498/T 6111290/H [ 55176 6052]>> endobj xref 5989 2744 0000000016 00000 n 0000061228 00000 n 0000061378 00000 n ``` I want to copy only the binary file. But how to know when the headers part is ended? I tried check check if the line contains a `\r\n\r\n` but looks like this standard does not applies to server response, only to client. This given: ``` Content-disposition: inline; filename="foo.pdf" Content-Length: 6231119 Connection: close Content-Type: application/pdf %PDF-1.6 %âãÏÓ 5989 0 obj <</Linearized 1/L 6231119/O 5992/E 371504/N 1498/T 6111290/H [ 55176 6052]>> endobj xref 5989 2744 0000000016 00000 n ``` Here is the C Code: ``` while((readed = recv(sock, buffer, 128, 0)) > 0) { if(isnheader == 0 && strstr(buffer, "\r\n\r\n") != NULL) isnheader = 1; if(isnheader) fwrite(buffer, 1, readed, fp); } ``` UPDATE: I put a `continue` control into my if-statement: ``` if(isnheader == 0 && strstr(buffer, "\r\n\r\n") != NULL) { isnheader = 1; continue; } ``` Well, it works as expected. But as @Alnitak have mentioned, it's not safe.

Original source