Tommy Stanton

Computer programmer and banjo picker

Testing an HTTPS POST request

Update

An alternative to the Dancer-powered program that I present here might be Test::Fake::HTTPD, though I have yet to try it. If you use Test::Fake::HTTPD instead of Dancer, you would still use the App::TLSMe-related code in your program. brian d foy offers a tutorial in his blog post.

See run_https_server()--It looks like you should be able to do OO-style (new, then run) if you pass a 'scheme' argument to new (although this is not documented).

Lastly, I also came across the related HTTP::Daemon::SSL, though I have not tried that either.


Recently, I wanted to test an application that was going to make an HTTP POST request via HTTPS (to be subsequently referred to as "HTTPS POST"). The content-type of the request was supposed to be application/x-www-form-urlencoded.

In order to verify the request, and to really see what the request was going to look like, I went down a path of writing a small Perl-powered program. The goal was to quickly and to temporarily start an HTTP test server to be used for debugging.

By using CPAN modules like Dancer and App::TLSMe, I was able to run a local HTTP server wrapped in TLS (SSL). The resulting program, https-test-server, provides a simple way to inspect the HTTPS POST request:

#!/usr/bin/env perl
use Dancer;
use Data::Dumper;

use App::TLSMe;
use Proc::Fork qw( run_fork child );

my $http_port  = 3001;
my $https_port = 8080;

set port   => $http_port;
set logger => 'console';

run_fork { child {
    App::TLSMe->new(
        listen  => "0.0.0.0:${https_port}",
        backend => "0.0.0.0:${http_port}",
        method  => "any",
    )->run;
} };

get '/' => sub {
    return 'Is SSL working? :)';
};

post '/post_test' => sub {
    local $Data::Dumper::Indent = 0;

    debug 'Content-type: ' . request->content_type;
    debug 'Method used: ' . request->method;
    debug 'Body: ' . request->body;
    debug 'Body params: ' . Dumper(  { params('body') } );
    debug 'Query params: ' . Dumper( { params('query') } );
};

dance;

You probably need to install some dependencies for this program before you can run it. :) You can try this fun automated method, assuming that you have cpanm installed:

$ cpanm Perl::PrereqScanner && \
scan_prereqs --combine https-test-server | awk '{ print $1 }' | cpanm

Now, run the Perl-powered HTTP server, which will wrap itself in TLS:

$ perl https-test-server
>> Dancer 1.31 server 20543 listening on http://0.0.0.0:3001
== Entering the development dance floor ...

As specified in the source of https-test-server, Dancer will run the HTTP server at port 3001. The HTTPS entry point will be at port 8080, as provided by App::TLSMe.

Start by hitting the Dancer application at port 3001: point your browser to http://localhost:3001.

You will get a web page with the message "Is SSL working? :)" in your browser. Of course, the answer to that question is: "Not yet." :)

Now hit the Dancer application at port 8080: point your browser to https://localhost:8080. [1] You see that same message, but now it's coming at you through an encrypted connection. Say it with me: "Oh yeah!" :)

Now it's time to see what an HTTPS POST will do, via the /post_test route in the web application. In another shell, use curl(1) to POST form data. In this case, I sent a query string consisting of some familiar words (foo => bar and baz => quux):

$ curl --insecure --data 'foo=bar&baz=quux' https://localhost:8080/post_test
1

Switch back to the other shell. You should see some debugging statements like this:

[20543] debug @0.001974> [hit #1]Content-type: application/x-www-form-urlencoded in https-test-server l. 29
[20543] debug @0.002348> [hit #1]Method used: POST in https-test-server l. 30
[20543] debug @0.002645> [hit #1]Body:  foo=bar&baz=quux in https-test-server l. 31
[20543] debug @0.003818> [hit #1]Body params: $VAR1 = {'baz' => 'quux',' foo' => 'bar'}; in https-test-server l. 32
[20543] debug @0.004271> [hit #1]Query params: $VAR1 = {}; in https-test-server l. 33

The content-type is correct, the method is correct, and the sent form data is present in both the body and in the body params. Excellent! :) Now you can use the https://localhost:8080/post_test URL as an endpoint anywhere else to see what a POST request will look like.

Lastly, I'll note that in taking a tip from an old Dancer mailing list thread, I had tried to follow the instructions in the HTTP::Server::Simple documentation to subclass the module, in order to enable the SSL layer underneath. That wasn't working, so luckily I came across App::TLSMe after seeing a mention of it in the PocketIO documentation. It works beautifully. Thanks Viacheslav! :)

[1] You'll likely get a scary message from the browser about how it's an insecure connection. That's because App::TLSMe uses a phony SSL certificate by default. Fine by me: who pays for legitimate SSL certificates for development environments? :)

blog comments powered by Disqus