File Coverage

lib/XML/Compile/Transport/SOAPHTTP_MojoUA.pm
Criterion Covered Total %
statement 55 177 31.0
branch 0 58 0.0
condition 0 34 0.0
subroutine 18 26 69.2
pod 3 5 60.0
total 76 300 25.3


line stmt bran cond sub pod time code
1             # Copyrights 2016-2019 by [Mark Overmeer ].
2             # For other contributors see ChangeLog.
3             # See the manual pages for details on the licensing terms.
4             # Pod stripped from pm file by OODoc 2.02.
5             # This code is part of distribution XML-Compile-SOAP-Mojolicious.
6             # Meta-POD processed with # OODoc into POD and HTML manual-pages. See README.md
7             # Copyright Mark Overmeer. Licensed under the same terms as Perl itself.
8              
9             package XML::Compile::Transport::SOAPHTTP_MojoUA;
10 1     1   641521 use vars '$VERSION';
  1         3  
  1         53  
11             $VERSION = '0.04';
12              
13 1     1   6 use base 'XML::Compile::Transport';
  1         1  
  1         462  
14              
15 1     1   2424 use warnings;
  1         3  
  1         20  
16 1     1   4 use strict;
  1         2  
  1         17  
17              
18 1     1   3 use Log::Report 'xml-compile-soap-mojolicious';
  1         2  
  1         4  
19              
20 1     1   648 use XML::Compile::Transport::SOAPHTTP ();
  1         35286  
  1         30  
21              
22             BEGIN {
23             # code mixin from XML::Compile::Transport::SOAPHTTP
24 1     1   9 no strict 'refs';
  1         2  
  1         48  
25 1     1   3 foreach (qw/_prepare_xop_call _prepare_simple_call _prepare_for_no_answer/) {
26 3         5 *{ __PACKAGE__ . "::$_" } = \&{"XML::Compile::Transport::SOAPHTTP::$_"};
  3         34  
  3         8  
27             }
28             }
29              
30 1     1   5 use XML::Compile::SOAP::Util qw/SOAP11ENV SOAP11HTTP/;
  1         2  
  1         48  
31 1     1   5 use XML::Compile ();
  1         2  
  1         11  
32              
33 1     1   5 use Mojolicious (); # only for version info?!
  1         2  
  1         19  
34 1     1   6 use Mojo::UserAgent ();
  1         1  
  1         13  
35 1     1   5 use Mojo::IOLoop ();
  1         1  
  1         10  
36 1     1   4 use HTTP::Request ();
  1         1  
  1         11  
37 1     1   3 use HTTP::Response ();
  1         3  
  1         17  
38 1     1   14 use HTTP::Headers ();
  1         3  
  1         22  
39 1     1   5 use Scalar::Util qw(blessed reftype);
  1         1  
  1         1299  
40              
41             # (Microsofts HTTP Extension Framework)
42             my $http_ext_id = SOAP11ENV;
43              
44             __PACKAGE__->register(SOAP11HTTP);
45              
46              
47             sub init($) {
48 0     0 0   my ($self, $args) = @_;
49 0 0         if(my $cb = $self->{ua_start_cb} = delete $args->{ua_start_callback}) {
50 0 0         panic "callback not a code" if reftype $cb ne 'CODE';
51             }
52              
53 0           $self->SUPER::init($args);
54 0           $self;
55             }
56              
57             sub initWSDL11($) {
58 0     0 0   my ($class, $wsdl) = @_;
59 0           trace "initialize SOAPHTTP-MojoUA transporter for WSDL11";
60             }
61              
62             #-------------------------------------------
63              
64              
65 0     0 1   sub uaStartCallback { shift->{ua_start_cb} }
66              
67             #-------------------------------------------
68              
69              
70             sub compileClient(@) {
71 0     0 1   my ( $self, %args ) = @_;
72 0           my $call = $self->_prepare_call( \%args );
73 0   0       my $kind = $args{kind} || 'request-response';
74 0           my $parser = XML::LibXML->new;
75              
76             sub {
77 0     0     my ($xmlout, $trace, $mtom, $callback) = @_;
78 0           my $start = time;
79 0 0         my $textout = ref $xmlout ? $xmlout->toString : $xmlout;
80              
81             #warn $xmlout->toString(1); # show message sent
82              
83 0           my $stringify = time;
84 0           $trace->{transport_start} = $start;
85              
86             my $handler = sub {
87 0           my ($textin, $xops) = @_;
88 0           my $connected = time;
89              
90 0           my $xmlin;
91 0 0         if($textin) {
92 0           $xmlin = eval { $parser->parse_string($$textin) };
  0            
93 0 0         $trace->{error} = $@ if $@;
94             }
95              
96 0           my $answer;
97 0 0         if($kind eq 'one-way') {
    0          
98 0           my $response = $trace->{http_response};
99 0 0         my $code = defined $response ? $response->code : -1;
100 0 0 0       if($code == 202) { $answer = $xmlin || {} }
  0            
101 0           else { $trace->{error} = "call failed with code $code" }
102             }
103 0           elsif($xmlin) { $answer = $xmlin }
104 0   0       else { $trace->{error} ||= 'no xml as answer' }
105              
106 0           my $end = $trace->{transport_end} = time;
107              
108 0           $trace->{stringify_elapse} = $stringify - $start;
109 0           $trace->{connect_elapse} = $connected - $stringify;
110 0           $trace->{parse_elapse} = $end - $connected;
111 0           $trace->{transport_elapse} = $end - $start;
112              
113 0           return ( $answer, $trace, $xops );
114 0           };
115              
116 0           $call->( \$textout, $trace, $mtom, sub { $callback->( $handler->(@_) ) } );
  0            
117 0           };
118             } ## end sub compileClient(@)
119              
120              
121             sub _prepare_call($) {
122 0     0     my ($self, $args) = @_;
123 0   0       my $method = $args->{method} || 'POST';
124 0   0       my $soap = $args->{soap} || 'SOAP11';
125 0 0         my $version = ref $soap ? $soap->version : $soap;
126 0   0       my $mpost_id = $args->{mpost_id} || 42;
127 0           my $action = $args->{action};
128 0           my $mime = $args->{mime};
129 0   0       my $kind = $args->{kind} || 'request-response';
130 0   0       my $expect = $kind ne 'one-way' && $kind ne 'notification-operation';
131              
132 0           my $charset = $self->charset;
133              
134             # Prepare header
135 0   0       my $header = $args->{header} || HTTP::Headers->new;
136 0           $self->headerAddVersions($header);
137              
138 0           my $content_type;
139 0 0         if($version eq 'SOAP11') {
    0          
140 0 0 0       $mime ||= ref $soap ? $soap->mimeType : 'text/xml';
141 0           $content_type = qq{$mime; charset=$charset};
142             }
143             elsif($version eq 'SOAP12') {
144 0 0 0       $mime ||= ref $soap ? $soap->mimeType : 'application/soap+xml';
145 0 0         my $sa = defined $action ? qq{; action="$action"} : '';
146 0           $content_type = qq{$mime; charset=$charset$sa};
147 0           $header->header( Accept => $mime ); # not the HTML answer
148             }
149             else {
150 0           error __x"SOAP version {version} not implemented", version => $version;
151             }
152              
153 0 0         if($method eq 'POST') {
    0          
154 0 0         $header->header( SOAPAction => qq{"$action"} )
155             if defined $action;
156             }
157             elsif($method eq 'M-POST') {
158 0           $header->header( Man => qq{"$http_ext_id"; ns=$mpost_id} );
159 0 0         $header->header( "$mpost_id-SOAPAction", qq{"$action"} )
160             if $version eq 'SOAP11';
161             }
162             else {
163 0           error __x"SOAP method must be POST or M-POST, not {method}"
164             , method => $method;
165             }
166              
167             # Prepare request
168              
169             # Ideally, we should change server when one fails, and stick to that
170             # one as long as possible.
171 0           my $server = $self->address;
172 0           my $request = HTTP::Request->new(
173             $method => $server,
174             $header
175             );
176 0           $request->protocol('HTTP/1.1');
177              
178             # Create handler
179              
180             my ($create_message, $parse_message)
181 0 0         = exists $INC{'XML/Compile/XOP.pm'}
182             ? $self->_prepare_xop_call($content_type)
183             : $self->_prepare_simple_call($content_type);
184              
185 0 0         $parse_message = $self->_prepare_for_no_answer($parse_message)
186             unless $expect;
187              
188             # Needs to be outside the sub so it does not go out of scope
189             # when the sub is left (which would cause the termination of
190             # the request)
191 0           my $ua = Mojo::UserAgent->new;
192              
193 0 0         if(my $callback = $self->uaStartCallback) {
194 0           $ua->on(start => $callback);
195             }
196              
197             # async call
198             sub {
199 0     0     my ( $content, $trace, $mtom, $callback ) = @_;
200 0           $create_message->($request, $content, $mtom);
201              
202 0           $trace->{http_request} = $request;
203              
204             my $handler = sub {
205 0           my $tx = shift;
206              
207 0 0 0       unless(blessed $tx && $tx->isa('Mojo::Transaction::HTTP')) {
208 0           $trace->{error} = "Did not receive a transaction object";
209 0           return $callback->(undef, undef, $trace);
210             }
211              
212 0           my $res = $tx->res;
213 0           my $headers = $res->headers->to_hash(1);
214 0           foreach my $h ( keys %{$headers} ) {
  0            
215 0 0         if ( reftype $headers->{$h} eq 'ARRAY') {
216 0           $headers->{$h} = join ', ', @{$headers->{$h}};
  0            
217             }
218             }
219              
220 0           my $err = $res->error;
221             $headers->{'Client-Warning'} = 'Client side error: '.$err->{message}
222 0 0 0       if $err && !$err->{code};
223              
224 0           my $response = $trace->{http_response} = HTTP::Response->new(
225             $res->code, $res->message, [%$headers], $res->body
226             );
227              
228 0 0         if($response->header('Client-Warning')) {
229 0           $trace->{error} = $response->message;
230 0           return $callback->(undef, undef, $trace);
231             }
232              
233 0 0         if($response->is_error) {
234 0           $trace->{error} = $response->message;
235              
236             # still try to parse the response for Fault blocks
237             }
238              
239 0           my ($parsed, $mtom) = try { $parse_message->($response) };
  0            
240 0 0         if ($@) {
241 0           $trace->{error} = $@->wasFatal->message;
242 0           return $callback->(undef, undef, $trace);
243             }
244              
245 0           try { $callback->($parsed, $mtom, $trace) };
  0            
246 0           };
247              
248 0           my $tx = $ua->build_tx($request->method => $request->uri->as_string);
249              
250 0           foreach my $hdr_name ($request->headers->header_field_names) {
251 0 0         next if $hdr_name eq "Content-Length";
252              
253 0           foreach my $hdr ( $request->headers->header($hdr_name) ) {
254 0           $tx->req->headers->append( $hdr_name => $hdr );
255             }
256             }
257 0           $tx->req->body($request->content);
258              
259             $ua->start(
260             $tx => sub {
261 0           my ($ua, $tx) = @_;
262 0           $handler->($tx);
263             },
264 0           );
265 0           };
266             }
267              
268              
269              
270             sub headerAddVersions($) {
271 0     0 1   my ( $thing, $h ) = @_;
272 0           foreach my $pkg (
273             qw/XML::Compile
274             XML::Compile::Cache
275             XML::Compile::SOAP
276             XML::LibXML
277             Mojolicious/ )
278             {
279 1     1   6 no strict 'refs';
  1         2  
  1         129  
280 0   0       my $version = ${"${pkg}::VERSION"} || 'undef';
281 0           ( my $field = "X-$pkg-Version" ) =~ s/\:\:/-/g;
282 0           $h->header($field => $version);
283             }
284             }
285              
286             1;