Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I fake the client IP address in a unit test for a Mojolicious app?

In my Mojolicious app I need to use the client's IP address ($c->tx->remote_address) for rate limiting of a service. This works well.

I am now trying to build a unit test for this feature, but I am having trouble faking the client's IP in my tests.

First I thought that local_address in Mojo::UserAgent might do what I want, but that's where the user agent binds the application locally, and changing it breaks everything because it cannot find the app any more.

I then tried using Sub::Override to replace the remote_address in Mojo::Transaction, but that already applies to the client when I do $t->post_ok, it tries sending a request to the IP that doesn't exist because the remote address on the client side is the server's address, and I am stuck with a waiting blocking request that will never succeed because the server it wants does not exist.

You can use the following MCVE to try around. The expected result is for the tests to pass.

use strict;
use warnings;
use Test::More;
use Test::Mojo;
use Mojolicious::Lite;

get '/foo' => sub { my $c = shift; $c->render( text => $c->tx->remote_address ) };

my $t = Test::Mojo->new;
$t->get_ok('/foo')->content_like(qr/\Q127.0.0.1/);

# TODO change client IP address to 10.1.1.1
# in a way that the /foo route sees it
$t->get_ok('/foo')->content_like(qr/\Q10.1.1.1/);

done_testing;

I know how to do this with Catalyst and Dancer (or other Test::Plack based systems), but those approaches don't work here.

like image 391
simbabque Avatar asked Oct 26 '17 17:10

simbabque


1 Answers

The author of Mojolicious pointed out on IRC to look at the unit tests in the Mojo dist for the X-Forwarded-For header implementation, so I did.

We need to set the $ENV{MOJO_REVERSE_PROXY} to a true value in the unit test and restart the server, then send an X-Forwarded-For header with the new IP address and things will just work.

use strict;
use warnings;
use Test::More;
use Test::Mojo;
use Mojolicious::Lite;

get '/foo' => sub { my $c = shift; $c->render( text => $c->tx->remote_address ) };

my $t = Test::Mojo->new;
$t->get_ok('/foo')->content_like(qr/\Q127.0.0.1/);

{
    local $ENV{MOJO_REVERSE_PROXY} = 1;
    $t->ua->server->restart;
    $t->get_ok( '/foo' => { 'X-Forwarded-For' => '10.1.1.1' } )->content_like(qr/\Q10.1.1.1/);
}

done_testing;

The tests now pass.

ok 1 - GET /foo
ok 2 - content is similar
ok 3 - GET /foo
ok 4 - content is similar
1..4
like image 100
simbabque Avatar answered Oct 25 '22 16:10

simbabque