Download & Extend

Retrieve HTTP response headers

Project:SimpleTest
Version:6.x-2.x-dev
Component:Code
Category:feature request
Priority:normal
Assigned:Unassigned
Status:closed (fixed)

Issue Summary

There is currently no way to see if the correct response headers have been received.

Comments

#1

Status:active» needs review
AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php_.330582-1_DRUPAL-6--2.patch2.15 KBIdleFailed: Failed to apply patch.View details

#2

Forgot to account for cases where a header can be sent multiple times.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-2_DRUPAL-6--2.patch2.14 KBIdleFailed: Failed to apply patch.View details

#3

Project:SimpleTest» Drupal core
Version:6.x-2.x-dev» 7.x-dev
Component:Code» simpletest.module
Priority:critical» normal

This should be discussed and committed to core before being backported to SimpleTest 6.x-2.x.

#4

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-4_HEAD.patch1.99 KBIdleFailed: Failed to apply patch.View details

#5

I really miss this feature.

It would be useful if headers were returned in a name => value array, e.g. array('Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT', 'Last-Modified' => 'Fri, 07 Nov 2008 16:59:08 GMT', …), like $result->headers from drupal_http_request(), and/or if there was function to get a header with a specific name, e.g. drupalGetHeader($name).

#6

How would you check for two headers of the same type?

#7

According to RFC 2616, section 4.2 (last paragraph), two headers with the same name may be combined in one by concatenating the values separated by comma. The following two header blocks are equivalent:

Foo: 1
Foo: 2

Foo: 1,2

So in the name => value array I'd combine identical headers using comma.

#8

Curl may perform a dialog when HTTP authentication is being used, resulting in multiple requests. How would you keep the headers from different requests separate?

#9

I would only keep the headers from the last request like drupalGetContent() and assertResponse() do. If a test wishes to inspect the "intermediate" request, I suggest it call curlExec() with an option to do only one request (i.e. CURLOPT_FOLLOWLOCATION = FALSE etc). Not sure whether this is possible with the current version of drupal_web_test_case, though.

#10

Status:needs review» needs work

The last submitted patch failed testing.

#11

#12

This patch adds the ability to get response headers and add request headers. It adds a test to ensure the behaviour described in #9.

The patch also updates drupal_http_request() to use the $name/$value wording as discussed in #327269: drupal_page_cache_header() should compare timestamp where the patch was originally posted.

#13

... the patch:

AttachmentSizeStatusTest resultOperations
simpletest-headers-1.patch16.53 KBIdleFailed: Failed to apply patch.View details

#14

I'm using headers to test an HTTP authorization exchange, in which three sets of headers may be received before the message body is transmitted.

#15

This patch adds access to the response headers from intermediate requests (see the unit test for an example). Will that work?

AttachmentSizeStatusTest resultOperations
simpletest-headers-2.patch18.7 KBIdleFailed: Failed to apply patch.View details

#16

Thanks, that would work. Another solution would be to store the headers from each response as an arrays within a larger array. When $this->drupalGetHeaders() is invoked without arguments, the headers from the last response would be popped off the end of the array. Given the argument "all", the entire array could be returned.

I'll do a patch tomorrow.

#17

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-17.patch2.58 KBIdleFailed: 7362 passes, 0 fails, 2377 exceptionsView details
drupal_web_test_case.php-330582-17_D6.patch2.58 KBIgnored: Check issue status.NoneNone

#18

Status:needs review» closed (won't fix)

It is easy enough to override curlHeaderCallback() in your own test class to get the headers and do whatever you need to do with them. There is no need for this feature, given that it will waste memory while only being useful in a small number of test cases. I suggest we won't fix this, there is enough clutter in DWTC already.

#19

This feature would be useful in several other issues: #327269: drupal_page_cache_header() should compare timestamp, #201122: Drupal should support disabling anonymous sessions, #43462: cache_set and cache_get base_url brokenosity. I hope you will reconsider your decision.

Is the increased memory usage more than 1-2 KB total? We don't save the headers from old requests when drupalGet() is called again, and the old headers are garbage collected, so the 1-2 KB don't add up.

#20

Status:closed (won't fix)» needs review

Checking headers is not a specialized task. Many modules set headers. If they have to reimplement curlHeaderCallback(), there is a serious chance that things could break when drupal_web_test_case.php is updated.

#21

Here is what pwolanin is doing in #280934: Use httponly cookie support when available:

<?php
/**
+   * Implementation of curlHeaderCallback().
+   */
+  protected function curlHeaderCallback($ch, $header) {
+   
// Look for a Set-Cookie header.
+    if (preg_match('/^Set-Cookie.+$/i', $header, $matches)) {
+     
$this->saved_cookie = $header;
+    }
+
+    return
parent::curlHeaderCallback($ch, $header);
+  }
?>

Would that kill you?

Simpletest is already slow and already requires a lot of memory. No need to add clutter to the clutter.

#22

The memory required for page headers is insignificant compared to that required for page content. If you can show a significant performance problem from retrieving headers, we can address that. As I said, reimplementing curlHeaderCallback() easily breaks things. pwolanin broke SimpleTest error headers. Keeping SimpleTest uncluttered is the reason I produced the latest patch. My patch keeps things simple and ensures that future updates to SimpleTest core will not be rendered ineffective by people reimplementing curlHeaderCallback().

#23

One possible way to address the performance concerns, is to do the heavy lifting in drupalGetHeaders(). Is there a reason the header parsing could not be postponed until drupalGetHeaders() is called?

#24

@Darren Oh: pwolanin has not broken anything. Why would you think that the SimpleTest error headers are "broken"?

#25

With this patch, the headers are parsed on demand in drupalGetHeaders().

AttachmentSizeStatusTest resultOperations
simpletest-headers-3.patch18.61 KBIgnored: Check issue status.NoneNone

#26

Simpler patch.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-26_0.patch2.52 KBIgnored: Check issue status.NoneNone

#27

To sum up: The patches in #25 and #26 both implement drupalGetHeaders() in largely the same way. In addition, the one in #25 includes a test, adds drupalGetHeader($name) for getting only one header, updates the terminology for header names and values in common.inc to be consistent with than in drupal_web_test_case, and adds the ability to add request headers. Some of this is outside the scope of this bug but was included due to discussion in #327269: drupal_page_cache_header() should compare timestamp where earlier versions of the patch was posted.

#28

Thanks for implementing my suggestion. The patch in #25/#26 gives us the best of both worlds: no performance impact and a clean API to access the headers. Should pwolanin's test referenced in the discussion above be updated

Personally, I think drupalGetHeader($name) is a LOT more useful than drupalGetHeaders().

#29

Simpler version of c960657's patch, limited to the current issue.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-29.patch3.52 KBIdleFailed: Failed to apply patch.View details
drupal_web_test_case.php-330582-29_D6.patch3.59 KBIgnored: Check issue status.NoneNone

#30

I'm not sure I understand why $all_requests is useful. The PHPdoc is not really helping me understand the purpose of $all_requests.

#31

If a page is redirected, it would not be sufficient to examine only the last set of headers received. The same applies when using HTTP authentication. $all_requests may be a misleading name for this variable.

#32

It would be good if we could improve the variable name and its explanation. Darren's comment in #31 is already a better explanation than what we have right now.

Second, it looks like we need to grep the existing tests for header parsing and see if we can reuse our new API. Damien referenced some code from pwolanin that might be a candidate for clean-up.

It looks like we should be able to drive this patch home reasonably easily.

#33

Instead of Status-Line I suggest using a key that doesn't give the impression that this is an actual header, e.g. :status. This is not a valid HTTP header name (see the token production in RFC 2616, section 2.2).

+          list($name, $value) = array('Status-Line' => $header);
This is an unusual way of assigning two variables. It took me a few moments to figure out what was going on.

+          $headers[$request][$name] .= ','. trim($value);
Small coding style glitch (misses a space before the period).

+    // Save header to the _headers array.
+    $this->_headers[] = $header;

I don't think the comment adds anything that isn't already obvious.

Now that the headers are accessible through drupalGetHeaders(), I don't think that drupalHead() should put them in drupalGetContent() too.

#34

Suggestions implemented.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-34.patch6.47 KBIdleFailed: 7256 passes, 0 fails, 4 exceptionsView details
drupal_web_test_case.php-330582-34_D6.patch4.22 KBIgnored: Check issue status.NoneNone

#35

Status:needs review» needs work

The last submitted patch failed testing.

#36

Some additional comments:
+   * Gets the value of an HTTP header return by the last request.
I assume this should have been "returned".
I suggest the wording "HTTP response header" to emphasize that we are not referring to request headers (not that this is particularly unclear, but it doesn't hurt).

If you are removing CURLOPT_HEADER from drupalHead(), you may want to remove it from the rest of the file as well (originally suggested by mfb).

The :status pseudo-header should be described in the Doxygen comment.

The patch in #25 contains a test that might be relevant.

#37

Status:needs work» needs review

Implemented more suggestions. This patch will cause the current cache test to fail.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-37.patch7.73 KBIdleFailed: 7301 passes, 0 fails, 4 exceptionsView details
drupal_web_test_case.php-330582-37_D6.patch4.8 KBIgnored: Check issue status.NoneNone

#38

Status:needs review» needs work

The last submitted patch failed testing.

#39

Status:needs work» needs review

The test that failed was the page cache test, as expected. The test should succeed after this patch is committed.

#40

Status:needs review» needs work

AFAICT the test is failing due to this line:
+          list($name, $value) = array(':status' => $header);
There are no keys 0 and 1 in that array, so it triggers two notices. Replacing this with $name = ':status'; $value = $header; fixes the problem.

#41

Status:needs work» needs review

Fixed.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-41_D6.patch4.8 KBIgnored: Check issue status.NoneNone
drupal_web_test_case.php-330582-41.patch7.73 KBIdleFailed: Failed to apply patch.View details

#42

+          list($name, $value) = array(':status', $header);
I still think this is a peculiar way of assigning two variables. The bug mentioned in #40 is a good example of how unusual coding patterns are more likely to hide bugs, because it is harder to see what is going on. But I think it works now. Apart from that the code looks good.

The comments are now much better. Perhaps the handling of duplicate header names using comma deserves mention?

Don't you think a test would be relevant?

#43

Suggestions implemented. There is no test for drupalGetContent(), so I don't think we need one for drupalGetHeader() or drupalGetHeaders() either.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-43_D6.patch4.88 KBIgnored: Check issue status.NoneNone
drupal_web_test_case.php-330582-43.patch7.81 KBIdleFailed: Failed to apply patch.View details

#44

Status:needs review» needs work

The last submitted patch failed testing.

#45

Apart from the conflict with the #338403: Make standards for inherited methods consistent for all classes (including tests) that prevents the patch for applying, it looks like this wont work, because the cookie is set in an intermediate request:

-    $this->assertTrue(preg_match('/HttpOnly/i', $this->saved_cookie), t('Session cookie is set as HttpOnly.'));
+    $this->assertTrue(preg_match('/HttpOnly/i', $this->drupalGetHeader('Set-Cookie')), t('Session cookie is set as HttpOnly.'));

There is no test for drupalGetContent(), so I don't think we need one for drupalGetHeader() or drupalGetHeaders() either.

drupalGetHeaders() is considerably more complex than drupalGetContent(). Also, the construction of a complete test suite for Drupal is still work in progress, so some functions may not have tests even though they should have (specifically drupalGetContent() is used by almost every other unit test, so testing it directly as well probably isn't that relevant).

#46

Please be specific about what you would test.

#47

simpletest-headers-3.patch contains an example that covers the case where drupalGetHeaders(TRUE) returns headers for several requests. You could also test whether the two Cache-Control headers in drupal_page_header() are properly concatenated using comma. Then all code paths of the new functions would be covered.

The tests may not only prevent future bugs in Drupal itself but also help discover if cURL behaves differently on different platforms (it doesn't seem unlikely that e.g. the header callback has subtle differences in different versions of cURL and/or PHP). The test suite has helped me report several bugs that occur with PHP 5.2.0 but not with newer versions (e.g #310904: Use early fetch and document why).

#48

Status:needs work» needs review

Added tests and the ability to check all requests for a header. Also made header names case-insensitive.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-48.patch10.77 KBIdlePassed: 103 passes, 0 fails, 0 exceptionsView details

#49

Left an unused variable in the last patch.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-49_D6.patch7.52 KBIgnored: Check issue status.NoneNone
drupal_web_test_case.php-330582-49.patch10.71 KBIdlePassed: 52 passes, 0 fails, 0 exceptionsView details

#50

Status:needs review» needs work

This looks good. Nice that you did the lowercasing of header names.

Just two nits:

+   * be checked by default. However, if true is passed as the second argument,
true should be TRUE.

+   *   The name of the header to retrieve. Names are case-insensitive (see RFC
+   *   2616 section 4.2.
You missed a ).

#51

this is PHP5, if you are using, there is hardly any need for protected $_headers; the underscore where the underscore is the pseudo-visibility marker that Drupal uses. Just protected $headers will do.

#52

Status:needs work» needs review

Suggestions implemented.

AttachmentSizeStatusTest resultOperations
drupal_web_test_case.php-330582-52_D6.patch7.52 KBIgnored: Check issue status.NoneNone
drupal_web_test_case.php-330582-52.patch10.7 KBIdleRepository checkout: failed to checkout from [:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal-contrib/modules/simpletest/].View details

#53

Status:needs review» fixed

I reviewed this patch and it looks ready. I committed it to CVS HEAD. Thanks all.

#54

Project:Drupal core» SimpleTest
Version:7.x-dev» 6.x-2.x-dev
Component:simpletest.module» Code
Status:fixed» reviewed & tested by the community

Ready to be committed to SimpleTest (see #3).

#55

Status:reviewed & tested by the community» fixed

Got all passes.

Committed.

#56

Status:fixed» closed (fixed)

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