App-DuckPAN
view release on metacpan or search on metacpan
lib/App/DuckPAN/Web.pm view on Meta::CPAN
package App::DuckPAN::Web;
our $AUTHORITY = 'cpan:DDG';
# ABSTRACT: Webserver for duckpan server
$App::DuckPAN::Web::VERSION = '1021';
use Moo;
use DDG::Request;
use DDG::Test::Location;
use DDG::Test::Language;
use DDG::Meta::Data;
use Path::Tiny;
use Plack::Request;
use Plack::Response;
use Plack::MIME;
use HTML::Entities;
use HTML::TreeBuilder;
use HTML::Element;
use Data::Printer return_value => 'dump';
use HTTP::Request;
use HTTP::Headers;
use LWP::UserAgent;
use URI::Escape;
use JSON;
has blocks => ( is => 'ro', required => 1 );
has page_root => ( is => 'ro', required => 1 );
has page_spice => ( is => 'ro', required => 1 );
has page_css => ( is => 'ro', required => 1 );
has page_js => ( is => 'ro', required => 1 );
has page_locales => ( is => 'ro', required => 1 );
has page_templates => ( is => 'ro', required => 1 );
has server_hostname => ( is => 'ro', required => 0 );
has _our_hostname => ( is => 'rw' );
has _share_dir_hash => ( is => 'rw' );
has _path_hash => ( is => 'rw' );
has _rewrite_hash => ( is => 'rw' );
has ua => (
is => 'ro',
default => sub {
LWP::UserAgent->new(
agent => "Mozilla/5.0", #User Agent required for some API's (eg. Vimeo, IsItUp)
timeout => 5,
ssl_opts => { verify_hostname => 0 },
env_proxy => 1,
);
},
);
sub BUILD {
my ( $self ) = @_;
my %share_dir_hash;
my %path_hash;
my %rewrite_hash;
for (@{$self->blocks}) {
for (@{$_->only_plugin_objs}) {
if ($_->does('DDG::IsSpice')) {
$rewrite_hash{ref $_} = $_->rewrite if $_->has_rewrite;
my $rewrites = $_->alt_rewrites;
while(my ($short_name, $rewrite) = each %$rewrites){
$rewrite_hash{$short_name} = $rewrite;
$path_hash{$rewrite->path} = $short_name;
}
}
$share_dir_hash{$_->module_share_dir} = ref $_ if $_->can('module_share_dir');
$path_hash{$_->path} = ref $_ if $_->can('path');
}
}
$self->_share_dir_hash(\%share_dir_hash);
$self->_path_hash(\%path_hash);
$self->_rewrite_hash(\%rewrite_hash);
}
sub run_psgi {
my ( $self, $app, $env ) = @_;
$self->_our_hostname($env->{HTTP_HOST}) unless $self->_our_hostname;
my $request = Plack::Request->new($env);
my $response = $self->request($app, $request);
return $response->finalize;
}
my $has_common_js = 0;
sub request {
my ( $self, $app, $request ) = @_;
my $hostname = $self->server_hostname;
my @path_parts = split(/\/+/,$request->request_uri);
shift @path_parts;
lib/App/DuckPAN/Web.pm view on Meta::CPAN
POST => $to,
$h,
$post_body
);
}
else {
$req = HTTP::Request->new(
GET => $to,
$h
);
}
p($req->as_string);
$res = $self->ua->request($req);
if ($res->is_success) {
$body = $res->decoded_content;
# Encode utf8 api_responses to bytestream for Plack.
utf8::encode $body if utf8::is_utf8 $body;
warn "Cannot use wrap_jsonp_callback and wrap_string callback at the same time!" if $wrap_jsonp_callback && $wrap_string_callback;
if ($wrap_jsonp_callback && $callback) {
$body = $callback.'('.$body.');' unless $missing_envs;
}
elsif ($wrap_string_callback && $callback) {
$body =~ s/"/\\"/g;
$body =~ s/\n/\\n/g;
$body =~ s/\R//g;
$body = qq{$callback("'.$body.'");} unless $missing_envs;
}
$response->code($res->code);
$response->content_type($res->content_type);
}
else {
p($res->status_line, color => { string => 'red' });
my $errormsg = (pop @{[split'::', $spice_class]}). ": ".$res->status_line;
$body = '$("#message").removeClass("is-hidden").append("<div class=\"msg msg--warning\">'. $errormsg .'</div>");';
}
}
}
}
unless ($rewrite){
$response->status(404);
my $path = join "/", @path_parts;
my $errormsg = "ERROR: Rewrite not found - $path";
print "\n" . $errormsg . "\n";
$body = $errormsg;
}
}
elsif ($request->param('duckduckhack_ignore')) {
$response->status(204);
$body = "";
}
elsif ($request->param('duckduckhack_css')) {
$response->content_type('text/css');
$body = $self->page_css;
}
elsif ($request->param('duckduckhack_js')) {
$response->content_type('text/javascript');
$body = $self->page_js;
}
elsif ($request->param('duckduckhack_locales')) {
$response->content_type('text/javascript');
$body = $self->page_locales;
}
elsif ($request->param('duckduckhack_templates')) {
$response->content_type('text/javascript');
$body = $self->page_templates;
}
elsif ($request->param('q') && $request->path_info eq '/') {
my $query = $request->param('q');
$query =~ s/^\s+|\s+$//g; # strip leading & trailing whitespace
Encode::_utf8_on($query);
my $ddg_request = DDG::Request->new(
query_raw => $query,
location => test_location_by_env(),
language => test_language_by_env(),
);
my @results = ();
my @calls_nrj = ();
my @calls_nrc = ();
my @calls_script = ();
my %calls_template = ();
my @calls_goodie;
my @calls_fathead;
my @ids;
my $page = $self->page_spice;
my $uri_encoded_query = uri_escape_utf8($query, "^A-Za-z");
my $html_encoded_query = encode_entities($query);
my $uri_encoded_ddh = quotemeta(uri_escape('duckduckhack-template-for-spice2', "^A-Za-z0-9"));
$page =~ s/duckduckhack-template-for-spice2/$html_encoded_query/g;
$page =~ s/$uri_encoded_ddh/$uri_encoded_query/g;
# For debugging query replacement.
#p($uri_encoded_ddh);
#p($page);
my $root = HTML::TreeBuilder->new;
$root->parse($page);
##########
# FATHEAD
my $repo = $app->get_ia_type;
if ($repo->{name} eq "Fathead") {
my $output_txt = $app->fathead->output_txt;
if (my $result = $app->fathead->structured_answer_for_query($query)) {
p($result, colored => $app->colors);
push @calls_fathead, $result;
}
else {
# TODO Fallback to DDG API?
return $self->_no_results_error($query, $root);
}
}
###################
# SPICE & GOODIES
for (@{$self->blocks}) {
push(@results,$_->request($ddg_request));
}
( run in 1.043 second using v1.01-cache-2.11-cpan-ceb78f64989 )