I bumped into this one when trying to compare hashes of the message body while implementing HMAC authentication on an nginx /python server.

The drupal_http_request() function adds addition CRLFs before and after the body. We found this because file_get_contents() using stream_context_set_option didn't cause this to happen.

see:
http://tools.ietf.org/html/rfc2616#page-31

generic-message = start-line
*(message-header CRLF)
CRLF
[ message-body ]
start-line = Request-Line | Status-Line

and http://www.tcpipguide.com/free/t_HTTPRequestMessageFormat.htm

Only local images are allowed.
Uploaded with plasq's Skitch!
--- common.inc	(revision 3505)
+++ common.inc	(working copy)
@@ -498,9 +498,9 @@
 
   $request = $method .' '. $path ." HTTP/1.0\r\n";
   $request .= implode("\r\n", $defaults);
-  $request .= "\r\n\r\n";
+  $request .= "\r\n";
   if ($data) {
-    $request .= $data ."\r\n";
+    $request .= $data;
   }
   $result->request = $request;
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

JacobSingh’s picture

sorry, lost the screenshot in the filter:
http://img.skitch.com/20081210-rac3y35j8mctj6qx1bc33dcpbe.jpg

pwolanin’s picture

Also, there is a bug here that strlen() may not return the correct length, due to mbstring.func_overload. Comments on the PHP site suggest getting the byte count using mb_strlen($string, '8bit'); if it is available.

pwolanin’s picture

Here's Jacob's missing screenshot:

Heine’s picture

Status: Active » Needs review
FileSize
633 bytes

First of all, drupal_http_request only supports HTTP 1.0 (RFC 1945).

The proposed patch is incorrect because implode creates for array('line1' => 'value', 'line2' => 'value', 'line3' => 'value') the following request part (spaces/linebreaks for clarity only):

METHOD path HTTP/1.0 CRLF
line 1 : value CRLF
line 2 : value CRLF
line 3 : value

The next few lines then make this

After $request .= "\r\n\r\n"; we have:

METHOD path HTTP/1.0 CRLF
line 1 : value CRLF
line 2 : value CRLF
line 3 : value CRLF
CRLF (the empty line)

Then we add the body.

Can the extra CRLF between header and body you noticed originate from a header value that already had a CRLF?

The CRLF after the entity body is forbidden by the BNF in RFC 1945 (4.1. Full request). drupal_http_request was added in revision 1.304, but I was unable to find the associated issue, so I do not know why the CRLF was added.

Attached patch removes the CRLF epilogue and also allows sending '0' as data.

JacobSingh’s picture

Status: Needs review » Reviewed & tested by the community

ah, indeed. I think a client lib I was using was pre-adding the \r\n on the last header.

Thanks, patch looks good to me.

Heine’s picture

Version: 6.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Needs review

Patch is against 7

keith.smith’s picture

Title: drupal_http_reqeust adds additional \r\n to message body against HTTP specs » drupal_http_request adds additional \r\n to message body against HTTP specs
pwolanin’s picture

Title: drupal_http_request adds additional \r\n to message body against HTTP specs » drupal_http_reqeust adds additional \r\n to message body against HTTP specs
Version: 7.x-dev » 6.x-dev
Status: Needs review » Reviewed & tested by the community
FileSize
630 bytes

Looks good, but maybe we should just check isset()?

keith.smith’s picture

Title: drupal_http_reqeust adds additional \r\n to message body against HTTP specs » drupal_http_request adds additional \r\n to message body against HTTP specs
Dave Reid’s picture

Version: 6.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Needs review

We should check if (isset($data)) since it may be null. That will still allow '0' to be sent as data.

pwolanin’s picture

Title: drupal_http_request adds additional \r\n to message body against HTTP specs » drupal_http_reqeust adds additional \r\n to message body against HTTP specs
Version: 7.x-dev » 6.x-dev
Status: Needs review » Reviewed & tested by the community
FileSize
667 bytes

here's a 6.x backport.

Heine’s picture

NULL will be cast to an empty string; why the need for isset?

Dave Reid’s picture

Version: 6.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Needs work

Cross-posted. :)

pwolanin’s picture

Version: 7.x-dev » 6.x-dev
Status: Needs work » Reviewed & tested by the community
FileSize
670 bytes

actually, talking with Heine, his version is probably better, since it's simpler and the NULL to string cast causes no problems.

here's a 6.x verison of his.

pwolanin’s picture

patch in #14 also applies to 5.x

Gábor Hojtsy’s picture

Version: 6.x-dev » 7.x-dev

Committed to 6.x. Moving back to 7.x, let's get this in there really :)

Status: Reviewed & tested by the community » Needs work

The last submitted patch failed testing.

Dave Reid’s picture

Status: Needs work » Needs review
FileSize
823 bytes

Reposting with a better patch name so it gets picked up by the testing bot. This is also the same as patch #4.

Dave Reid’s picture

Should we also maybe run a trim() or a preg_replace on the header values sent by drupal_http_request so we don't get extra line feeds where they shouldn't be (prevent the problem from #5)?

In drupal_http_request():

  foreach ($headers as $header => $value) {
    $defaults[$header] = $header .': '. trim($value);
    // Or...
    $defaults[$header] = $header .': '. preg_replace('/[\n\r]/', '', $value);
  }

That should probably be a separate issue, I know, but I just wanted to get some initial feedback.

Dave Reid’s picture

Well, from reading RCF 1945, it says that "Header fields can be extended over multiple lines by preceding each extra line with at least one SP or HT, though this is not recommended." trim() would be the appropriate since it will remove any line returns or spaces from the ends of the header value string.

Status: Needs review » Needs work

The last submitted patch failed testing.

Dave Reid’s picture

Completely messed the last patch up...trying again.

Dave Reid’s picture

Status: Needs work » Needs review

Status back to code needs review.

jlkreiss’s picture

FileSize
837 bytes

I dive into this issue, having a problem very close to it:

The code in drupal_http_request adds a default "Content-Length" header that takes strlen($data) as value.
Thus, when issuing a simple GET request with no body content, we still have this "Content-Length: 0" header.
That's a problem for some servers hardened by Apache mod_security who blindly reject GET requests with a Content-Length header (see example here: http://www.modsecurity.org/documentation/modsecurity-apache/1.9.3/html-m... , third rule from the end).
As a result, Aggregator is unable to retrieve a feed from this site : http://attacvillefranche.ouvaton.org/backend.php3 , for example (gets a 403 Forbidden response, due to mod_security rejecting the request).

Examining HTTP/1.0 RFC, I didn't find anything that forbids us to have a Content-Length field with no body following it, but given that it may cause problems with some servers and that it lacks coherence, I would suggest to clean up the code...

I propose to test $data before adding the default "Content-Length" header (see diff attachment).
Sorry for my english and my inexisting programming skills, I hope you nevertheless will find my comment usefull...

keith.smith’s picture

Title: drupal_http_reqeust adds additional \r\n to message body against HTTP specs » drupal_http_request adds additional \r\n to message body against HTTP specs
pwolanin’s picture

I think you can make this simple if($data) - there is no need to compare to an empty string.

Dries’s picture

I committed the patch in #22 but not the patch in #24. They seem to do different things. Let's keep talking about the patch in #24. (Ideally, we would create a new issue for #24).

Dave Reid’s picture

Status: Needs review » Fixed

Agreed, #24 should be a separate issue.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for two weeks with no activity.