Debugging requests with cURL

For more than one time I had to debug HTTP request or response headers and other details. To do that, I use two techniques, both based on cURL library. I explain them ahead.

Technique #1: From the command line

This is the easiest way to debug. It doesn’t require writing any actual code. Just call curl program from the command line, as usual, adding a new param: -vvv. This will enable the highest verbosity level.

$ curl -vvv http://google.com
* Rebuilt URL to: http://google.com/
* Trying 2800:3f0:4001:802::200e...
* Connected to google.com (2800:3f0:4001:802::200e) port 80 (#0)
> GET / HTTP/1.1
> Host: google.com
> User-Agent: curl/7.43.0
> Accept: */*
> 
< HTTP/1.1 302 Found
< Cache-Control: private
< Content-Type: text/html; charset=UTF-8
< Location: http://www.google.com.br/?gfe_rd=cr&ei=bUG8V53JGcvK8gfp3L-YBg
< Content-Length: 262
< Date: Tue, 23 Aug 2016 12:28:29 GMT
< 
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="http://www.google.com.br/?gfe_rd=cr&amp;ei=bUG8V53JGcvK8gfp3L-YBg">here</A>.
</BODY></HTML>
* Connection #0 to host google.com left intact

As you can see in the example above, it outputs all request and response info.

It’s possible to output everything to a file, by adding > output_file.txt to the end of the command. Using our previous call:

$ curl -vvv http://google.com > output.txt

Well, one may now ask: if this is so easy, why do you have a second way to debug request? Following we’ll see why that.

Technique #2: From a PHP script

I’ve written on debugging cURL and PHP at Kettle.io Blog. Let’s say you have to send a dynamic header with the request, like a JWT authorization token. It’s not impossible to that from the command line, but it’s easier using programming. For those cases, I use the cURL PHP extension. Check out the script below.

$url = 'http://google.com';
$headers = [
    'Accept' => 'application/json',
];

/*
 * We're going to use the output buffer to store the debug info.
 */
ob_start();
$out = fopen('php://output', 'w');

$handler = curl_init($url);

/*
 * Here we set the library verbosity and redirect the error output to the 
 * output buffer.
 */
curl_setopt($handler, CURLOPT_VERBOSE, true);
curl_setopt($handler, CURLOPT_STDERR, $out);

$requestHeaders = [];
foreach ($headers as $k => $v) {
    $requestHeaders[] = $k . ': ' . $v;
}
curl_setopt($handler, CURLOPT_HTTPHEADER, $requestHeaders);
curl_setopt($handler, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($handler);
fclose($out);

/*
 * Joining debug info and response body.
 */
$data = ob_get_clean();
$data .= PHP_EOL . $response . PHP_EOL;
echo $data;

Now, you can customize this code to add some dynamic data to a header or any other request part. After doing that, run it using the PHP program from the command line:

$ php curldebug.php

P.S.: I’m assuming that you saved the script as curldebug.php.

As we did with the curl program, it’s possible to output everything to a file. Just append the > output_file.txt to the call.

Conclusion

Debugging requests can be a lifesaver when dealing with third-party APIs and other services. Headers may contain helpful info to find what is going wrong with that weird response body.

Propel + Symfony2 : Debugando queries em comandos

Quando no ambiente de desenvolvimento, em um projeto baseado no Symfony2, usar o webprofiler na interface web (a partir da barra que fica no rodapé das páginas) é uma mão na roda em várias situações. Mas no console geralmente não temos essa facilidade tão a mão, porém não é impossível acessá-la. Especificamente para as queries executadas através do Propel, é possível usar o seguinte trecho para fins de debug:

$profiler = $this->getContainer()->get('profiler');
$db = $profiler->get('propel');
$db->collect(new \Symfony\Component\HttpFoundation\Request(), new \Symfony\Component\HttpFoundation\Response()); // Stubs, não são usados pelo profiler
var_dump($db->getQueries());

Você pode dar uma olhada na classe Symfony\Bridge\Propel1\DataCollector\PropelDataCollector e conferir os métodos disponíveis.

Outros profilers podem ser acessados através do container, mas como a requisição (request) e a resposta (response) não estão disponíveis no console, pode ser que nem todos funcionem como esperado.

Testando comandos do Symfony que usam serviços da aplicação

Se você tem um comando no Symfony2 que usa serviços da aplicação, como um ORM por exemplo, e seguir o modelo que a documentação do framework fornece para escrever testes unitários, poderá ver erros com os a seguir:

Fatal error: Call to undefined method Symfony\Component\Console\Application::getKernel() in [...]/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php on line [...]

ao acessar o kernel da aplicação, a partir do comando, ou:

Fatal error: Call to a member function getParameter() on a non-object in [...]

ao tentar acessar algum método do container.

O primeiro caso, acontece quando você usa a classe Symfony\Component\Console\Application ao invés de Symfony\Bundle\FrameworkBundle\Console\Application. Verifique os uses da sua classe de testes e ajuste se for necessário.

A segunda exceção é lançada quando você está usando a classe Application correta, mas usando um kernel inválido na sua instanciação. Talvez exista outro modo de fazer isso, mas para mim funcionou extender a classe Symfony\Bundle\FrameworkBundle\Test\WebTestCase no lugar de \PHPUnit_Framework_TestCase e construir a aplicação da seguinte forma:

$application = new Application(self::createClient()->getKernel());

Script de deploy para projetos que usam git

Há alguns meses atrás desenvolvi um script de deploy para um projeto em que estou trabalhando. Com o tempo vi a possibilidade de melhorar alguns pontos, especialmente com o uso do componente Console do Symfony2,  e agora disponibilizo uma versão pública que pode ser usada com projetos armazenados em um repositório git.

https://github.com/straube/deploy

Uma das limitações dessa versão é que ela trabalha apenas com SSH, mas em breve devo adicionar suporte a FTP.