diff --git a/Makefile.PL b/Makefile.PL index f6cf107..6e65fd5 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -17,7 +17,6 @@ requires 'Encode::JP::Mobile' => 0.25; requires 'HTML::ReplacePictogramMobileJp' => 0.06; requires 'HTML::Tree' => 3.23; requires 'HTML::TreeBuilder::XPath' => 0.09; -requires 'HTTP::Engine' => '0.03001'; requires 'Params::Validate' => 0.91; requires 'Template' => 2.19; requires 'UNIVERSAL::require' => '0.11'; @@ -27,8 +26,10 @@ requires 'HTTP::MobileAttribute' => 0.13; requires 'Path::Class'; requires 'HTTP::Session' => 0.29; requires 'HTTP::Cookies'; # part of LWP +requires 'Test::More' => 0.88; test_requires('Test::More'); +test_requires('Test::Requires'); features( 'Better Encoding detection' => [ diff --git a/lib/Moxy.pm b/lib/Moxy.pm index 92a8657..8e77a07 100644 --- a/lib/Moxy.pm +++ b/lib/Moxy.pm @@ -29,6 +29,9 @@ use URI::Heuristic qw(uf_uristr); use URI; use YAML; use Time::HiRes (); +use Plack::Response; +use Moxy::Request; +use HTTP::Message::PSGI; use HTTP::MobileAttribute plugins => [ qw/CarrierLetter IS/, { @@ -70,6 +73,18 @@ sub assets_path { }; } +sub res { + Plack::Response->new(@_); +} +sub HTTP::Response::to_plack_response { + my $self = shift; + return res( + $self->code, + $self->headers, + $self->content, + ); +} + # ------------------------------------------------------------------------- sub run_hook_and_get_response { @@ -154,6 +169,17 @@ sub rewrite_html { return $result; } +sub to_app { + my ($self) = @_; + sub { + my $env = shift; + my $req = Moxy::Request->new($env); + my $res = $self->handle_request($req); + $res->content_length( length($res->content) ); # adjust content-length. + $res->finalize(); + }; +} + sub handle_request { my ($self, $req) = @_; @@ -184,12 +210,12 @@ sub handle_request { my $auth = join(',', $req->headers->authorization_basic); if ($state->isa('Moxy::Session::State::BasicAuth') && !$auth) { $self->log(debug => 'basicauth'); - return HTTP::Engine::Response->new( - status => 401, - headers => { + return res( + 401, + [ WWW_Authenticate => qq{Basic realm="Moxy needs basic auth.Only for identification.Password is dummy."}, - }, - body => 'authentication required', + ], + 'authentication required', ); } else { $self->log(debug => "session: state: $state, store: $store"); @@ -214,7 +240,7 @@ sub _make_response { my $self = shift; my %args = validate( @_ => +{ - req => { isa => 'HTTP::Engine::Request', }, + req => { isa => 'Moxy::Request', }, session => { type => OBJECT }, } ); @@ -260,11 +286,10 @@ sub _make_response { } my $redirect = $base . '/' . uri_escape($location); $self->log(debug => "redirect to $redirect"); - return HTTP::Engine::Response->new( - status => 302, - headers => { + return res( + 302, [ Location => $redirect, - }, + ], ); } else { my $content_type = $res->header('Content-Type'); @@ -275,9 +300,7 @@ sub _make_response { $res->content( encode($res->charset, rewrite_css($base, decode($res->charset, $res->content), $url), Encode::FB_HTMLCREF) ); } - my $response = HTTP::Engine::Response->new(); - $response->set_http_response($res); - return $response; + return $res->to_plack_response(); } } else { # please input url. @@ -305,9 +328,7 @@ sub _make_response { mobile_attribute => HTTP::MobileAttribute->new('KDDI-KC26 UP.Browser/6.2.0.7.3.129 (GUI) MMP/2.0'), session => $args{session}, ); - my $res = HTTP::Engine::Response->new; - $res->set_http_response($response); - $res; + return $response->to_plack_response(); } } diff --git a/lib/Moxy/Request.pm b/lib/Moxy/Request.pm new file mode 100644 index 0000000..88ad5e8 --- /dev/null +++ b/lib/Moxy/Request.pm @@ -0,0 +1,16 @@ +package Moxy::Request; +use strict; +use warnings; +use base qw/Plack::Request/; + +sub as_http_request { + my $self = shift; + return HTTP::Request->new( + $self->method, + $self->uri, + $self->headers, + $self->raw_body, + ); +} + +1; diff --git a/moxy.pl b/moxy.pl deleted file mode 100755 index eda8d1e..0000000 --- a/moxy.pl +++ /dev/null @@ -1,126 +0,0 @@ -#!/usr/bin/perl -use strict; -use warnings; -use FindBin; -use File::Spec::Functions; -use lib catfile( $FindBin::Bin, 'lib' ); -use Getopt::Long qw/GetOptions/; -use File::Temp (); -use Moxy; -use HTTP::Engine; -use Hash::Merge; -use Pod::Usage; # core module - -&main; exit; - -sub main { - GetOptions( - 'daemonize' => \my $daemonize, - 'port=i' => \my $port, - 'log=s' => \my $log, - 'timeout=i' => \my $timeout, - 'db=s' => \my $sessiondb, - 'assets=s' => \my $assets, - 'conf=s' => \my $conffile, - 'help' => \my $help, - ) or pod2usage(); - pod2usage() if $help; - - # set default value - $sessiondb ||= File::Temp->new( UNLINK => 1 ); - $port ||= 3128; - $log ||= 'info'; - $timeout ||= 16; - $assets ||= catfile( $FindBin::RealBin, 'assets' ); - - my $conf = +{ - global => { - server => { - module => 'ServerSimple', - args => { port => $port, } - }, - timeout => $timeout, - log => { level => $log, }, - session => { - state => { module => 'BasicAuth', }, - store => { - module => 'DBM', - config => { - file => "$sessiondb", # we need stringify for file::temp - dbm_class => 'NDBM_File', - }, - }, - }, - assets_path => $assets, - }, - }; - - if ($conffile) { - my $fconf = YAML::LoadFile($conffile); - Hash::Merge::set_behavior('RIGHT_PRECEDENT'); - $conf = Hash::Merge::merge($conf, $fconf); - } - - _run($daemonize, $conf); -} - -sub _run { - my ($daemonize_fg, $config) = @_; - - if ($daemonize_fg) { - if (my $pid = fork) { - exit 0; - } elsif (defined $pid) { - _stdio_close(); - _start($config); - } else { - die "fork failed: $@"; - } - } else { - _start($config); - } -} - -sub _start { - my ($config) = @_; - - my $moxy = Moxy->new($config); - HTTP::Engine->new( - interface => { - module => $config->{global}->{server}->{module}, - args => $config->{global}->{server}->{args}, - request_handler => sub { - my $req = shift; - $moxy->handle_request( $req ); - }, - } - )->run; -} - -sub _stdio_close { - close(STDIN); - close(STDOUT); - close(STDERR); - - open(STDIN, "+>/dev/null"); ## no critic. - open(STDOUT, "+>&STDIN"); ## no critic. - open(STDERR, "+>&STDIN"); ## no critic. -} - -__END__ - -=head1 NAME - -moxy.pl - bootstrap script for moxy - -=head1 SYNOPSIS - - $ moxy.pl - --daemonize # daemonize or not? - --port=4455 # specify your favorite port number - --log=debug # log level - --timeout=3 # timeout - --assets=/my/assets # path to assets dir - --db=~/.moxy.db # path to session db - --help # display this help message - diff --git a/moxy.psgi b/moxy.psgi index 57b41f2..f08a652 100644 --- a/moxy.psgi +++ b/moxy.psgi @@ -6,7 +6,6 @@ use lib File::Spec->catfile( dirname(__FILE__), 'lib' ); use File::Temp; use Plack::Builder; -use HTTP::Engine; use Moxy; # preload @@ -31,17 +30,10 @@ my $config = +{ }, }; my $moxy = Moxy->new($config); -my $engine = HTTP::Engine->new( - interface => { - module => 'PSGI', - request_handler => sub { - $moxy->handle_request( @_ ); - }, - } -); builder { enable_if { $_[0]->{REMOTE_ADDR} eq '127.0.0.1' } "Plack::Middleware::ReverseProxy"; - sub { $engine->run(@_) }; + + $moxy->to_app(); }; diff --git a/t/Plugins/UserAgentSwitcher.t b/t/Plugins/UserAgentSwitcher.t index ffb9862..d72754e 100644 --- a/t/Plugins/UserAgentSwitcher.t +++ b/t/Plugins/UserAgentSwitcher.t @@ -7,14 +7,14 @@ use FindBin; use File::Spec::Functions; use HTTP::Response; use HTTP::Request; -use HTTP::Engine; +use HTTP::Message::PSGI; my $moxy = Moxy->new( { global => { assets_path => catfile( $FindBin::Bin, '..', '..', 'assets' ), 'log' => { - level => 'debug', + level => 'info', }, session => { store => { @@ -35,16 +35,8 @@ sub test { GET => $input ); $req->authorization_basic('foobar'); - my $res = HTTP::Engine->new( - interface => { - module => 'Test', - args => {}, - request_handler => sub { - my $req = shift; - $moxy->handle_request($req); - }, - } - )->run($req); + my $app = $moxy->to_app(); + my $res = res_from_psgi($app->($req->to_psgi)); is $res->header('Location'), $expected; } diff --git a/t/mech.t b/t/mech.t new file mode 100644 index 0000000..a2c7353 --- /dev/null +++ b/t/mech.t @@ -0,0 +1,43 @@ +use strict; +use warnings; +use utf8; +use Test::Requires 'Test::WWW::Mechanize::PSGI'; +use Test::More; +use Moxy; +use FindBin; +use File::Spec::Functions qw/catfile/; + +binmode Test::More->builder->$_, ":utf8" for qw/output failure_output todo_output/; + +my $moxy = Moxy->new( + { + global => { + assets_path => catfile( $FindBin::Bin, '..', 'assets' ), + 'log' => { + level => 'info', + }, + session => { + store => { + module => 'Test', + config => {}, + }, + } + }, + plugins => [ + { module => 'UserAgentSwitcher' }, + ], + } +); +my $app = $moxy->to_app(); + +# ------------------------------------------------------------------------- + +my $mech = Test::WWW::Mechanize::PSGI->new(app => $app); +$mech->get('/'); +is $mech->res->code(), 401; +$mech->credentials('oh', 'my god'); +$mech->get_ok('/'); +$mech->get_ok('/http://wassr.jp/'); +$mech->content_contains('お気軽'); + +done_testing;