2012-06-28 152 views
4

服务器返回HTTP标头和二进制文件;像这样:如何知道HTTP头部分何时结束?

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 

我想只复制二进制文件。但是如何知道标题部分何时结束?我试过检查一下这行是否包含\r\n\r\n,但看起来这个标准不适用于服务器响应,只适用于客户端。这给出:

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 

这里是C代码:

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:

我把continue控制了我的if语句:

if(isnheader == 0 && strstr(buffer, "\r\n\r\n") != NULL) { 
    isnheader = 1; 
    continue; 
} 

好,它按预期工作。但正如@Alnitak所提到的那样,这并不安全。

回答

2

您没有正确解析输入。这里有几件事你做错了:

  • 你的代码似乎暗示你的缓冲区最多只能包含一行标题数据。但是,recv()不会在数据的“行”上运行,而会在二进制数据块上运行。因此,如果您告诉它缓冲区长度为128个字节,它将尝试用128个字节的数据填充缓冲区(如果可用的话)(即使128个字节的数据包含多个“行”)。
  • 你的代码没有考虑到头部中断的“\ r \ n”可能被两个不同的recv()调用拉到你的缓冲区,这会阻止你的代码识别头部中断。
  • 如果确实发现了标头中断(如果标头的大小恰到好处,可能会发生这种情况),您将最终推出带有终止符“\ r \ n”和标头中断(“\ r \ n \ n“)到你的二进制数据副本中。

我写了一个快速的功能,应该找HTTP标头的结尾,写服务器的响应文件流的其余部分:

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; 
} 

我没有测试的代码,所以它可能有简单的错误,但它应该指向正确的方向来解析C中缓冲区的字符串数据。

+0

非常感谢您的回答。我试过了,但我保存了0个字节。 :( – Jack

+0

@Jack我刚刚完成的测试在测试程序的例子,我做了一些调整,适当转移缓冲数据,转而使用read()和指数(),并更新了代码既要工作“ \ r \ n'和'\ n'行结束符 –

+0

非常感谢。 – Jack

16

头部和主体都应该由\r\n\r\n

(RFC 2616的4.1节)被分离但是一些服务器可能省略\r和只发送\n线,特别是如果他们不能消毒任何CGI供给头以确保它们包括\r

您还需要考虑如何分块读取 - 分隔符可能会跨越128字节的块,这将阻止strstr调用的工作。

+0

感谢您的回答!但我仍然不知道如何通过C代码进行过滤。你让我想起它,主要是因为'strstr()'不好检查它和大小,我选择了128,因为它通常是一些文件中一行的长度。 – Jack

相关问题