File Coverage

blib/lib/SOAP/Transport/HTTP.pm
Criterion Covered Total %
statement 140 383 36.5
branch 34 190 17.8
condition 15 150 10.0
subroutine 23 55 41.8
pod n/a
total 212 778 27.2


line stmt bran cond sub pod time code
1             # ======================================================================
2             #
3             # Copyright (C) 2000-2004 Paul Kulchenko (paulclinger@yahoo.com)
4             # SOAP::Lite is free software; you can redistribute it
5             # and/or modify it under the same terms as Perl itself.
6             #
7             # ======================================================================
8              
9             package SOAP::Transport::HTTP;
10              
11 13     13   1289 use strict;
  13         29  
  13         851  
12              
13             our $VERSION = 1.11;
14              
15 13     13   76 use SOAP::Lite;
  13         23  
  13         111  
16 13     13   71 use SOAP::Packager;
  13         25  
  13         606  
17              
18             # ======================================================================
19              
20             package SOAP::Transport::HTTP::Client;
21              
22 13     13   72 use vars qw(@ISA $COMPRESS $USERAGENT_CLASS);
  13         22  
  13         1834  
23             $USERAGENT_CLASS = 'LWP::UserAgent';
24             @ISA = qw(SOAP::Client);
25              
26             $COMPRESS = 'deflate';
27              
28             my ( %redirect, %mpost, %nocompress );
29              
30             # hack for HTTP connection that returns Keep-Alive
31             # miscommunication (?) between LWP::Protocol and LWP::Protocol::http
32             # dies after timeout, but seems like we could make it work
33             my $_patched = 0;
34              
35             sub patch {
36 12 50   12   70 return if $_patched;
37 13     13   11271 BEGIN { local ($^W) = 0; }
38             {
39 12         33 local $^W = 0;
  12         73  
40             sub LWP::UserAgent::redirect_ok;
41 0     0   0 *LWP::UserAgent::redirect_ok = sub { 1 }
42 12         262 }
43             {
44              
45 12         38 package
46             LWP::Protocol;
47 12         49 local $^W = 0;
48 12         41 my $collect = \&collect; # store original
49             *collect = sub {
50 1 50 33 1   849009 if ( defined $_[2]->header('Connection')
51             && $_[2]->header('Connection') eq 'Keep-Alive' ) {
52 0         0 my $data = $_[3]->();
53             my $next =
54             $_[2]->header('Content-Length') &&
55             SOAP::Utils::bytelength($$data) ==
56             $_[2]->header('Content-Length')
57 0     0   0 ? sub { my $str = ''; \$str; }
  0         0  
58 0 0 0     0 : $_[3];
59 0         0 my $done = 0;
60             $_[3] = sub {
61 0 0   0   0 $done++ ? &$next : $data;
62 0         0 };
63             }
64 1         88 goto &$collect;
65 12         103 };
66             }
67 12         42 $_patched++;
68             }
69              
70 11     11   50 sub DESTROY { SOAP::Trace::objects('()') }
71              
72             sub http_request {
73 23     23   922 my $self = shift;
74 23 100       104 if (@_) { $self->{'_http_request'} = shift; return $self }
  12         72  
  12         39  
75 11         79 return $self->{'_http_request'};
76             }
77              
78             sub http_response {
79 165     165   921744 my $self = shift;
80 165 100       436 if (@_) { $self->{'_http_response'} = shift; return $self }
  11         83  
  11         34  
81 154         1029 return $self->{'_http_response'};
82             }
83              
84             sub new {
85 12     12   32 my $class = shift;
86              
87 12 50       52 return $class if ref $class; # skip if we're already object...
88              
89 12 50       44 if ( !grep { $_ eq $USERAGENT_CLASS } @ISA ) {
  12         128  
90 12         168 push @ISA, $USERAGENT_CLASS;
91             }
92              
93 12 50       1251 eval("require $USERAGENT_CLASS")
94             or die "Could not load UserAgent class $USERAGENT_CLASS: $@";
95              
96 12         599625 require HTTP::Request;
97 12         72 require HTTP::Headers;
98              
99 12 50       101 patch() if $SOAP::Constants::PATCH_HTTP_KEEPALIVE;
100              
101 12         26 my ( @params, @methods );
102 12         61 while (@_) {
103 12 50       386 $class->can( $_[0] )
104             ? push( @methods, shift() => shift )
105             : push( @params, shift );
106             }
107 12         135 my $self = $class->SUPER::new(@params);
108              
109 12 50       43195 die
110             "SOAP::Transport::HTTP::Client must inherit from LWP::UserAgent, or one of its subclasses"
111             if !$self->isa("LWP::UserAgent");
112              
113 12         233 $self->agent( join '/', 'SOAP::Lite', 'Perl',
114             $SOAP::Transport::HTTP::VERSION );
115 12         6750 $self->options( {} );
116              
117 12         114 $self->http_request( HTTP::Request->new() );
118              
119 12         73 while (@methods) {
120 12         60 my ( $method, $params ) = splice( @methods, 0, 2 );
121 12 50       103 $self->$method( ref $params eq 'ARRAY' ? @$params : $params );
122             }
123              
124 12         98 SOAP::Trace::objects('()');
125              
126 12         2470 return $self;
127             }
128              
129             sub send_receive {
130 11     11   106 my ( $self, %parameters ) = @_;
131 11         56 my ( $context, $envelope, $endpoint, $action, $encoding, $parts ) =
132             @parameters{qw(context envelope endpoint action encoding parts)};
133              
134 11   50     53 $encoding ||= 'UTF-8';
135              
136 11   33     123 $endpoint ||= $self->endpoint;
137              
138 11         21 my $method = 'POST';
139 11         31 $COMPRESS = 'gzip';
140              
141             $self->options->{is_compress} ||=
142             exists $self->options->{compress_threshold}
143 11   33     52 && eval { require Compress::Zlib };
      33        
144              
145             # Initialize the basic about the HTTP Request object
146 11         45 my $http_request = $self->http_request()->clone();
147              
148             # $self->http_request(HTTP::Request->new);
149 11         3120 $http_request->headers( HTTP::Headers->new );
150              
151             # TODO - add application/dime
152 11         341 $http_request->header(
153             Accept => ['text/xml', 'multipart/*', 'application/soap'] );
154 11         1081 $http_request->method($method);
155 11         136 $http_request->url($endpoint);
156              
157 13     13   205 no strict 'refs';
  13         28  
  13         15957  
158 11 50       122928 if ($parts) {
159 0         0 my $packager = $context->packager;
160 0         0 $envelope = $packager->package( $envelope, $context );
161 0         0 for my $hname ( keys %{$packager->headers_http} ) {
  0         0  
162 0         0 $http_request->headers->header(
163             $hname => $packager->headers_http->{$hname} );
164             }
165              
166             # TODO - DIME support
167             }
168              
169             COMPRESS: {
170 11   33     36 my $compressed =
  11         156  
171             !exists $nocompress{$endpoint}
172             && $self->options->{is_compress}
173             && ( $self->options->{compress_threshold} || 0 ) < length $envelope;
174              
175              
176 11         239 my $original_encoding = $http_request->content_encoding;
177              
178 11         857 while (1) {
179              
180             # check cache for redirect
181 11 50       59 $endpoint = $redirect{$endpoint} if exists $redirect{$endpoint};
182              
183             # check cache for M-POST
184 11 50       55 $method = 'M-POST' if exists $mpost{$endpoint};
185              
186             # what's this all about?
187             # unfortunately combination of LWP and Perl 5.6.1 and later has bug
188             # in sending multibyte characters. LWP uses length() to calculate
189             # content-length header and starting 5.6.1 length() calculates chars
190             # instead of bytes. 'use bytes' in THIS file doesn't work, because
191             # it's lexically scoped. Unfortunately, content-length we calculate
192             # here doesn't work either, because LWP overwrites it with
193             # content-length it calculates (which is wrong) AND uses length()
194             # during syswrite/sysread, so we are in a bad shape anyway.
195             #
196             # what to do? we calculate proper content-length (using
197             # bytelength() function from SOAP::Utils) and then drop utf8 mark
198             # from string (doing pack with 'C0A*' modifier) if length and
199             # bytelength are not the same
200 11         84 my $bytelength = SOAP::Utils::bytelength($envelope);
201 11 50       70 if ($] < 5.008) {
202 0         0 $envelope = pack( 'C0A*', $envelope );
203             }
204             else {
205 11         116 require Encode;
206 11         76 $envelope = Encode::encode($encoding, $envelope);
207             }
208             # if !$SOAP::Constants::DO_NOT_USE_LWP_LENGTH_HACK
209             # && length($envelope) != $bytelength;
210              
211             # compress after encoding
212             # doing it before breaks the compressed content (#74577)
213 11 50       2110 $envelope = Compress::Zlib::memGzip($envelope) if $compressed;
214              
215 11         91 $http_request->content($envelope);
216 11         316 $http_request->protocol('HTTP/1.1');
217              
218 11 50 33     209 $http_request->proxy_authorization_basic( $ENV{'HTTP_proxy_user'},
219             $ENV{'HTTP_proxy_pass'} )
220             if ( $ENV{'HTTP_proxy_user'} && $ENV{'HTTP_proxy_pass'} );
221              
222             # by Murray Nesbitt
223 11 50       57 if ( $method eq 'M-POST' ) {
224 0         0 my $prefix = sprintf '%04d', int( rand(1000) );
225 0         0 $http_request->header(
226             Man => qq!"$SOAP::Constants::NS_ENV"; ns=$prefix! );
227 0 0       0 $http_request->header( "$prefix-SOAPAction" => $action )
228             if defined $action;
229             }
230             else {
231 11 50       108 $http_request->header( SOAPAction => $action )
232             if defined $action;
233             }
234              
235             # $http_request->header(Expect => '100-Continue');
236              
237             # allow compress if present and let server know we could handle it
238 11 50       986 $http_request->header( 'Accept-Encoding' =>
239             [$SOAP::Transport::HTTP::Client::COMPRESS] )
240             if $self->options->{is_compress};
241              
242 11 50       51 $http_request->content_encoding(
243             $SOAP::Transport::HTTP::Client::COMPRESS)
244             if $compressed;
245              
246 11 50 0     137 if ( !$http_request->content_type ) {
    0          
247 11 100 66     557 $http_request->content_type(
248             join '; ',
249             $SOAP::Constants::DEFAULT_HTTP_CONTENT_TYPE,
250             !$SOAP::Constants::DO_NOT_USE_CHARSET && $encoding
251             ? 'charset=' . lc($encoding)
252             : () );
253             }
254             elsif ( !$SOAP::Constants::DO_NOT_USE_CHARSET && $encoding ) {
255 0         0 my $tmpType = $http_request->headers->header('Content-type');
256              
257             # $http_request->content_type($tmpType.'; charset=' . lc($encoding));
258 0         0 my $addition = '; charset=' . lc($encoding);
259 0 0       0 $http_request->content_type( $tmpType . $addition )
260             if ( $tmpType !~ /$addition/ );
261             }
262              
263 11         264 $http_request->content_length($bytelength);
264 11         626 SOAP::Trace::transport($http_request);
265 11         62 SOAP::Trace::debug( $http_request->as_string );
266              
267 11 50       77 $self->SUPER::env_proxy if $ENV{'HTTP_proxy'};
268              
269             # send and receive the stuff.
270             # TODO maybe eval this? what happens on connection close?
271 11         186 $self->http_response( $self->SUPER::request($http_request) );
272 11         197 SOAP::Trace::transport( $self->http_response );
273 11         39 SOAP::Trace::debug( $self->http_response->as_string );
274              
275             # 100 OK, continue to read?
276 11 50 33     60 if ( (
    50 33        
      33        
277             $self->http_response->code == 510
278             || $self->http_response->code == 501
279             )
280             && $method ne 'M-POST'
281             ) {
282 0         0 $mpost{$endpoint} = 1;
283             }
284             elsif ( $self->http_response->code == 415 && $compressed ) {
285              
286             # 415 Unsupported Media Type
287 0         0 $nocompress{$endpoint} = 1;
288 0         0 $envelope = Compress::Zlib::memGunzip($envelope);
289 0         0 $http_request->headers->remove_header('Content-Encoding');
290 0         0 redo COMPRESS; # try again without compression
291             }
292             else {
293 11         232 last;
294             }
295             }
296             }
297              
298 11 50 33     47 $redirect{$endpoint} = $self->http_response->request->url
299             if $self->http_response->previous
300             && $self->http_response->previous->is_redirect;
301              
302 11         159 $self->code( $self->http_response->code );
303 11         54 $self->message( $self->http_response->message );
304 11         50 $self->is_success( $self->http_response->is_success );
305 11         74 $self->status( $self->http_response->status_line );
306              
307             # Pull out any cookies from the response headers
308 11 50       61 $self->{'_cookie_jar'}->extract_cookies( $self->http_response )
309             if $self->{'_cookie_jar'};
310              
311 11 50 33     719 my $content =
    50 50        
312             ( $self->http_response->content_encoding || '' ) =~
313             /\b$SOAP::Transport::HTTP::Client::COMPRESS\b/o
314             && $self->options->{is_compress}
315             ? Compress::Zlib::memGunzip( $self->http_response->content )
316             : ( $self->http_response->content_encoding || '' ) =~ /\S/ ? die
317 0         0 "Can't understand returned Content-Encoding (@{[$self->http_response->content_encoding]})\n"
318             : $self->http_response->content;
319              
320 11 50       136 return $self->http_response->content_type =~ m!^multipart/!i
321             ? join( "\n", $self->http_response->headers_as_string, $content )
322             : $content;
323             }
324              
325             # ======================================================================
326              
327             package SOAP::Transport::HTTP::Server;
328              
329 13     13   98 use vars qw(@ISA $COMPRESS);
  13         32  
  13         954  
330             @ISA = qw(SOAP::Server);
331              
332 13     13   84 use URI;
  13         33  
  13         4309  
333              
334             $COMPRESS = 'deflate';
335              
336 0     0     sub DESTROY { SOAP::Trace::objects('()') }
337              
338             sub new {
339 0     0     require LWP::UserAgent;
340 0           my $self = shift;
341 0 0         return $self if ref $self; # we're already an object
342              
343 0           my $class = $self;
344 0           $self = $class->SUPER::new(@_);
345             $self->{'_on_action'} = sub {
346 0   0 0     ( my $action = shift || '' ) =~ s/^(\"?)(.*)\1$/$2/;
347 0 0 0       die
      0        
      0        
      0        
348 0           "SOAPAction shall match 'uri#method' if present (got '$action', expected '@{[join('#', @_)]}'\n"
349             if $action
350             && $action ne join( '#', @_ )
351             && $action ne join( '/', @_ )
352             && ( substr( $_[0], -1, 1 ) ne '/'
353             || $action ne join( '', @_ ) );
354 0           };
355 0           SOAP::Trace::objects('()');
356              
357 0           return $self;
358             }
359              
360             sub BEGIN {
361 13     13   93 no strict 'refs';
  13         37  
  13         1300  
362 13     13   47 for my $method (qw(request response)) {
363 26         87 my $field = '_' . $method;
364             *$method = sub {
365 0     0   0 my $self = shift->new;
366             @_
367 0 0       0 ? ( $self->{$field} = shift, return $self )
368             : return $self->{$field};
369 26         16775 };
370             }
371             }
372              
373             sub handle {
374 0     0     my $self = shift->new;
375              
376 0           SOAP::Trace::debug( $self->request->content );
377              
378 0 0         if ( $self->request->method eq 'POST' ) {
    0          
379 0   0       $self->action( $self->request->header('SOAPAction') || undef );
380             }
381             elsif ( $self->request->method eq 'M-POST' ) {
382 0 0         return $self->response(
383             HTTP::Response->new(
384             510, # NOT EXTENDED
385             "Expected Mandatory header with $SOAP::Constants::NS_ENV as unique URI"
386             ) )
387             if $self->request->header('Man') !~
388             /^"$SOAP::Constants::NS_ENV";\s*ns\s*=\s*(\d+)/;
389 0   0       $self->action( $self->request->header("$1-SOAPAction") || undef );
390             }
391             else {
392 0           return $self->response(
393             HTTP::Response->new(405) ) # METHOD NOT ALLOWED
394             }
395              
396 0   0       my $compressed =
397             ( $self->request->content_encoding || '' ) =~ /\b$COMPRESS\b/;
398             $self->options->{is_compress} ||=
399 0   0       $compressed && eval { require Compress::Zlib };
      0        
400              
401             # signal error if content-encoding is 'deflate', but we don't want it OR
402             # something else, so we don't understand it
403 0 0 0       return $self->response(
      0        
      0        
      0        
404             HTTP::Response->new(415) ) # UNSUPPORTED MEDIA TYPE
405             if $compressed && !$self->options->{is_compress}
406             || !$compressed
407             && ( $self->request->content_encoding || '' ) =~ /\S/;
408              
409 0   0       my $content_type = $self->request->content_type || '';
410              
411             # in some environments (PerlEx?) content_type could be empty, so allow it also
412             # anyway it'll blow up inside ::Server::handle if something wrong with message
413             # TBD: but what to do with MIME encoded messages in THOSE environments?
414 0 0 0       return $self->make_fault( $SOAP::Constants::FAULT_CLIENT,
      0        
      0        
      0        
      0        
415             "Content-Type must be 'text/xml,' 'multipart/*,' "
416             . "'application/soap+xml,' 'or 'application/dime' instead of '$content_type'"
417             )
418             if !$SOAP::Constants::DO_NOT_CHECK_CONTENT_TYPE
419             && $content_type
420             && $content_type ne 'application/soap+xml'
421             && $content_type ne 'text/xml'
422             && $content_type ne 'application/dime'
423             && $content_type !~ m!^multipart/!;
424              
425             # TODO - Handle the Expect: 100-Continue HTTP/1.1 Header
426 0 0 0       if ( defined( $self->request->header("Expect") )
427             && ( $self->request->header("Expect") eq "100-Continue" ) ) {
428              
429             }
430              
431             # TODO - this should query SOAP::Packager to see what types it supports,
432             # I don't like how this is hardcoded here.
433 0 0         my $content =
434             $compressed
435             ? Compress::Zlib::uncompress( $self->request->content )
436             : $self->request->content;
437              
438 0 0         my $response = $self->SUPER::handle(
    0          
439             $self->request->content_type =~ m!^multipart/!
440             ? join( "\n", $self->request->headers_as_string, $content )
441             : $content
442             ) or return;
443              
444 0           SOAP::Trace::debug($response);
445              
446 0           $self->make_response( $SOAP::Constants::HTTP_ON_SUCCESS_CODE, $response );
447             }
448              
449             sub make_fault {
450 0     0     my $self = shift;
451 0           $self->make_response(
452             $SOAP::Constants::HTTP_ON_FAULT_CODE => $self->SUPER::make_fault(@_)
453             );
454 0           return;
455             }
456              
457             sub make_response {
458 0     0     my ( $self, $code, $response ) = @_;
459              
460 0 0         my $encoding = $1
461             if $response =~ /^<\?xml(?: version="1.0"| encoding="([^\"]+)")+\?>/;
462              
463 0 0         $response =~ s!(\?>)!$1!
464             if $self->request->content_type eq 'multipart/form-data';
465              
466             $self->options->{is_compress} ||=
467             exists $self->options->{compress_threshold}
468 0   0       && eval { require Compress::Zlib };
      0        
469              
470 0   0       my $compressed = $self->options->{is_compress}
471             && grep( /\b($COMPRESS|\*)\b/,
472             $self->request->header('Accept-Encoding') )
473             && ( $self->options->{compress_threshold} || 0 ) <
474             SOAP::Utils::bytelength $response;
475              
476 0 0         $response = Compress::Zlib::compress($response) if $compressed;
477              
478             # this next line does not look like a good test to see if something is multipart
479             # perhaps a /content-type:.*multipart\//gi is a better regex?
480 0           my ($is_multipart) =
481             ( $response =~ /^content-type:.* boundary="([^\"]*)"/im );
482              
483             $self->response(
484             HTTP::Response->new(
485             $code => undef,
486             HTTP::Headers->new(
487             'SOAPServer' => $self->product_tokens,
488             $compressed ? ( 'Content-Encoding' => $COMPRESS ) : (),
489             'Content-Type' => join( '; ',
490             'text/xml',
491             !$SOAP::Constants::DO_NOT_USE_CHARSET
492             && $encoding ? 'charset=' . lc($encoding) : () ),
493             'Content-Length' => SOAP::Utils::bytelength $response
494             ),
495             ( $] > 5.007 )
496 0 0 0       ? do { require Encode; Encode::encode( $encoding, $response ) }
  0 0          
  0 0          
497             : $response,
498             ) );
499              
500 0 0         $self->response->headers->header( 'Content-Type' =>
501             'Multipart/Related; type="text/xml"; start=""; boundary="'
502             . $is_multipart
503             . '"' )
504             if $is_multipart;
505             }
506              
507             # ->VERSION leaks a scalar every call - no idea why.
508             sub product_tokens {
509 0     0     join '/', 'SOAP::Lite', 'Perl', $SOAP::Transport::HTTP::VERSION;
510             }
511              
512             # ======================================================================
513              
514             package SOAP::Transport::HTTP::CGI;
515              
516 13     13   101 use vars qw(@ISA);
  13         30  
  13         11824  
517             @ISA = qw(SOAP::Transport::HTTP::Server);
518              
519 0     0     sub DESTROY { SOAP::Trace::objects('()') }
520              
521             sub new {
522 0     0     my $self = shift;
523 0 0         return $self if ref $self;
524              
525 0   0       my $class = ref($self) || $self;
526 0           $self = $class->SUPER::new(@_);
527 0           SOAP::Trace::objects('()');
528              
529 0           return $self;
530             }
531              
532             sub make_response {
533 0     0     my $self = shift;
534 0           $self->SUPER::make_response(@_);
535             }
536              
537             sub handle {
538 0     0     my $self = shift->new;
539              
540 0   0       my $length = $ENV{'CONTENT_LENGTH'} || 0;
541              
542             # if the HTTP_TRANSFER_ENCODING env is defined, set $chunked if it's chunked*
543             # else to false
544 0   0       my $chunked = (defined $ENV{'HTTP_TRANSFER_ENCODING'}
545             && $ENV{'HTTP_TRANSFER_ENCODING'} =~ /^chunked.*$/) || 0;
546              
547              
548 0           my $content = q{};
549              
550 0 0         if ($chunked) {
551 0           my $buffer;
552 0           binmode(STDIN);
553 0           while ( read( STDIN, my $buffer, 1024 ) ) {
554 0           $content .= $buffer;
555             }
556 0           $length = length($content);
557             }
558              
559 0 0 0       if ( !$length ) {
    0          
560 0           $self->response( HTTP::Response->new(411) ) # LENGTH REQUIRED
561             }
562             elsif ( defined $SOAP::Constants::MAX_CONTENT_SIZE
563             && $length > $SOAP::Constants::MAX_CONTENT_SIZE ) {
564 0           $self->response( HTTP::Response->new(413) ) # REQUEST ENTITY TOO LARGE
565             }
566             else {
567 0 0 0       if ( exists $ENV{EXPECT} && $ENV{EXPECT} =~ /\b100-Continue\b/i ) {
568 0           print "HTTP/1.1 100 Continue\r\n\r\n";
569             }
570              
571             #my $content = q{};
572 0 0         if ( !$chunked ) {
573 0           my $buffer;
574 0           binmode(STDIN);
575 0 0         if ( defined $ENV{'MOD_PERL'} ) {
576 0           while ( read( STDIN, $buffer, $length ) ) {
577 0           $content .= $buffer;
578 0 0         last if ( length($content) >= $length );
579             }
580             } else {
581 0           while ( sysread( STDIN, $buffer, $length ) ) {
582 0           $content .= $buffer;
583 0 0         last if ( length($content) >= $length );
584             }
585             }
586             }
587              
588             $self->request(
589 0 0         HTTP::Request->new(
    0          
590             $ENV{'REQUEST_METHOD'} || '' => $ENV{'SCRIPT_NAME'},
591             HTTP::Headers->new(
592 0   0       map { (
593             /^HTTP_(.+)/i
594             ? ( $1 =~ m/SOAPACTION/ )
595             ? ('SOAPAction')
596             : ($1)
597             : $_
598             ) => $ENV{$_}
599             } keys %ENV
600             ),
601             $content,
602             ) );
603 0           $self->SUPER::handle;
604             }
605              
606             # imitate nph- cgi for IIS (pointed by Murray Nesbitt)
607 0 0 0       my $status =
      0        
608             defined( $ENV{'SERVER_SOFTWARE'} )
609             && $ENV{'SERVER_SOFTWARE'} =~ /IIS/
610             ? $ENV{SERVER_PROTOCOL} || 'HTTP/1.0'
611             : 'Status:';
612 0           my $code = $self->response->code;
613              
614 0           binmode(STDOUT);
615              
616 0           print STDOUT "$status $code ", HTTP::Status::status_message($code),
617             "\015\012", $self->response->headers_as_string("\015\012"), "\015\012",
618             $self->response->content;
619             }
620              
621             # ======================================================================
622              
623             package SOAP::Transport::HTTP::Daemon;
624              
625 13     13   98 use Carp ();
  13         44  
  13         366  
626 13     13   66 use vars qw($AUTOLOAD @ISA);
  13         25  
  13         6702  
627             @ISA = qw(SOAP::Transport::HTTP::Server);
628              
629 0     0     sub DESTROY { SOAP::Trace::objects('()') }
630              
631             #sub new { require HTTP::Daemon;
632             sub new {
633 0     0     my $self = shift;
634 0 0         return $self if ( ref $self );
635              
636 0           my $class = $self;
637              
638 0           my ( @params, @methods );
639 0           while (@_) {
640 0 0         $class->can( $_[0] )
641             ? push( @methods, shift() => shift )
642             : push( @params, shift );
643             }
644 0           $self = $class->SUPER::new;
645              
646             # Added in 0.65 - Thanks to Nils Sowen
647             # use SSL if there is any parameter with SSL_* in the name
648 0 0 0       $self->SSL(1) if !$self->SSL && grep /^SSL_/, @params;
649 0           my $http_daemon = $self->http_daemon_class;
650 0 0 0       eval "require $http_daemon"
651             or Carp::croak $@
652             unless $http_daemon->can('new');
653              
654 0 0         $self->{_daemon} = $http_daemon->new(@params)
655             or Carp::croak "Can't create daemon: $!";
656              
657             # End SSL patch
658              
659 0           $self->myuri( URI->new( $self->url )->canonical->as_string );
660              
661 0           while (@methods) {
662 0           my ( $method, $params ) = splice( @methods, 0, 2 );
663 0 0         $self->$method(
664             ref $params eq 'ARRAY'
665             ? @$params
666             : $params
667             );
668             }
669 0           SOAP::Trace::objects('()');
670              
671 0           return $self;
672             }
673              
674             sub SSL {
675 0     0     my $self = shift->new;
676 0 0         if (@_) {
677 0           $self->{_SSL} = shift;
678 0           return $self;
679             }
680 0           return $self->{_SSL};
681             }
682              
683 0 0   0     sub http_daemon_class { shift->SSL ? 'HTTP::Daemon::SSL' : 'HTTP::Daemon' }
684              
685             sub AUTOLOAD {
686 0     0     my $method = substr( $AUTOLOAD, rindex( $AUTOLOAD, '::' ) + 2 );
687 0 0         return if $method eq 'DESTROY';
688              
689 13     13   107 no strict 'refs';
  13         28  
  13         3916  
690 0     0     *$AUTOLOAD = sub { shift->{_daemon}->$method(@_) };
  0            
691 0           goto &$AUTOLOAD;
692             }
693              
694             sub handle {
695 0     0     my $self = shift->new;
696 0           while ( my $c = $self->accept ) {
697 0           while ( my $r = $c->get_request ) {
698 0           $self->request($r);
699 0           $self->SUPER::handle;
700 0           eval {
701 0     0     local $SIG{PIPE} = sub {die "SIGPIPE"};
  0            
702 0           $c->send_response( $self->response );
703             };
704 0 0 0       if ($@ && $@ !~ /^SIGPIPE/) {
705 0           die $@;
706             }
707             }
708              
709             # replaced ->close, thanks to Sean Meisner
710             # shutdown() doesn't work on AIX. close() is used in this case. Thanks to Jos Clijmans
711 0 0         $c->can('shutdown')
712             ? $c->shutdown(2)
713             : $c->close();
714 0           $c->close;
715             }
716             }
717              
718             # ======================================================================
719              
720             package SOAP::Transport::HTTP::Apache;
721              
722 13     13   82 use vars qw(@ISA);
  13         34  
  13         16397  
723             @ISA = qw(SOAP::Transport::HTTP::Server);
724              
725 0     0     sub DESTROY { SOAP::Trace::objects('()') }
726              
727             sub new {
728 0     0     my $self = shift;
729 0 0         unless ( ref $self ) {
730 0   0       my $class = ref($self) || $self;
731 0           $self = $class->SUPER::new(@_);
732 0           SOAP::Trace::objects('()');
733             }
734              
735             # Added this code thanks to JT Justman
736             # This code improves and provides more robust support for
737             # multiple versions of Apache and mod_perl
738              
739             # mod_perl 2.0
740 0 0 0       if ( defined $ENV{MOD_PERL_API_VERSION}
741             && $ENV{MOD_PERL_API_VERSION} >= 2 ) {
742 0           require Apache2::RequestRec;
743 0           require Apache2::RequestIO;
744 0           require Apache2::Const;
745 0           require Apache2::RequestUtil;
746 0           require APR::Table;
747 0           Apache2::Const->import( -compile => 'OK' );
748 0           Apache2::Const->import( -compile => 'HTTP_BAD_REQUEST' );
749 0           $self->{'MOD_PERL_VERSION'} = 2;
750 0           $self->{OK} = &Apache2::Const::OK;
751             }
752             else { # mod_perl 1.xx
753 0 0         die "Could not find or load mod_perl"
754             unless ( eval "require mod_perl" );
755 0 0         die "Could not detect your version of mod_perl"
756             if ( !defined($mod_perl::VERSION) );
757 0 0         if ( $mod_perl::VERSION < 1.99 ) {
758 0           require Apache;
759 0           require Apache::Constants;
760 0           Apache::Constants->import('OK');
761 0           Apache::Constants->import('HTTP_BAD_REQUEST');
762 0           $self->{'MOD_PERL_VERSION'} = 1;
763 0           $self->{OK} = &Apache::Constants::OK;
764             }
765             else {
766 0           require Apache::RequestRec;
767 0           require Apache::RequestIO;
768 0           require Apache::Const;
769 0           Apache::Const->import( -compile => 'OK' );
770 0           Apache::Const->import( -compile => 'HTTP_BAD_REQUEST' );
771 0           $self->{'MOD_PERL_VERSION'} = 1.99;
772 0           $self->{OK} = &Apache::OK;
773             }
774             }
775              
776 0           return $self;
777             }
778              
779             sub handler {
780 0     0     my $self = shift->new;
781 0           my $r = shift;
782              
783             # Begin patch from JT Justman
784 0 0         if ( !$r ) {
785 0 0         if ( $self->{'MOD_PERL_VERSION'} < 2 ) {
786 0           $r = Apache->request();
787             }
788             else {
789 0           $r = Apache2::RequestUtil->request();
790             }
791             }
792              
793 0           my $cont_len;
794 0 0         if ( $self->{'MOD_PERL_VERSION'} < 2 ) {
795 0           $cont_len = $r->header_in('Content-length');
796             }
797             else {
798 0           $cont_len = $r->headers_in->get('Content-length');
799             }
800              
801             # End patch from JT Justman
802              
803 0           my $content = "";
804 0 0         if ( $cont_len > 0 ) {
805 0           my $buf;
806              
807             # attempt to slurp in the content at once...
808 0           $content .= $buf while ( $r->read( $buf, $cont_len ) > 0 );
809             }
810             else {
811              
812             # throw appropriate error for mod_perl 2
813 0 0         return Apache2::Const::HTTP_BAD_REQUEST()
814             if ( $self->{'MOD_PERL_VERSION'} >= 2 );
815 0           return Apache::Constants::BAD_REQUEST();
816             }
817              
818 0           my %headers;
819 0 0         if ( $self->{'MOD_PERL_VERSION'} < 2 ) {
820 0           %headers = $r->headers_in; # Apache::Table structure
821             } else {
822 0           %headers = %{ $r->headers_in }; # Apache2::RequestRec structure
  0            
823             }
824            
825 0           $self->request(
826             HTTP::Request->new(
827             $r->method() => $r->uri,
828             HTTP::Headers->new( %headers ),
829             $content
830             ) );
831 0           $self->SUPER::handle;
832              
833             # we will specify status manually for Apache, because
834             # if we do it as it has to be done, returning SERVER_ERROR,
835             # Apache will modify our content_type to 'text/html; ....'
836             # which is not what we want.
837             # will emulate normal response, but with custom status code
838             # which could also be 500.
839 0 0         if ($self->{'MOD_PERL_VERSION'} < 2 ) {
840 0           $r->status( $self->response->code );
841             }
842             else {
843 0           $r->status_line($self->response->code);
844             }
845              
846             # Begin JT Justman patch
847 0 0         if ( $self->{'MOD_PERL_VERSION'} > 1 ) {
848 0     0     $self->response->headers->scan(sub { $r->headers_out->add(@_) });
  0            
849 0           $r->content_type( join '; ', $self->response->content_type );
850             }
851             else {
852 0     0     $self->response->headers->scan( sub { $r->header_out(@_) } );
  0            
853 0           $r->send_http_header( join '; ', $self->response->content_type );
854             }
855              
856 0           $r->print( $self->response->content );
857 0           return $self->{OK};
858              
859             # End JT Justman patch
860             }
861              
862             sub configure {
863 0     0     my $self = shift->new;
864 0           my $config = shift->dir_config;
865 0           for (%$config) {
866 0 0         $config->{$_} =~ /=>/
    0          
    0          
867             ? $self->$_( {split /\s*(?:=>|,)\s*/, $config->{$_}} )
868             : ref $self->$_() ? () # hm, nothing can be done here
869             : $self->$_( split /\s+|\s*,\s*/, $config->{$_} )
870             if $self->can($_);
871             }
872 0           return $self;
873             }
874              
875             {
876              
877             # just create alias
878             sub handle;
879             *handle = \&handler
880             }
881              
882             # ======================================================================
883             #
884             # Copyright (C) 2001 Single Source oy (marko.asplund@kronodoc.fi)
885             # a FastCGI transport class for SOAP::Lite.
886             # Updated formatting and removed dead code in new() in 2008
887             # by Martin Kutter
888             #
889             # ======================================================================
890              
891             package SOAP::Transport::HTTP::FCGI;
892              
893 13     13   91 use vars qw(@ISA);
  13         31  
  13         4065  
894             @ISA = qw(SOAP::Transport::HTTP::CGI);
895              
896 0     0     sub DESTROY { SOAP::Trace::objects('()') }
897              
898             sub new {
899              
900 0     0     require FCGI;
901 0           Exporter::require_version( 'FCGI' => 0.47 )
902             ; # requires thread-safe interface
903              
904 0           my $class = shift;
905 0 0         return $class if ref $class;
906              
907 0           my $self = $class->SUPER::new(@_);
908 0           $self->{_fcgirq} = FCGI::Request( \*STDIN, \*STDOUT, \*STDERR );
909 0           SOAP::Trace::objects('()');
910              
911 0           return $self;
912             }
913              
914             sub handle {
915 0     0     my $self = shift->new;
916              
917 0           my ( $r1, $r2 );
918 0           my $fcgirq = $self->{_fcgirq};
919              
920 0           while ( ( $r1 = $fcgirq->Accept() ) >= 0 ) {
921 0           $r2 = $self->SUPER::handle;
922             }
923              
924 0           return undef;
925             }
926              
927             # ======================================================================
928              
929             1;