Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to capture full HTTP request data (headers and body) with PHP?

I have a problem implementing an API that works with Java, but fails to work with cURL. We've gone through everything so far and there must be something that is different between the requests that Java makes and what we make.

In PHP we can get header data by looking at $_SERVER['HTTP_*'] variables and we can get request body from file_get_contents('php://input'); But we cannot get the exact data sent from user agent to client.

Is it possible to get the full request, that user agent sends, with PHP? Headers and body included? If so, then how?

The only example I found is here, but this one gets the body the way I mentioned, while it gets headers by parsing through $_SERVER, which seems like a hack since it's never 100% of what was actually sent.

All help and tips are appreciated!

like image 679
kingmaple Avatar asked May 25 '12 08:05

kingmaple


2 Answers

for headers you can try apache_request_headers() and for body I dont know other method than file_get_contents('php://input');

like image 167
Vytautas Avatar answered Oct 06 '22 00:10

Vytautas


Old question, but for anyone needing to do this in the future... The best (probably only) way would be to take full control of the server by being the server.

Set up a socket server listening on port 80 (if this is all you need the server to do), or any other port if 80 is not available.

That way you can capture the request completely unmodified. Examples of basic socket servers are plentiful, here is a simplified version of the latest one I implemented, which will print the full request:

<?php
//Read the port number from first parameter on the command line if set
$port = (isset($argv[1])) ? intval($argv[1]) : 80;

//Just a helper
function dlog($string) {
    echo '[' . date('Y-m-d H:i:s') . '] ' . $string . "\n";
}

//Create socket
while (($sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
    dlog("socket_create() failed: reason: " . socket_strerror(socket_last_error()));
    sleep(1);
}

//Reduce blocking if previous connections weren't ended correctly
if (!socket_set_option($sock, SOL_SOCKET, SO_REUSEADDR, 1)) {
    dlog("socket_set_option() failed: reason: " . socket_strerror(socket_last_error($sock)));
    exit;
}

//Bind to port
$tries = 0;
while (@socket_bind($sock, 0, $port) === false) {
    dlog("socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)));
    sleep(1);
    $tries++;
    if ($tries>30) {
        dlog("socket_bind() failed 30 times giving up...");
        exit;
    }
}

//Start listening
while (@socket_listen($sock, 5) === false) {
    dlog("socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)));
    sleep(1);
}

//Makes it possible to accept several simultaneous connections
socket_set_nonblock($sock);

//Keeps track of active connections
$clients = array();

dlog("server started...");

while(true) {
    //Accept new connections
    while (($msgsock = @socket_accept($sock)) !== false) {
        //Prevent blocking
        socket_set_nonblock($msgsock);

        //Get IP - just for logging
        socket_getpeername($msgsock, $remote_address);

        //Add new client to array
        $clients[] = array('sock' => $msgsock, 'timeout' => time()+30, 'ip' => $remote_address);

        dlog("$remote_address connected, client count: ".count($clients));
    }
    //Loop existing clients and read input
    foreach($clients as $key => $client) {
        $rec = '';
        $buf = '';
        while (true) {
            //Read 2 kb into buffer
            $buf = socket_read($clients[$key]['sock'], 2048, PHP_BINARY_READ);

            //Break if error reading
            if ($buf === false) break;

            //Append buffer to input
            $rec .= $buf;

            //If no more data is available socket read returns an empty string - break
            if ($buf === '') break;
        }
        if ($rec=='') {
            //If nothing was received from this client for 30 seconds then end the connection
            if ($clients[$key]['timeout']<time()) {
                dlog('No data from ' . $clients[$key]['ip'] . ' for 30 seconds. Ending connection');

                //Close socket
                socket_close($client['sock']);

                //Clean up clients array
                unset($clients[$key]);
            }
        } else {
            //If something was received increase the timeout
            $clients[$key]['timeout']=time()+30;

            //And.... DO SOMETHING
            dlog('Raw data received from ' . $clients[$key]['ip'] . "\n------\n" . $rec . "\n------");
        }
    }

    //Allow the server to do other stuff by sleeping for 50 ms on each iteration
    usleep(50000);
}

//We'll never reach here, but some logic should be implemented to correctly end the server
foreach($clients as $key => $client) {
    socket_close($client['sock']);
}
@socket_close($sock);
exit;

To start the server on port 8080 just run php filename.php 8080 from a shell.

like image 24
Mikk3lRo Avatar answered Oct 06 '22 00:10

Mikk3lRo