demichi has asked for the wisdom of the Perl Monks concerning the following question:

Hi,

I am currently workin on some JSON data requests from a webserver and I have trouble with the error handling.

It works fine with the correct credentials in authorization_basic. Now I tested it with wrong credentials and

I do not undestand why the scripts dies with "Can't locate LWP/Authen/Session.pm".

Here my code:

my $par_user = "123"; my $par_pass = "topsecret"; LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST","REST + HTTP request start"); my $ua = LWP::UserAgent->new; $ua->agent("Mozilla/8.0"); # pretend we are very capable browser $ua->timeout(30); $ua->env_proxy; my $req = HTTP::Request->new; $req->header('content-type' => 'application/json'); $req->header('accept' => 'application/json'); $req->method($par_httpmethod); $req->authorization_basic($par_user,$par_pass); $req->uri($url_string); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST","Meth +od: $par_httpmethod"); LOG_MSG($par_log_fh,$par_vbse,"v",$par_lglvl,7,"RESTREQUEST","Auth +orisation: $par_user,$par_pass"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST","URL: + $url_string"); my $response = $ua->request($req); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST","ssss +"); if ($response->is_success) { $http_status_code = $response->code; $http_decoded = $response->decoded_content; LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST"," +HTTP request successful"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST"," +HTTP status code: $http_status_code"); LOG_MSG($par_log_fh,$par_vbse,"v",$par_lglvl,7,"RESTREQUEST"," +HTTP decoded: $http_decoded"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,5,"RESTREQUEST"," +REST HTTP request finished"); return ($http_status_code,$http_decoded); } else { $http_status_code = $response->code; $http_decoded = $response->decoded_content; LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,3,"RESTREQUEST"," +HTTP request NOT successful"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,3,"RESTREQUEST"," +HTTP status code: $http_status_code"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,3,"RESTREQUEST"," +HTTP decoded: $http_decoded"); LOG_MSG($par_log_fh,$par_vbse,"n",$par_lglvl,3,"RESTREQUEST"," +REST HTTP request finished"); return ($http_status_code,$http_decoded); }

Here the output (don't worry for my died filehandle on log module)

2016.11.15,14:23:36,NOTICE,RESTGETPORTS,REST get ports start 2016.11.15,14:23:36,NOTICE,RESTREQUEST,REST HTTP request start 2016.11.15,14:23:36,NOTICE,RESTREQUEST,Method: GET 2016.11.15,14:23:36,NOTICE,RESTREQUEST,URL: http://10.3.3.3/Configurat +ionManager/v1/objects/storages/800000058068/ports
2016.11.15,14:23:36,ERROR,GENERAL,Script died: Can't locate LWP/Authen +/Session.pm in @INC (@INC contains: G:/development/HiCHperfpkg/bin\.. +\lib\modules G:/development/HiCHperfpkg/bin C:/Perl64/site/li b C:/Perl64/lib .) at (eval 72) line 2.
print() on closed filehandle $fh at G:/development/HiCHperfpkg/bin\..\ +lib\modules/xxx_LOG.pm line 92. 2016.11.15,14:23:36,WARNING,GENERAL,Script warning: print() on closed +filehandle $fh at G:/development/HiCHperfpkg/bin\..\lib\modules/xxx_L +OG.pm line 92. 2016.11.15,14:23:36,NOTICE,RESTREQUEST,ssss print() on closed filehandle $fh at G:/development/HiCHperfpkg/bin\..\ +lib\modules/xxx_LOG.pm line 92. 2016.11.15,14:23:36,WARNING,GENERAL,Script warning: print() on closed +filehandle $fh at G:/development/HiCHperfpkg/bin\..\lib\modules/xxx_L +OG.pm line 92. 2016.11.15,14:23:36,ERROR,RESTREQUEST,HTTP request NOT successful print() on closed filehandle $fh at G:/development/HiCHperfpkg/bin\..\ +lib\modules/xxx_LOG.pm line 92. 2016.11.15,14:23:36,WARNING,GENERAL,Script warning: print() on closed +filehandle $fh at G:/development/HiCHperfpkg/bin\..\lib\modules/xxx_L +OG.pm line 92. 2016.11.15,14:23:36,ERROR,RESTREQUEST,HTTP status code: 401 print() on closed filehandle $fh at G:/development/HiCHperfpkg/bin\..\ +lib\modules/xxx_LOG.pm line 92. 2016.11.15,14:23:36,WARNING,GENERAL,Script warning: print() on closed +filehandle $fh at G:/development/HiCHperfpkg/bin\..\lib\modules/xxx_L +OG.pm line 92. 2016.11.15,14:23:36,ERROR,RESTREQUEST,HTTP decoded: { "errorSource" : "/ConfigurationManager/v1/objects/storages/800000058 +068/ports", "message" : "The Authorization HTTP header, which is required for au +thentication, is not specified.", "solution" : "Specify the necessary value for the Authorization head +er.", "messageId" : "KART30010-E"

For example if I test it with wrong URL I get an expected error message.

2016.11.15,14:38:28,NOTICE,RESTGETSTORAGE,REST get storage start 2016.11.15,14:38:28,NOTICE,RESTREQUEST,REST HTTP request start 2016.11.15,14:38:28,NOTICE,RESTREQUEST,Method: GET 2016.11.15,14:38:28,NOTICE,RESTREQUEST,URL: http://10.0.1.11/Configura +tionManager/v1/objects/storages/ 2016.11.15,14:38:49,NOTICE,RESTREQUEST,ssss 2016.11.15,14:38:49,ERROR,RESTREQUEST,HTTP request NOT successful 2016.11.15,14:38:49,ERROR,RESTREQUEST,HTTP status code: 500 2016.11.15,14:38:49,ERROR,RESTREQUEST,HTTP decoded: Can't connect to 1 +0.70.4.14:80 (10060) LWP::Protocol::http::Socket: connect: 10060 at C:/Perl64/lib/LWP/Proto +col/http.pm line 51. 2016.11.15,14:38:49,ERROR,RESTREQUEST,REST HTTP request finished

Thanks for any hints how I can do a proper error handling for wrong user authentication.

regards deMichi

Replies are listed 'Best First'.
Re: HTTP::Request - authorization_basic
by davido (Cardinal) on Nov 15, 2016 at 16:14 UTC

    I think it's going to be hard to answer this question definitively the way it is written.

    The filehandle problem is hard to ignore since its warnings-spew occupies such a large portion of your post. But the most serious problem is this one:

    2016.11.15,14:23:36,ERROR,GENERAL,Script died: Can't locate LWP/Authen +/Session.pm in @INC (@INC contains: G:/development/HiCHperfpkg/bin\.. +\lib\modules G:/development/HiCHperfpkg/bin C:/Perl64/site/lib C:/Per +l64/lib .) at (eval 72) line 2.

    Your problem description states that you do not get this message when you test with a wrong URL, but you do get it if you test with the real target URL. It seems likely that the issue manifests itself with those criteria because the LWP::Authen::Session module is being dynamically loaded, on demand, when authentication is needed. If you hit the wrong URL, no authentication is needed, so the module never gets loaded. As soon as you point to the correct URL, authentication is needed, and you get your error message.

    So it seems you do not have LWP::Authen::Session installed on your system, or at least not in a place where Perl can find it. The line of code where that module is loaded was not included in your sample code, either. We cannot really look to see the precise problem because the code that is causing the problem happens in a place outside of the code you showed us.

    I went to http://search.cpan.org and the interwebs themselves looking for mention of LWP::Authen::Session and came up empty handed. I even grepped through the code of other LWP::* modules that seemed like they might be relevant to find mention of LWP::Authen::Session, and didn't find anything. I'm reluctant to mention this, because as soon as I do (or within a few minutes) someone will find it and expose the inadequacies in my searching. But I'm going with the assumption that LWP::Authen::Session must be an in-house module, not available on CPAN. If that's the case, you'll have to talk to someone at your shop and find out how to get it installed.


    Dave

      Hi Dave,

      it works without problem when the credentials are correct. So there must be some authentication working.

      When I comment out the line ..

      my $req = HTTP::Request->new; $req->header('content-type' => 'application/json'); $req->header('accept' => 'application/json'); $req->method($par_httpmethod); #$req->authorization_basic($par_user,$par_pass); $req->uri($url_string); my $response = $ua->request($req);

      .. I get the same error message.

      regards
      de Michi
Re: HTTP::Request - authorization_basic
by hippo (Archbishop) on Nov 16, 2016 at 11:19 UTC

    I agree with davido - there is no evidence which I can find of any LWP::Authen::Session module publicly available anywhere. You (or your programmers) have likely written this module and plugged it into the existing HTTP client framework. This is therefore a problem for you and your organisation to solve internally.

    Here is an SSCCE which shows how HTTP Basic Auth failures are properly handled in the modules available on CPAN:

    #!/usr/bin/env perl use strict; use warnings; use LWP::UserAgent; use Test::More tests => 1; my $ua = LWP::UserAgent->new (); my $res = $ua->get ('https://pause.perl.org/pause/authenquery'); is ($res->status_line, '401 Unauthorized', 'Request returns a valid 40 +1');
      All,

      I found the code example for authorization_basic somewhere on perlmonks ...

      http://www.perlmonks.org/?node_id=148451

      or

      http://www.perlmonks.org/bare/?node_id=313478

      and also on stackoverflow.

      http://stackoverflow.com/questions/8203964/lwpuseragent-http-basic-authentication

      or here https://perlmaven.com/basic-authentication-with-lwp-useragent-and-http-request-common

      So this is nothing internally.

      regards, de michi

        There is a lot of misinformation going on in this thread. Looking at the source of LWP::UserAgent, I see the following code:

        sub request { ... { my $proxy = ($code == &HTTP::Status::RC_PROXY_AUTHENTICATION_REQUI +RED); my $ch_header = $proxy || $request->method eq 'CONNECT' ? "Proxy-Authenticate" : "WWW-Authenticate"; my @challenge = $response->header($ch_header); unless (@challenge) { $response->header("Client-Warning" => "Missing Authenticate header"); return $response; } require HTTP::Headers::Util; CHALLENGE: for my $challenge (@challenge) { $challenge =~ tr/,/;/; # "," is used to separate auth-params! +! ($challenge) = HTTP::Headers::Util::split_header_words($challe +nge); my $scheme = shift(@$challenge); shift(@$challenge); # no value $challenge = { @$challenge }; # make rest into a hash unless ($scheme =~ /^([a-z]+(?:-[a-z]+)*)$/) { $response->header("Client-Warning" => "Bad authentication scheme '$scheme'"); return $response; } $scheme = $1; # untainted now my $class = "LWP::Authen::\u$scheme"; $class =~ s/-/_/g; no strict 'refs'; unless (%{"$class\::"}) { # try to load it eval "require $class"; if ($@) { if ($@ =~ /^Can\'t locate/) { $response->header("Client-Warning" => "Unsupported authentication scheme '$scheme'"); } else { $response->header("Client-Warning" => $@); } next CHALLENGE; } } ...

        So, HTTP::Request will attempt to load a module LWP::Authen::$scheme when the server responds with a WWW-Authenticate header, which it does if your credentials are wrong.

        Contrary to your expectation, it doesn't seem to respond with WWW-Authenticate: Basic but with WWW-Authenticate: Session, which makes LWP::UserAgent fail with the above error message. Maybe consider looking at what actually goes over the wire, what the actual response of the server is in your case, and maybe then decide to either fake LWP::Authen::Session as identical to LWP::Authen::Basic or to implement it yourself or to raise a bug against LWP::UserAgent.