How can I omit the headers for a route when using Mojo? - perl

Mojo seems to want add headers to the response. Is there any method to suppress headers given a context object?
$r->get('/')->to( cb => sub {
my $c = shift;
# No headers for this response
} );

In my case, I was using Mojo::Server::CGI. You can see the problems on line 29 and on line 35
return undef if $self->nph && !_write($res, 'get_start_line_chunk');
...
return undef unless _write($res, 'get_header_chunk');
You can get around this by mucking with the internals,
$c->res->content->_headers->{header_buffer} = '';
$c->res->{start_buffer} = '';
But an even better way to is detect if anything has been written to STDOUT and to suppress the whole request if so,
# We withhold headers if anything has written to
# STDOUT. This is neccessary because some scripts, in-transition
# to Mojo will still use `print`, and output headers
if ( tell(*STDOUT) != 0 ) {
return undef;
}
That what I did anyway when I published Mojo::Server::CGI::LegacyMigrate

Related

Get blob uploaded data with pure Perl

In Javascript, I am sending a blob using XHR by the following code:
var v=new FormData();
v.append("EFD",new Blob([...Uint8Array...]));
var h=new XMLHttpRequest();
h.setRequestHeader("Content-type","multipart/form-data; charset=utf-8");
h.open("POST","...url...");
h.send(v);
In the server, I have created in Perl the following function, that suppose to implement CGI->param and CGI->upload:
# QS (Query String) receive in argument string for single parameter or array of many required parameters.
# If string been supplied: Return the value of the parameter or undef if missing.
# If array been supplied, a hash will be returned with keys for param names and their corresponding values.
# If the first argument is undef, then return hash with ALL available parameters.
sub QS {
my $b=$ENV{'QUERY_STRING'};
if($ENV{'REQUEST_METHOD'} eq "POST") {
read(STDIN,$b,$ENV{'CONTENT_LENGTH'}) or die "E100";
}
my $e=$_[0]; my $t=&AT($e); my $r={}; my #q=split(/&/,$b);
my %p=(); if($t eq "A") { %p=map { $_=>1 } #{$e}; }
foreach my $i(#q) {
my ($k,$s)=split(/=/,$i); $s=~tr/+//; $s=~s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
if($t eq "") { $r->{$k}=$s; }
elsif($t eq "A") { if($p{$k}) { $r->{$k}=$s; } }
elsif($k eq $_[0]) { return $s; }
}
return $r;
}
# AT is a function for determining type of an object, and also a quck way to distinguish between just a string and a number.
sub AT {
if(!defined $_[0]) { return ""; } my $v=ref($_[0]);
if($v eq "") { return ($_[0]*1 eq $_[0])?"N":"S"; }
my $k={"ARRAY"=>"A","HASH"=>"H"};
return $k->{$v}||$_[0]->{_obt}||$v;
}
So in the main program it will be called as:
my $EFD=&FW::QS("EFD"); # FW = The module name where QS and AT are.
When I issuing the POST from the client, the script in the server does not pop-up any errors, and does not terminates - it continues to run and run and run.... Endlessly.... Consuming 100% CPU time and 100% memory - without any explanation.
I have these in the beginning of the script, though:
use strict;
use warnings;
use diagnostics;
but it still behave in such a way that I need to kill the script in order to terminate it...
Anyone know what I did wrong...? No infinite loop here, as far as I know... If I change the Blob to regular classic way of "...url...?EFD=dhglkhserkhgoi" then it works just fine, but I want a Blob....
Thanks a lot
This QS function is only usable for POSTs with an application/x-www-urlencoded body, which yours isn't.

Perl CGI redirect after cancelling on a 401

I've currently got a small script running that sends a 401 to the client, upon cancelling and not providing user details the script will return nothing.
I'd like to send a redirect to the page they have come from instead.
The main subroutine looks like this;
#!usr/bin/perl
use strict;
use CGI;
sub checkAuth {
my ($user, $pass) = &getAuthUsers(); # Get the user and pass of already authenticated users.
unless ($user) {
&sendAuthenticationHeader(); # Send 401
}
# Check user against DB and return 1 for success.
if ( &checkUser($user, $pass) eq 'Y') { return 1 };
else { # This is the redirect I'm trying to issue.
my $cgi = CGI->new();
print $cgi->redirect($ENV{HTTP_REFERER}); # Redirect to the referer url
exit;
}
}
Unfortunately whenever I try to send new headers it's just received as plain text.
Any help is appreciated, thanks in advance.
sendAuthenticationHeader() emits a header with a 401 status code.
print $cgi->redirect($ENV{HTTP_REFERER}); emits a header with a 302 status code. Of course, since you've already emitted a header, this gets treated as the body.
There's no point to return a 401 if you want to redirect. Change your code to
sub checkAuth {
my ($user, $pass) = getAuthUsers();
if (!$user || !checkUser($user, $pass)) {
print CGI::redirect($ENV{HTTP_REFERER});
exit;
}
}
Notes:
Removed incorrect &. Don't tell Perl to ignore the prototype of subs. Address the underlying issue instead if required.
The return value of checkUser is boolean, so it should return either a true or a false value (e.g. 0 or 1), not two true values (e.g. N or Y). The above code assumed you fixed this.

Using variable for HTTP request headers with Perl

I am trying to write a function to create HTTP requests (POST and GET mostly) in Perl. I am keeping everything generic by using variables so that I don't have to worry about the type of request, the payload, headers, etc, however HTTP::Request->header() doesn't seem to like my variable:
my($req_type, $headers, $endpoint, $args, $request, $jsonfile) = #_;
my $ua = LWP::UserAgent->new;
my $req = HTTP::Request->new($req_type => $endpoint);
$req->content_type('application/json');
foreach (#$headers) {
$req->push_header($_);
}
$req->content($args);
$req->content($request);
print "request : ".$req->as_string;
I tried a few different approches, and using push_header got me the closest, but I realize it may not be the best solution. I think it might have something to do with single quotes getting passed in:
#headers = "'x-auth-token' => '$_token'";
I can post more of the code if it is helpful. I'm hoping some Perl guru will know exactly what I'm doing wrong. I'm sure it's something to do with the format of the string I'm passing in.
#headers = "'x-auth-token' => '$_token'";
The header function expects to be passed two arguments. The header name and the header value.
You are passing it one argument: a string containing a fragment of Perl code.
You need to format your data more sensibly.
my %headers = (
"x-auth-token" => $_token;
);
and
foreach my $header_name (keys %headers) {
$req->push_header($header_name => $headers{$header_name});
}

Perl, Net::Traceroute::PurePerl return value

This is a sub routine that I copied from CPAN. It works fine as it is when I run it from the command line. I have a similar function from Net::Traceroute that also works fine AND allows me to return the string with a SOAP call. The problem comes when I try to return the ~string(?) from the function below with a SOAP call.
sub tr {
use Net::Traceroute::PurePerl;
my $t = new Net::Traceroute::PurePerl(
backend => 'PurePerl', # this optional
host => 'www.whatever.com',
debug => 0,
max_ttl => 30,
query_timeout => 2,
packetlen => 40,
protocol => 'udp', # Or icmp
);
$t->traceroute;
return $t;
}
The output looks like a string except the last part of the string looks like this:
28 * * *
29 * * *
30 * * *
Net::Traceroute::PurePerl=HASH(0x11fa6bf0)
I don't know what is different about Net::Traceroute::PurePerl that won't allow me to return the value with SOAP since the Net::Traceroute version does allow me to return it with SOAP.
Edit:
To debug, I just edited the last lines like this:
#$t->traceroute;
#return $t;
$foo = "test";
return $foo;
Note that this returns "test" throught the SOAP call. However, if I uncomment $t->traceroute;, the SOAP call breaks. The client side gets nothing back through the SOAP call.
Anytime you see something that looks like "HASH(0x11fa6bf0)", try using Data::Dumper to show you what it is.
(Assuming your hashref is in $t)
use Data::Dumper;
print Dumper $t;
(Data::Printer will give you an even more useful look at what's inside your reference, particularly if you've got coderefs inside it, but the advantage of Data::Dumper is that it's been in the Perl core for a long time, so you should always have it available...)
Add $t->pretty_print; after $t->traceroute;

Perl -- 'Not a HASH reference' error when using JSON::RPC::Client

I'm a newbie in Perl.
I have a JSON-RPC server running at http://localhost:19000 and I need to call checkEmail() method.
use JSON::RPC::Client;
my $client = new JSON::RPC::Client;
my $url = 'http://localhost:19000';
my $callobj = {
method => 'checkEmail',
params => [ 'rprikhodchenko#gmail.com' ],
};
my $res = $client->call($url, $callobj);
if($res) {
if ($res->is_error) {
print "Error : ", $res->error_message;
}
else {
print $res->result;
}
}
else {
print $client->status_line;
}
When I try to launch it it tells following:
perl ./check_ac.pl
Not a HASH reference at /usr/local/share/perl/5.10.1/JSON/RPC/Client.pm line 193.
UPD:
Full stack-trace:
perl -MCarp::Always ./check_ac.pl
Not a HASH reference at /usr/local/share/perl/5.10.1/JSON/RPC/Client.pm line 193
JSON::RPC::ReturnObject::new('JSON::RPC::ReturnObject', 'HTTP::Response=HASH(0x9938d48)', 'JSON=SCALAR(0x96f1518)') called at /usr/local/share/perl/5.10.1/JSON/RPC/Client.pm line 118
JSON::RPC::Client::call('JSON::RPC::Client=HASH(0x944a818)', 'http://localhost:19000', 'HASH(0x96f1578)') called at ./check_ac.pl line 11
This error means that your JSON-RPC server is not actually one, inasmuch as it does not satisfy requirement 7.3. The error is triggered when JSON::RPC::Client assumes the document returned by the JSON-RPC service is well-formed (i.e., a JSON Object), and this assumptions turns out to have been in error. A bug report to the author of JSON::RPC::Client would be an appropriate way to request better error messaging.
I would attack this sort of problem by finding out what the server was returning that was causing JSON::RPC::Client to choke. Unfortunately, JRC fails to provide adequate hookpoints for finding this out, so you'll have to be a little bit tricky.
I don't like editing external libraries, so I recommend an extend-and-override approach to instrumenting traffic with the JSON-RPC server. Something like this (in check_ac.pl):
use Data::Dumper qw();
package JSON::RPC::InstrumentedClient;
use base 'JSON::RPC::Client';
# This would be better done with Module::Install, but I'm limiting dependencies today.
sub _get {
my ($self, #args) = #_;
return $self->_dump_response($self->SUPER::_get(#args));
}
sub _post {
my ($self, #args) = #_;
return $self->_dump_response($self->SUPER::_post(#args));
}
sub _dump_response {
my ($self, $response) = #_;
warn Data::Dumper::Dump([$response->decoded_content], [qw(content)]);
return $response;
}
package main;
my $client = JSON::RPC::InstrumentedClient->new();
my $url = 'http://localhost:19000';
... # rest of check_ac.pl
This wraps the calls to _get and _post that JSON::RPC::Client makes internally in such a way as to let you examine what the web server actually said in response to the request we made. The above code dumps the text content of the page; this might not be the right thing in your case and will blow up if an error is encountered. It's a debugging aid only, to help you figure out from the client code side what is wrong with the server.
That's enough caveats for now, I think. Good luck.
It seems to be a bug in method new of JSON::RPC::ReturnObject.
sub new {
my ($class, $obj, $json) = #_;
my $content = ( $json || JSON->new->utf8 )->decode( $obj->content );
#...
# line 193
$content->{error} ? $self->is_success(0) : $self->is_success(1);
#...
}
$content's value will be something returned from a JSON::decode() call. But looking at the documentation, it seems that JSON->decode() returns a scalar which could be a number, a string, an array reference, or a hash reference.
Unfortunately, JSON::RPC::ReturnObject->new() doesn't check what sort of thing JSON->decode() returned before trying to access it as a hashref. Given your error, I'm going to go ahead and assume what it got in your case was not one. :-)
I don't know if there's a way to force a fix from your code. I'd recommend contacting the author and letting him know about the issue, and/or filing a bug.