title: Disassemble HTTP Response (C)
body: <p>In continuation of <a href="https://stackoverflow.com/questions/3382173/download-http-thru-sockets-c">this question</a> I ask the following question.</p>
  
  <p>I've managed to read and understand what D.Shawley has recommended and I already finished a lot of work. But a problem occured to me again. I've managed to get the Content-Length of the page out of the HTTP response, but now I'm experiencing some problems with getting the length of the HTTP response header. As I understand from the RFC 2616 document, a response header can be preceded with several CRLFs and must be followed by one or more CRLFs. Now I'm heaving trouble with determining the end of the response header.</p>
  
  <p>I've came up with the following files:</p>
  
  <p><strong>IEFDowner.h</strong></p>
  
  <pre><code>    /*
       *  IEFDowner.h
       *  Downer
       *
       *  Created by ief2 on 2/08/10.
       *  Copyright 2010 ief2. All rights reserved.
       *
       *  http://developerief2.site11.com
       *  
       */
  
      #include &lt;stdio.h&gt; /* snprintf() */
      #include &lt;sys/socket.h&gt; /* SOCKET */
      #include &lt;netdb.h&gt; /* struct addrinfo */
      #include &lt;stdlib.h&gt; /* exit() */
      #include &lt;string.h&gt; /* memset(), strlen(), strcpy(), strstr() */
      #include &lt;errno.h&gt; /* errno */
      #include &lt;unistd.h&gt; /* close() */
  
      enum _IEFDownerErrorType {
          IEFNoError,
          IEFGAIError, /* Use gai_strerror() to get the error message */
          IEFConnectError, /* Failed to connect with the socket */
          IEFSendError, /* Use strerror() to get the error message */
          IEFReceiveError /* Error with recv() */
      };
      typedef enum _IEFDownerErrorType IEFDownerErrorType;
  
      /*
       Requests the data from the host at the given path thru the given protocol.
  
       PAREMTERS
       - host
          The host to connect to.
  
       - filepath
          The path to the file which to download from the host.
  
       - buffer
          A buffer to fill with the received data
  
       - maxSize
          The maximum size of the buffer
  
       - receivedBytes
          The amount of received bytes
  
       - errorType
          The type of error received. See the header file for more info (enum _IEFDownerErrorType)
  
       RETURN VALUE
          The function returns 0 if it succeeded or another code if it failed.
       */
  
      int
      IEFDownerHTTPDownload(const char *host, 
                            const char *filepath, 
                            void *buffer, 
                            unsigned long maxSize, 
                            long *receivedBytes, 
                            IEFDownerErrorType *errorType);
  
      /*
       Returns a pointer to a structure containing the IP4 or IP6 addresss.
  
       PARAMETERS
       - sa
          A pointer to a structure of sockaddr.
  
       RETURN VALUE
          Returns a pointer to a structure of the type sockaddr_in or the type sockaddr_in6.
       */
      void *
      IEFDownerGetInAddr(struct sockaddr *sa);
  
      /*
       Gets the content-length information out of an HTTP response header.
  
       PARAMETERS
       - httpHeader
          The null terminated response.
  
       - contentLength
          Upon return it contains the content length.
  
       RETURN VALUE
          The function returns 0 if it succeeded or -1 if it did not.
       */
      int
      IEFDownerGetContentLengthOfPage(const char *httpHeader, 
                                      int *contentLength);
  
      /*
       Gets the string lenth of the header information
  
       PARAMETERS
       - received
          The received header
  
       - headerSize
          Upon return contains the header length
  
       RETURN VALUE
          The function returns 0 if it succeeded. If there was no header information found, it returns -1.
  
       DISCUSSION
          The header size includes the trailing CRLF's behind the header data. 
          All empty CRLF's are included until a non-empty line is met. 
          If there're zero empty lines after the first non empty line, the header information was not found.
       */
      int
      IEFDownerGetSizeOfHeader(const char *received, 
                               int *headerSize);
  </code></pre>
  
  <p><strong>IEFDowner.c</strong></p>
  
  <pre><code>/*
  *  IEFDowner.c
  *  Downer
  *
  *  Created by ief2 on 2/08/10.
  *  Copyright 2010 ief2. All rights reserved.
  *
  *  http://developerief2.site11.com
  *  
  */
  
  
  
  #include "IEFDowner.h"
  
  int
  IEFDownerHTTPDownload(const char *host, const char *filepath, void *buffer, unsigned long maxSize, long *rB, IEFDownerErrorType *errorType) {
      int status;                 // contains returned statuses
      int sockfd;                 // the socket file descriptor
      struct addrinfo *infos;     // linked list
      struct addrinfo hints;      // hints to getaddrinfo()
      struct addrinfo *p;         // used in the loop to get valid addrinfo
      long receivedBytes;         // the received bytes
      unsigned requestLength;     // the length of the request
      char *request;              // the http request
  
      // GET ADDRESS INFO
      // fill hints
      memset(&amp;hints, 0, sizeof(hints));
      hints.ai_socktype = SOCK_STREAM;
      hints.ai_flags = AI_PASSIVE;
      hints.ai_family = AF_UNSPEC;
  
      // getaddrinfo
      status = getaddrinfo(host, 
                           "80", 
                           &amp;hints, 
                           &amp;infos);
      if(status != 0) {
          if(errorType) *errorType = IEFGAIError;
          return status;
      }
  
      // FIND FIRST VALID SOCKET
      for(p = infos; p != NULL; p = p-&gt;ai_next) {
          // create
          sockfd = socket(p-&gt;ai_family,
                          p-&gt;ai_socktype,
                          p-&gt;ai_protocol);
          if(sockfd == -1)
              continue;
  
          // try to connect
          status = connect(sockfd, 
                           p-&gt;ai_addr, 
                           p-&gt;ai_addrlen);
          if(status == -1) {
              close(sockfd);
              continue;
          }
  
          break;
      }
  
      // no valid socket found
      if(p == NULL) {
          if(errorType) *errorType = IEFConnectError;
          return status;
      }
  
      // GET RID OF INFOS
      freeaddrinfo(infos);
  
      // SEND HTTP REQUEST
      // calculate request length and make
      requestLength = strlen("GET ") + strlen(filepath) + strlen(" HTTP\1.0\n") + strlen("Host: ") + strlen(host) + strlen(":80\n\n") + 1;
      request = malloc(sizeof(char) * requestLength);
      snprintf(request, requestLength, "GET %s HTTP\1.0\nHost: %s:80\n\n", filepath, host);
  
      // send
      status = send(sockfd,
                    request,
                    requestLength - 1,
                    0);
      if(status == -1) {
          if(errorType) *errorType = IEFSendError;
          close(sockfd);
          return errno;
      }
  
      // RECEIVE DATA
      receivedBytes = recv(sockfd, 
                           buffer, 
                           maxSize, 
                           0);
      if(receivedBytes == -1 || receivedBytes == 0) {
          if(errorType) *errorType = IEFReceiveError;
          close(sockfd);
          return receivedBytes;
      }
  
      // SET POINTERS
      if(errorType) *errorType = IEFNoError;
      if(rB) *rB = receivedBytes;
      return 0;
  }
  
  void *
  IEFDownerGetInAddr(struct sockaddr *sa) {
      // IP4
      if(sa-&gt;sa_family == AF_INET)
          return &amp;(((struct sockaddr_in *) sa)-&gt;sin_addr);
  
      // IP6
      return &amp;(((struct sockaddr_in6 *) sa)-&gt;sin6_addr);
  }
  
  int
  IEFDownerGetContentLengthOfPage(const char *httpHeader, int *contentLength) {
      register int i;
      int length;
      char *next;
      char *completeHeader;
      char *header;
      char *fieldName;
  
      // MAKE HEADER MUTABLE
      completeHeader = malloc(sizeof(char) * (strlen(httpHeader) + 1));
      strcpy(completeHeader, httpHeader);
      header = completeHeader;
  
      // SEARCH FOR LINE
      // loop for all lines
      next = header;
      do {
          header = next;
  
          // replace newline
          next = strstr(header, "\n");
          if(next == NULL) {
              free(header);
              return -1;
          }
          *next = '\0';
          for(i = 0; i != strlen("\n"); i++)
              next++;
  
      } while (strcasestr(header, "Content-Length:") == NULL);
  
      // SCAN INTEGER
      fieldName = strcasestr(header, "Content-Length:");
      for(i = 0; i != strlen("Content-Length:"); i++)
          fieldName++;
  
      sscanf(fieldName, "%d", &amp;length);
      if(contentLength) *contentLength = length;
  
      free(completeHeader);
      return 0;
  }
  
  int
  IEFDownerGetSizeOfHeader(const char *received, int *headerSize) {
      int length;
      int receivedLength;
      char *next;
      char *completeHeader;
      char *header;
      char *checkChar;
      int firstNonEmptyLineFound;
      int emptiesFound;
  
      // MAKE HEADER MUTABLE
      completeHeader = malloc(sizeof(char) * (strlen(received) + 1));
      strcpy(completeHeader, received);
      header = completeHeader;
  
      // SEARCH FOR FIRST NON EMPTY LINE
      receivedLength = strlen(header);
      firstNonEmptyLineFound = 0;
      for(next = header; *next != (char)NULL; next++) {
          printf("%c",*next);
          if(*next != '\n' &amp;&amp; *next != '\r') {
              firstNonEmptyLineFound = 1;
              printf("\nFirst Non Empty Found\n\n");
              next++;
              break;
          }
      }
      if(firstNonEmptyLineFound == 0) {
          free(completeHeader);
          return -1;
      }
  
      // SEARCH FOR FIRST EMPTY LINE
      emptiesFound = 0;
      for(; *next != (char)NULL; next++) {
          checkChar = next;
          printf("%c", *checkChar);
          if(*checkChar == '\n' || *checkChar == '\r') {
              checkChar++;
              printf("%c", *checkChar);
              if(*checkChar == '\n' || *checkChar == '\r') {
                  emptiesFound = 1;
                  printf("Empty Line Found\n\n");
                  break;
              }
          }
      }
      if(emptiesFound == 0) {
          free(completeHeader);
          return -1;
      }
  
      // GET END OF HEADER
      for(; *next != (char)NULL; next++) {
          printf("%c", *next);
          if(*next != '\n' &amp;&amp; *next != '\r') {
              printf("End of header found");
              break;
          }
      }
  
      // INSERT NULL
      *next == '\0';
      length = strlen(header);
  
      if(headerSize) *headerSize = length;
  
      free(completeHeader);
      return 0;
  }
  </code></pre>
  
  <p><strong>main.c</strong></p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;stdarg.h&gt;
  #include "IEFDowner.h"
  
  
  #define SERVERNAME "developerief2.site11.com"
  #define PROTOCOL "80"
  #define FILENAME "http://developerief2.site11.com/welcome/welcome.php"
  #define MAXHEADERSIZE (1024*1024)
  #define DESTFILE "/Users/ief2/Desktop/file.png"
  
  void errorOut(int status, const char *format, ...);
  
  int main (int argc, const char * argv[]) {
      int status;             // return status
      void *headerData;       // header data
      int headerSize;         // size of header
      long rB;                // received bytes
      int pageSize;           // size of bytes of page
  
      // GET PAGE SIZE
      // get data
      headerData = malloc(1024);
      status = IEFDownerHTTPDownload(SERVERNAME, 
                                     FILENAME, 
                                     (void *)headerData, 
                                     1024 - 1, 
                                     &amp;rB, NULL);
      if(status != 0)
          errorOut(status, "An error occured while downloading header info\n");
  
      // null terminate data
      ((char *)headerData)[rB] = '\0';
  
      // get size
      status = IEFDownerGetContentLengthOfPage((const char *)headerData, &amp;pageSize);
      if(status != 0)
          errorOut(status, "An error occured while retreiving page size\n");
  
      printf("Page Size: %d\n", pageSize);
      printf("---------------------\n%s\n---------------------\n", headerData);
  
      // CALCULATE HEADER SIZE
      status = IEFDownerGetSizeOfHeader(headerData, &amp;headerSize);
      if(status != 0)
          errorOut(status, "An error occured while getting the header size\n");
      printf("Header Size: %d\n", headerSize);
  
      return 0;
  }
  
  void errorOut(int status, const char *format, ...) {
      va_list args;
      va_start(args, format);
      vfprintf(stderr, format, args);
      va_end(args);
      exit(status);
  }
  </code></pre>
  
  <p>The result I'm getting is not correct. I'm getting the end of the response after the first line already:</p>
  
  <pre><code>[Session started at 2010-08-03 21:32:47 +0000.]
  Page Size: 3600
  ---------------------
  HTTP/1.1 200 OK
  Date: Tue, 03 Aug 2010 19:32:44 GMT
  Server: Apache
  X-Powered-By: PHP/5.2.11
  Content-Length: 3600
  Connection: close
  Content-Type: text/html
  
  &lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;
  
  &lt;html xml:lang="en"&gt;
  &lt;!-- HEAD --&gt;
  &lt;head&gt;
      &lt;meta http-equiv="Content-type" content="text/html; charset=utf-16" /&gt;
      &lt;meta name="description" content="A site containing quality freeware for Mac OS X Tiger and newer. All applications are made by a 14 year old developer who masters AppleScript Studio, Objective-C, HTML, PHP, CSS and JavaScript." /&gt;
      &lt;meta name="keywords" content="free, freeware, mac os x, 10, tiger, leopard, mac os x, young, developer, freeware, AppleScript, Studio, Xcode, Objective-C, Cocoa" /&gt;
      &lt;title&gt;Free OSX Software :: Welcome&lt;/title&gt;
  
      &lt;!-- Shared Styles --&gt;
      &lt;link rel="stylesheet" href="../site-style/styles.css" type="text/css" media="screen" title="no title" charset="utf-8" /&gt;
  &lt;/head&gt;
  
  &lt;!-- PAGE CONTENTS --&gt;
  &lt;body&gt;
  
  ---------------------
  H
  First Non Empty Found
  
  TTP/1.1 200 OK
  Empty Line Found
  
  
  DEnd of header foundHeader Size: 1023
  
  The Debugger has exited with status 0.
  </code></pre>
  
  <p>I hope somebody can help me,
  ief2</p>