File Coverage

perllib/Arch/LiteWeb.pm
Criterion Covered Total %
statement 15 130 11.5
branch 0 56 0.0
condition 0 31 0.0
subroutine 5 12 41.6
pod 4 5 80.0
total 24 234 10.2


line stmt bran cond sub pod time code
1             # Arch Perl library, Copyright (C) 2004 Mikhael Goikhman
2             #
3             # This program is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by
5             # the Free Software Foundation; either version 2 of the License, or
6             # (at your option) any later version.
7             #
8             # This program is distributed in the hope that it will be useful,
9             # but WITHOUT ANY WARRANTY; without even the implied warranty of
10             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11             # GNU General Public License for more details.
12             #
13             # You should have received a copy of the GNU General Public License
14             # along with this program; if not, write to the Free Software
15             # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16              
17 3     3   130 use 5.005;
  3         13  
  3         131  
18 3     3   27 use strict;
  3         5  
  3         346  
19              
20             package Arch::LiteWeb;
21              
22 3     3   3290 use Socket;
  3         14237  
  3         13330  
23              
24             sub new ($) {
25 1     1 0 3 my $class = shift;
26 1         7 my $self = {
27             request_url => undef,
28             network_error => undef,
29             response_code => undef,
30             response_codestr => undef,
31             response_error => undef,
32             response_headers => undef,
33             response_content => undef,
34             };
35 1         5 return bless $self, $class;
36             }
37              
38             sub _parse_url ($) {
39 0     0     my $url = shift;
40 0 0         $url =~ m!^http://([\w\.]+)(?::(\d+))?(?:(/.*))?$! or return;
41 0           my $host = $1;
42 0   0       my $port = $2 || 80;
43 0   0       my $path = $3 || "/";
44 0           return ($host, $port, $path);
45             }
46              
47             sub get ($$%) {
48 0     0 1   my $self = shift;
49 0           my $url = shift;
50 0           my %args = @_;
51 0           $self->{request_url} = undef;
52 0           $self->{network_error} = undef;
53 0           $self->{response_code} = undef;
54 0           $self->{response_codestr} = undef;
55 0           $self->{response_error} = undef;
56 0           $self->{response_headers} = undef;
57 0           $self->{response_content} = undef;
58              
59 0           my $url_host = $args{url_host};
60 0           my $url_port = $args{url_port};
61 0           my $url_path = $args{url_path};
62 0 0         if ($url) {
63 0 0         ($url_host, $url_port, $url_path) = _parse_url($url)
64             or die "Unsupported url ($url), sorry\n";
65             }
66              
67 0           my $use_proxy = $args{use_proxy};
68 0   0       my $proxy_host = $args{proxy_host} || "";
69 0   0       my $proxy_port = $args{proxy_port} || 80;
70 0 0 0       if ($use_proxy && !$proxy_host && defined $ENV{http_proxy}) {
      0        
71 0 0         ($proxy_host, $proxy_port) = _parse_url($ENV{http_proxy})
72             or die "Unsupported http_proxy url ($ENV{http_proxy}), sorry";
73             }
74 0   0       my $endl = $args{endl} || "\015\012";
75 0   0       my $timeout = $args{timeout} || 20;
76 0   0       my $user_agent = $args{user_agent} || "Arch::LiteWeb/0.1";
77 0   0       my $max_redirect_depth = $args{max_redirect_depth} || 5;
78 0           my $redirect_depth = 0;
79              
80 0           my $more_headers = "";
81 0 0         $more_headers .= "Pragma: no-cache$endl" if $args{nocache};
82              
83 0 0         HTTP_CONNECTION:
84             my $url_port_str = $url_port? ":$url_port": "";
85 0           $url = $self->{request_url} = "http://$url_host$url_port_str$url_path";
86 0 0 0       print STDERR "getting: $url\n"
87             if $ENV{DEBUG} && ("$ENV{DEBUG}" & "\2") ne "\0";
88              
89 0 0         my $host = $use_proxy? $proxy_host: $url_host;
90 0 0         my $port = $use_proxy? $proxy_port: $url_port;
91 0 0         my $iaddr = inet_aton($host) or do {
92 0           $self->{network_error} = "Can't resolve host $host";
93 0           return undef;
94             };
95 0           my $paddr = sockaddr_in($port, $iaddr);
96 0           my $proto = getprotobyname('tcp');
97              
98             # should use POSIX instead or PERL_SIGNALS=unsafe to work in 5.8.*
99 0     0     local $SIG{ALRM} = sub { die "timeout\n"; };
  0            
100 0           alarm($timeout);
101             eval {
102 0 0         socket(SOCK, PF_INET, SOCK_STREAM, $proto) &&
103             connect(SOCK, $paddr)
104 0 0         } || do {
105 0           $self->{network_error} = "Can't connect host $host";
106 0           return undef;
107             };
108 0           alarm(0);
109 0           select(SOCK); $| = 1; select(STDOUT);
  0            
  0            
110              
111             # send http request
112 0           my $http_headers = "$endl" .
113             "Host: $host$endl" .
114             "Connection: close$endl" .
115             "User-Agent: $user_agent$endl" .
116             "$more_headers$endl";
117 0 0         my $uri = $use_proxy? $self->{request_url}: $url_path;
118 0           my $request = "GET $uri HTTP/1.1$http_headers";
119 0 0         print STDERR "$request" if $ENV{DEBUG_MESSAGES};
120 0           print SOCK $request;
121              
122 0           my $endl2 = "\015?\012";
123              
124             # read http response
125 0           my $line = ;
126 0 0         unless ($line =~ m!^HTTP/1\.\d (\d+) (\w.*?)$endl2$!) {
127 0           $line =~ s/$endl2$//;
128 0           $self->{network_error} = "Invalid/unsupported HTTP response ($line)";
129 0           return undef;
130             }
131 0           my $rc = $self->{response_code} = $1;
132 0           $self->{response_codestr} = $2;
133              
134 0           my $text = join('', );
135 0 0         print STDERR "$line$text" if $ENV{DEBUG_MESSAGES};
136              
137 0           my ($headers, $content) = split(/(?<=\012)$endl2/, $text, 2);
138 0           my $unparsed;
139             $headers = { map {
140 0           /^([\w-]+):\s*(.*)$/?
141 0           do { my ($k, $v) = (lc($1), $2); $k =~ s/-/_/g; ($k, $v) }:
  0            
  0            
142 0 0         do { $unparsed .= "$_\n"; () };
  0            
  0            
143             } split(/$endl2/, $headers) };
144 0 0         $headers->{x_unparsed} = $unparsed if $unparsed;
145 0           $self->{response_headers} = $headers;
146 0           $self->{response_content} = $content;
147              
148 0 0 0       if ($rc == 301 || $rc == 302) {
149 0 0         goto RETURN if $args{noredirect};
150              
151             # redirection
152 0 0         ++$redirect_depth < $max_redirect_depth or do {
153 0           $self->{response_error} = "Too deep redirection, max depth is $max_redirect_depth";
154 0           return undef;
155             };
156 0           my $new_url = $headers->{location};
157 0 0         unless ($new_url) {
158 0           $self->{response_error} = "Response code $rc with missing Location header";
159 0           return undef;
160             }
161 0 0         ($url_host, $url_port, $url_path) = _parse_url($new_url) or do {
162 0           $self->{response_error} = "Response code $rc with unsupported Location value ($new_url)";
163 0           return undef;
164             };
165 0           goto HTTP_CONNECTION;
166             }
167 0 0         unless ($rc == 200) {
168 0           $self->{response_error} = "Non-success HTTP response code $rc";
169 0           return undef;
170             }
171             RETURN:
172 0           return $content;
173             }
174              
175             sub post ($$$%) {
176 0     0 1   my $self = shift;
177 0           my $url = shift;
178 0           my $input = shift;
179 0           die "Not implemented yet\n";
180             }
181              
182             sub error ($) {
183 0     0 1   my $self = shift;
184 0   0       return $self->{network_error} || $self->{response_error};
185             }
186              
187             sub error_with_url ($) {
188 0     0 1   my $self = shift;
189 0           my $error = $self->error;
190 0 0         return undef unless $error;
191 0           return "$error\nwhile fetching $self->{request_url}\n";
192             }
193              
194 3     3   46 use vars '$AUTOLOAD';
  3         6  
  3         618  
195              
196             sub AUTOLOAD ($@) {
197 0     0     my $self = shift;
198 0           my @params = @_;
199              
200 0           my $method = $AUTOLOAD;
201              
202             # remove the package name
203 0           $method =~ s/.*://;
204             # DESTROY messages should never be propagated
205 0 0         return if $method eq 'DESTROY';
206              
207 0 0         die "No such method $AUTOLOAD\n" unless exists $self->{$method};
208 0           return $self->{$method};
209             }
210              
211             1;
212              
213             __END__