May 13, 2011, 12:57 p.m.
posted by stackme
Debugging the Raw HTTP Exchange
You want to analyze the HTTP request a browser makes to your server and the corresponding HTTP response. For example, your server doesn't supply the expected response to a particular request so you want to see exactly what the components of the request are.
For simple requests, connect to the web server with Telnet and type in the request headers. A sample exchange is shown in Figure.
Sending a request with Telnet
When you type in request headers, the web server doesn't know that it's just you typing and not a web browser submitting a request. However, some web servers have timeouts on how long they'll wait for a request, so it can be useful to pretype the request and then just paste it into Telnet. The first line of the request contains the request method (get), a space and the path of the file you want (/), and then a space and the protocol you're using (HTTP/1.0). The next line, the Host header, tells the server which virtual host to use if many are sharing the same IP address. A blank line tells the server that the request is over; it then spits back its response: first headers, then a blank line, and then the body of the response. The Netcat program (http://netcat.sourceforge.net/) is also useful for this sort of task.
Pasting text into Telnet can get tedious, and it's even harder to make requests with the post method that way. If you make a request with HTTP_Request, you can retrieve the response headers and the response body with the getResponseHeader( ) and getResponseBody( ) methods, as shown in Figure.
Getting response headers with HTTP_Request
To retrieve a specific response header, pass the header name to getResponseHeader( ). The header name must be all lowercase. Without an argument, getResponseHeader( ) returns an array containing all the response headers. HTTP_Request doesn't save the outgoing request in a variable, but you can reconstruct it by calling the _buildRequest( ) method, as shown in Figure.
Getting request headers with HTTP_Request
The request that Figure is something like:
POST /submit.php HTTP/1.1 User-Agent: PEAR HTTP_Request class ( http://pear.php.net/ ) Content-Type: application/x-www-form-urlencoded Connection: close Host: www.example.com Content-Length: 12 monkey=uncle
Accessing response headers with the http stream is possible, but you have to use a function such as fopen( ) that gives you a stream resource. One piece of the metadata you get when passing that stream resource to stream_get_meta_data( ) after the request has been made is the set of response headers. Figure demonstrates how to access response headers with a stream resource.
Getting response headers with the http stream
stream_get_meta_data( ) returns an array of information about the stream. The wrapper_data element of that array contains wrapper-specific data. For the http wrapper, that means the response headers, one per subarray element. Figure prints something like:
HTTP/1.1 200 OK Date: Sun, 07 May 2006 18:24:37 GMT Server: Apache/2.2.2 (Unix) Last-Modified: Sun, 07 May 2006 01:58:12 GMT ETag: "1348011-7-16167500" Accept-Ranges: bytes Content-Length: 7 Connection: close Content-Type: text/plain
The fopen( ) function accepts an optional stream context. Pass it as the fourth argument to fopen( ) if you want to use one. (The second argument is the mode and the third argument is the optional flag indicating whether to use include_path in looking for a file.)
With cURL, include response headers in the output from curl_exec( ) by setting the CURLOPT_HEADER option, as shown in Figure.
Getting response headers with cURL
To write the response headers directly to a file, open a filehandle with fopen( ) and set CURLOPT_WRITEHEADER to that filehandle, as shown in Figure.
Writing response headers to a file with cURL
cURL's CURLOPT_VERBOSE option causes curl_exec( ) and curl_close( ) to print out debugging information to standard error, including the contents of the request, as shown in Figure.
Verbose output from cURL
Figure prints something like:
* Connected to www.example.com (10.1.1.1) > POST /submit.php HTTP/1.1 Host: www.example.com Pragma: no-cache Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */* Content-Length: 23 Content-Type: application/x-www-form-urlencoded monkey=uncle&rhino=aunt* Connection #0 left intact * Closing connection #0
Because cURL prints the debugging information to standard error and not standard output, it can't be captured with output buffering. You can, however, open a filehandle for writing and set CURLOUT_STDERR to that filehandle to divert the debugging information to a file. This is shown in Figure.
Writing cURL verbose output to a file
Another way to access response headers with cURL is to write a "header function." This is similar to a cURL "write function" except it is called to handle response headers instead of the response body. Figure defines a HeaderSaver class whose header( ) method can be used as a header function to accumulate response headers.
Using a cURL header function
The HTTP 1.1 standard specifies that headers can span multiple lines by putting at least one space or tab character at the beginning of the additional lines of the header. The header arrays returned by stream_get_meta_data( ) and HTTP_Request::getResponseHeader( ) do not properly handle multiline headers, though. The additional lines in a header are treated as separate headers. The code in Figure, however, correctly combines the additional lines in multiline headers.
Documentation on curl_setopt( ) at http://www.php.net/curl-setopt, on stream_get_meta_data( ) at http://www.php.net/stream_get_meta_data, on fopen( ) at http://www.php.net/fopen, and on the PEAR HTTP_Request class at http://pear.php.net/package/HTTP_Request; the syntax of an HTTP request is defined in RFC 2616 and available at http://www.w3.org/Protocols/rfc2616/rfc2616.html. The rules about multiline message headers are in Section 4.2: http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2. The netcat program is available from the GNU Netcat project at http://netcat.sourceforge.net/.