File Coverage

blib/lib/POE/Component/Client/HTTP/Request.pm
Criterion Covered Total %
statement 222 234 94.8
branch 63 78 80.7
condition 17 21 80.9
subroutine 34 35 97.1
pod 19 19 100.0
total 355 387 91.7


line stmt bran cond sub pod time code
1             package POE::Component::Client::HTTP::Request;
2             # vim: ts=2 sw=2 expandtab
3             $POE::Component::Client::HTTP::Request::VERSION = '0.949';
4 22     22   24547 use strict;
  22         40  
  22         874  
5 22     22   114 use warnings;
  22         41  
  22         519  
6              
7 22     22   1751 use POE;
  22         105069  
  22         227  
8              
9 22     22   222376 use Carp;
  22         45  
  22         1614  
10 22     22   1759 use HTTP::Status;
  22         11364  
  22         9426  
11 22     22   136 use Errno qw(ETIMEDOUT);
  22         43  
  22         2521  
12              
13             BEGIN {
14 22     22   129 local $SIG{'__DIE__'} = 'DEFAULT';
15             # Allow more finely grained timeouts if Time::HiRes is available.
16             # This code is also in POE::Component::Client::HTTP
17 22         44 eval {
18 22         122 require Time::HiRes;
19 22         191 Time::HiRes->import("time");
20             };
21             }
22              
23             # Unique request ID, independent of wheel and timer IDs.
24             my $request_seq = 0;
25              
26 22     22   5546 use constant DEBUG => 0;
  22         44  
  22         2321  
27              
28             # TODO CONNECT - Add a flag to indicate whether to generate an HTTP
29             # CONNECT request for proxying, or to return REQ_HTTP_REQUEST. Add a
30             # method to update that flag.
31              
32             use constant {
33 22         5517 REQ_ID => 0,
34             REQ_POSTBACK => 1,
35             REQ_CONNECTION => 2,
36             REQ_HTTP_REQUEST => 3,
37             REQ_STATE => 4,
38             REQ_RESPONSE => 5,
39             REQ_BUFFER => 6,
40             REQ_OCTETS_GOT => 8,
41             REQ_TIMER => 9,
42             REQ_PROG_POSTBACK => 10,
43             REQ_USING_PROXY => 11,
44             REQ_HOST => 12,
45             REQ_PORT => 13,
46             REQ_HISTORY => 14,
47             REQ_START_TIME => 15,
48             REQ_FACTORY => 16,
49             REQ_CONN_ID => 17,
50             REQ_PEERNAME => 18,
51 22     22   136 };
  22         53  
52              
53             use constant {
54 22         3897 RS_CONNECT => 0x01, # establishing a connection
55             RS_SENDING => 0x02, # sending request to server
56             RS_IN_HEAD => 0x04, # waiting for or receiving headers
57             RS_REDIRECTED => 0x08, # request has been redirected
58             RS_IN_CONTENT => 0x20, # waiting for or receiving content
59             RS_DONE => 0x40, # received full content
60             RS_POSTED => 0x80, # we have posted back a response
61 22     22   1012 };
  22         49  
62              
63              
64             sub import {
65 43     43   119 my ($class) = shift;
66              
67 43         115 my $package = caller();
68              
69 43         693 foreach my $tag (@_) {
70 42 100       163 if ($tag eq ':fields') {
71 21         66 foreach my $sub (
72             qw(
73             REQ_ID REQ_POSTBACK REQ_CONNECTION REQ_HTTP_REQUEST REQ_STATE
74             REQ_RESPONSE REQ_BUFFER REQ_OCTETS_GOT REQ_TIMER
75             REQ_PROG_POSTBACK REQ_USING_PROXY REQ_HOST REQ_PORT
76             REQ_HISTORY REQ_START_TIME REQ_CONN_ID REQ_PEERNAME
77             )
78             ) {
79 22     22   121 no strict 'refs';
  22         44  
  22         1872  
80 357         554 *{$package . "::$sub"} = \&$sub;
  357         1117  
81             }
82             }
83              
84 42 100       1284 if ($tag eq ':states') {
85 21         62 foreach my $sub (
86             qw(
87             RS_CONNECT RS_SENDING RS_IN_HEAD RS_REDIRECTED
88             RS_IN_CONTENT RS_DONE RS_POSTED
89             )
90             ) {
91 22     22   119 no strict 'refs';
  22         38  
  22         90332  
92 147         371 *{$package . "::$sub"} = \&$sub;
  147         618  
93             }
94             }
95             }
96             }
97              
98              
99 277     277 1 1644 sub ID { return $_[0][REQ_ID] }
100              
101              
102             sub new {
103 65     65 1 15714 my $class = shift;
104              
105 65 100       430 croak __PACKAGE__ . "expects its arguments to be key/value pairs" if @_ & 1;
106 64         464 my %params = @_;
107              
108 64 100       395 croak "need a Request parameter" unless (defined $params{'Request'});
109 63 100       509 croak "Request must be a HTTP::Request object"
110             unless (UNIVERSAL::isa ($params{'Request'}, "HTTP::Request"));
111              
112 62 100       508 croak "need a Factory parameter" unless (defined $params{'Factory'});
113              
114 61         198 my ($http_request, $postback, $progress, $factory) =
115             @params{qw(Request Postback Progress Factory)};
116              
117 61         148 my $request_id = ++$request_seq;
118 61         72 DEBUG and warn "REQ: creating a request ($request_id)";
119              
120             # Get the host and port from the request object.
121 61         100 my ($host, $port, $scheme, $using_proxy);
122              
123 61         112 eval {
124 61         231 $host = $http_request->uri()->host();
125 61         2196 $port = $http_request->uri()->port();
126 60         2045 $scheme = $http_request->uri()->scheme();
127             };
128 61 100       1231 croak "Not a usable Request: $@" if ($@);
129              
130             # Add a host header if one isn't included. Must do this before
131             # we reset the $host for the proxy!
132 60 100 66     444 unless (
133             defined $http_request->header('Host') and
134             length $http_request->header('Host')
135             ) {
136 56         2885 my $error = _set_host_header($http_request);
137 56 50       196 croak "Can't set Host header: $error" if $error;
138             }
139              
140 60 100       491 if (defined $params{Proxy}) {
141             # This request qualifies for proxying. Replace the host and port
142             # with the proxy's host and port. This comes after the Host:
143             # header is set, so it doesn't break the request object.
144 8         11 ($host, $port) = @{$params{Proxy}->[rand @{$params{Proxy}}]};
  8         15  
  8         40  
145              
146 8         16 $using_proxy = 1;
147             }
148             else {
149 52         199 $using_proxy = 0;
150             }
151              
152             # Build the request.
153 60         448 my $self = [
154             $request_id, # REQ_ID
155             $postback, # REQ_POSTBACK
156             undef, # REQ_CONNECTION
157             $http_request, # REQ_HTTP_REQUEST
158             RS_CONNECT, # REQ_STATE
159             undef, # REQ_RESPONSE
160             '', # REQ_BUFFER
161             undef, # unused
162             0, # REQ_OCTETS_GOT
163             undef, # REQ_TIMER
164             $progress, # REQ_PROG_POSTBACK
165             $using_proxy, # REQ_USING_PROXY
166             $host, # REQ_HOST
167             $port, # REQ_PORT
168             undef, # REQ_HISTORY
169             time(), # REQ_START_TIME
170             $factory, # REQ_FACTORY
171             undef, # REQ_CONN_ID
172             undef, # REQ_PEERNAME
173             ];
174 60         514 return bless $self, $class;
175             }
176              
177              
178             sub return_response {
179 106     106 1 151 my ($self) = @_;
180              
181 106         316 DEBUG and warn "in return_response ", sprintf ("0x%02X", $self->[REQ_STATE]);
182 106 50       291 return if ($self->[REQ_STATE] & RS_POSTED);
183 106         524 my $response = $self->[REQ_RESPONSE];
184              
185             # If we have a cookie jar, have it frob our headers. LWP rocks!
186 106         414 $self->[REQ_FACTORY]->frob_cookies ($response);
187              
188             # If we're done, send back the HTTP::Response object, which
189             # is filled with content if we aren't streaming, or empty
190             # if we are. that there's no ARG1 lets the client know we're done
191             # with the content in the latter case
192 106 100       370 if ($self->[REQ_STATE] & RS_DONE) {
    50          
193 41         55 DEBUG and warn "done; returning $response for ", $self->[REQ_ID];
194 41         251 $self->[REQ_POSTBACK]->($self->[REQ_RESPONSE]);
195 41         4471 $self->[REQ_STATE] |= RS_POSTED;
196             #warn "state is now ", $self->[REQ_STATE];
197             }
198             elsif ($self->[REQ_STATE] & RS_IN_CONTENT) {
199             # If we are streaming, send the chunk back to the client session.
200             # Otherwise add the new octets to the response's content.
201             # This should only add up to content-length octets total!
202 65 100       311 if ($self->[REQ_FACTORY]->is_streaming) {
203 31         36 DEBUG and warn "returning partial $response";
204 31         105 $self->[REQ_POSTBACK]->($self->[REQ_RESPONSE], $self->[REQ_BUFFER]);
205             }
206             else {
207 34         40 DEBUG and warn "adding to $response";
208 34         259 $self->[REQ_RESPONSE]->add_content($self->[REQ_BUFFER]);
209             }
210             }
211 106         10180 $self->[REQ_BUFFER] = '';
212             }
213              
214              
215             sub add_eof {
216 41     41 1 140 my ($self) = @_;
217              
218 41 50       146 return if ($self->[REQ_STATE] & RS_POSTED);
219              
220 41 50       144 unless (defined $self->[REQ_RESPONSE]) {
221             # XXX I don't know if this is actually used
222 0         0 $self->error(400, "incomplete response a " . $self->[REQ_ID]);
223 0         0 return;
224             }
225              
226             # RFC 2616: "If a message is received with both a Transfer-Encoding
227             # header field and a Content-Length header field, the latter MUST be
228             # ignored."
229             #
230             # Google returns a Content-Length header with its HEAD request,
231             # generating "incomplete response" errors. Added a special case to
232             # ignore content for HEAD requests. This may thwart keep-alive,
233             # however.
234              
235 41 100 100     166 if (
      66        
      100        
236             $self->[REQ_HTTP_REQUEST]->method() ne "HEAD" and
237             defined $self->[REQ_RESPONSE]->content_length and
238             not defined $self->[REQ_RESPONSE]->header("Transfer-Encoding") and
239             $self->[REQ_OCTETS_GOT] < $self->[REQ_RESPONSE]->content_length
240             ) {
241 1         115 DEBUG and warn(
242             "got " . $self->[REQ_OCTETS_GOT] . " of " .
243             $self->[REQ_RESPONSE]->content_length
244             );
245              
246 1         6 $self->error(
247             400,
248             "incomplete response b " . $self->[REQ_ID] . ". Wanted " .
249             $self->[REQ_RESPONSE]->content_length() . " octets. Got " .
250             $self->[REQ_OCTETS_GOT] . "."
251             );
252             }
253             else {
254 40         3316 $self->[REQ_STATE] |= RS_DONE;
255 40         141 $self->return_response();
256             }
257             }
258              
259              
260             sub add_content {
261 67     67 1 133 my ($self, $data) = @_;
262              
263 67 50       206 if (ref $data) {
264 0         0 $self->[REQ_STATE] = RS_DONE;
265 0     0   0 $data->scan (sub {$self->[REQ_RESPONSE]->header (@_) });
  0         0  
266 0         0 return 1;
267             }
268              
269 67         260 $self->[REQ_BUFFER] .= $data;
270              
271             # Count how many octets we've received. -><- This may fail on
272             # perl 5.8 if the input has been identified as Unicode. Then
273             # again, the C in Driver::SysRW may have untainted the
274             # data... or it may have just changed the semantics of length()
275             # therein. If it's done the former, then we're safe. Otherwise
276             # we also need to C.
277             # TODO: write test(s) for this.
278              
279 67         124 my $this_chunk_length = length($self->[REQ_BUFFER]);
280 67         114 $self->[REQ_OCTETS_GOT] += $this_chunk_length;
281              
282 67         240 my $max = $self->[REQ_FACTORY]->max_response_size();
283              
284 67         80 DEBUG and warn(
285             "REQ: request ", $self->ID,
286             " received $self->[REQ_OCTETS_GOT] bytes; maximum is $max"
287             );
288              
289             # Fail if we've gone over the maximum content size to return.
290 67 100 100     271 if (defined $max and $self->[REQ_OCTETS_GOT] > $max) {
291 2         19 $self->error(
292             406,
293             "Response content is longer than specified MaxSize of $max. " .
294             "Use range requests to retrieve specific amounts of content."
295             );
296              
297 2         6 $self->[REQ_STATE] |= RS_DONE;
298 2         4 $self->[REQ_STATE] &= ~RS_IN_CONTENT;
299 2         13 return 1;
300             }
301              
302             # keep this for the progress callback (it gets cleared in return_response
303             # as I say below, this needs to go away.
304 65         117 my $buffer = $self->[REQ_BUFFER];
305              
306 65         204 $self->return_response;
307 65         87 DEBUG and do {
308             warn(
309             "REQ: request ", $self->ID,
310             " got $this_chunk_length octets of content..."
311             );
312              
313             warn(
314             "REQ: request ", $self->ID, " has $self->[REQ_OCTETS_GOT]",
315             (
316             $self->[REQ_RESPONSE]->content_length()
317             ? ( " out of " . $self->[REQ_RESPONSE]->content_length() )
318             : ""
319             ),
320             " octets"
321             );
322             };
323              
324 65 100       268 if ($self->[REQ_RESPONSE]->content_length) {
325              
326             # Report back progress
327 44 100       7501 $self->[REQ_PROG_POSTBACK]->(
328             $self->[REQ_OCTETS_GOT],
329             $self->[REQ_RESPONSE]->content_length,
330             #TODO: ugh. this is stupid. Must remove/deprecate!
331             $buffer,
332             ) if ($self->[REQ_PROG_POSTBACK]);
333              
334             # Stop reading when we have enough content. -><- Should never be
335             # greater than our content length.
336 44 100       3487 if ($self->[REQ_OCTETS_GOT] >= $self->[REQ_RESPONSE]->content_length) {
337 18         625 DEBUG and warn(
338             "REQ: request ", $self->ID, " has a full response... moving to done."
339             );
340 18         71 $self->[REQ_STATE] |= RS_DONE;
341 18         23 $self->[REQ_STATE] &= ~RS_IN_CONTENT;
342 18         67 return 1;
343             }
344             }
345              
346 47         1418 return 0;
347             }
348              
349              
350             ### Methods to manage the request's timer.
351              
352              
353             sub timer {
354 121     121 1 198 my ($self, $timer) = @_;
355              
356             # do it this way so we can set REQ_TIMER to undef
357 121 50       340 if (@_ == 2) {
358 0 0       0 die "overwriting timer $self->[REQ_TIMER]" if $self->[REQ_TIMER];
359 0         0 $self->[REQ_TIMER] = $timer;
360             }
361 121         1005 return $self->[REQ_TIMER];
362             }
363              
364              
365             sub create_timer {
366 55     55 1 122 my ($self, $timeout) = @_;
367              
368             # remove old timeout first
369 55         103 my $kernel = $POE::Kernel::poe_kernel;
370              
371 55         451 my $seconds = $timeout - (time() - $self->[REQ_START_TIME]);
372 55         228 $self->[REQ_TIMER] = $kernel->delay_set(
373             got_timeout => $seconds, $self->ID
374             );
375 55         4598 DEBUG and warn(
376             "TKO: request ", $self->ID,
377             " has timer $self->[REQ_TIMER] going off in $seconds seconds\n"
378             );
379             }
380              
381              
382             sub remove_timeout {
383 85     85 1 321 my ($self) = @_;
384              
385 85         347 my $alarm_id = $self->[REQ_TIMER];
386 85 100       357 if (defined $alarm_id) {
387 55         101 my $kernel = $POE::Kernel::poe_kernel;
388 55         77 DEBUG and warn "REQ: Removing timer $alarm_id";
389 55         446 $kernel->alarm_remove($alarm_id);
390 55         5466 $self->[REQ_TIMER] = undef;
391             }
392             }
393              
394              
395             sub postback {
396 4     4 1 10 my ($self, $postback) = @_;
397              
398 4 50       18 if (defined $postback) {
399 0         0 DEBUG and warn "REQ: modifying postback";
400 0         0 $self->[REQ_POSTBACK] = $postback;
401             }
402 4         16 return $self->[REQ_POSTBACK];
403             }
404              
405              
406             sub _set_host_header {
407 60     60   136 my ($request) = @_;
408 60         214 my $uri = $request->uri;
409              
410 60         373 my ($new_host, $new_port);
411 60         114 eval {
412 60         203 $new_host = $uri->host();
413 60         1547 $new_port = $uri->port();
414             # Only include the port if it's nonstandard.
415 60 100 66     1911 if ($new_port == 80 || $new_port == 443) {
416 3         20 $request->header( Host => $new_host );
417             }
418             else {
419 57         356 $request->header( Host => "$new_host:$new_port" );
420             }
421             };
422              
423             # Return Boolean state of the eval.
424 60         2822 return $@;
425             }
426              
427              
428             sub does_redirect {
429 4     4 1 9 my ($self, $last) = @_;
430              
431 4 50       15 if (defined $last) {
432 4         12 $self->[REQ_HISTORY] = $last;
433             # delete OLD timeout
434             #my $alarm_id = $last->[REQ_TIMEOUT];
435             #DEBUG and warn "RED: Removing old timeout $alarm_id\n";
436             #$POE::Kernel::poe_kernel->alarm_remove ($alarm_id);
437             }
438              
439 4         13 return defined $self->[REQ_HISTORY];
440             }
441              
442              
443             sub check_redirect {
444 48     48 1 88 my ($self) = @_;
445              
446 48         265 my $max = $self->[REQ_FACTORY]->max_redirect_count;
447              
448 48 100       173 if (defined $self->[REQ_HISTORY]) {
449 3         19 $self->[REQ_RESPONSE]->previous($self->[REQ_HISTORY]->[REQ_RESPONSE]);
450             }
451              
452 48 100       393 return undef unless ($self->[REQ_RESPONSE]->is_redirect);
453              
454             # Make sure to frob any cookies set. Redirect cookies are cookies, too!
455 6         95 $self->[REQ_FACTORY]->frob_cookies($self->[REQ_RESPONSE]);
456              
457 6         24 my $location_uri = $self->[REQ_RESPONSE]->header('Location');
458              
459 6         213 DEBUG and warn "REQ: Preparing redirect to $location_uri";
460 6         37 my $base = $self->[REQ_RESPONSE]->base();
461 6         3103 $location_uri = URI->new($location_uri, $base)->abs($base);
462 6         503 DEBUG and warn "RED: Actual redirect uri is $location_uri";
463              
464 6         14 my $prev = $self;
465 6         11 my $history = 0;
466 6         30 while ($prev = $prev->[REQ_HISTORY]) {
467 1 50       5 last if ++$history > $max;
468             }
469              
470 6 100       22 if ($history >= $max) {
471             #$self->[REQ_STATE] |= RS_DONE;
472 2         3 DEBUG and warn "RED: Too much redirection";
473             }
474             else { # All fine, yield new request and mark this disabled.
475 4         26 my $newrequest = $self->[REQ_HTTP_REQUEST]->clone();
476              
477             # Sanitize new request per rt #30400.
478             # TODO - What other headers are security risks?
479 4         1087 $newrequest->remove_header('Cookie');
480              
481 4         177 DEBUG and warn "RED: new request $newrequest";
482 4         18 $newrequest->uri($location_uri);
483              
484             # Don't change the Host header on a relative redirect. This
485             # allows the HTTP::Request's Host to remain intact, per
486             # rt.cpan.org #63990.
487 4 50       134 if (defined $location_uri->scheme()) {
488 4         66 DEBUG and warn "RED: redirecting to absolute location $location_uri";
489 4         20 _set_host_header($newrequest);
490             }
491             else {
492 0         0 DEBUG and warn "RED: no new Host for relative redirect to $location_uri";
493             }
494              
495 4         14 $self->[REQ_STATE] = RS_REDIRECTED;
496 4         5 DEBUG and warn "RED: new request $newrequest";
497 4         30 return $newrequest;
498             }
499 2         13 return undef;
500             }
501              
502              
503             sub send_to_wheel {
504 53     53 1 114 my ($self) = @_;
505              
506 53         138 $self->[REQ_STATE] = RS_SENDING;
507              
508 53         101 my $http_request = $self->[REQ_HTTP_REQUEST];
509              
510             # MEXNIX 2002-06-01: Check for proxy. Request query is a bit
511             # different...
512              
513 53         86 my $request_uri;
514 53 100       199 if ($self->[REQ_USING_PROXY]) {
515 8         103 $request_uri = $http_request->uri()->canonical();
516             }
517             else {
518 45         175 $request_uri = $http_request->uri()->canonical()->path_query();
519             }
520              
521 53         8761 my $request_string = (
522             $http_request->method() . ' ' .
523             $request_uri . ' ' .
524             $http_request->protocol() . "\x0D\x0A" .
525             $http_request->headers_as_string("\x0D\x0A") . "\x0D\x0A"
526             );
527            
528 53 100       6106 if ( !ref $http_request->content() ) {
529 52         1748 $request_string .= $http_request->content(); # . "\x0D\x0A"
530             }
531              
532 53         729 DEBUG and do {
533             my $formatted_request_string = $request_string;
534             $formatted_request_string =~ s/([^\n])$/$1\n/;
535             $formatted_request_string =~ s/^/| /mg;
536             warn ",----- SENDING REQUEST ", '-' x 56, "\n";
537             warn $formatted_request_string;
538             warn "`", '-' x 78, "\n";
539             };
540              
541 53         271 $self->[REQ_CONNECTION]->wheel->put ($request_string);
542             }
543              
544              
545             sub wheel {
546 165     165 1 517 my ($self) = @_;
547              
548             # FIXME - We don't support older versions of POE. Remove this chunk
549             # of code when we're not fixing something else.
550             #
551             #if (defined $new_wheel) {
552             # Switch wheels. This is cumbersome, but it works around a bug in
553             # older versions of POE.
554             # $self->[REQ_WHEEL] = undef;
555             # $self->[REQ_WHEEL] = $new_wheel;
556             #}
557              
558 165 100       453 return unless $self->[REQ_CONNECTION];
559 162         577 return $self->[REQ_CONNECTION]->wheel;
560             }
561              
562              
563             sub error {
564 14     14 1 90 my ($self, $code, $message) = @_;
565              
566 14         38 my $nl = "\n";
567              
568 14         175 my $http_msg = status_message($code);
569 14         321 my $r = HTTP::Response->new($code, $http_msg, [ 'X-PCCH-Errmsg', $message ]);
570 14         2047 my $m = (
571             "$nl"
572             . "Error: $http_msg$nl"
573             . "$nl"
574             . "

Error: $http_msg

$nl"
575             . "$message$nl"
576             . "This is a client error, not a server error.$nl"
577             . "$nl"
578             . "$nl"
579             );
580              
581 14         109 $r->content($m);
582 14         382 $r->request($self->[REQ_HTTP_REQUEST]);
583 14         182 $self->[REQ_POSTBACK]->($r);
584 14         2602 $self->[REQ_STATE] |= RS_POSTED;
585             }
586              
587              
588             sub connect_error {
589 3     3 1 9 my ($self, $operation, $errnum, $errstr) = @_;
590              
591 3         7 my $host = $self->[REQ_HOST];
592 3         7 my $port = $self->[REQ_PORT];
593              
594 3 50 66     23 if ($operation eq "connect" and $errnum == ETIMEDOUT) {
595 0         0 $self->error(408, "Connection to $host:$port failed: timeout");
596             }
597             else {
598 3         25 $self->error(
599             RC_INTERNAL_SERVER_ERROR,
600             "Connection to $host:$port failed: $operation error $errnum: $errstr"
601             );
602             }
603              
604 3         16 return;
605             }
606              
607              
608 60     60 1 1163 sub host { $_[0][REQ_HOST] }
609              
610              
611 60     60 1 254 sub port { $_[0][REQ_PORT] }
612              
613              
614             sub close_connection {
615 19     19 1 35 my ($self) = @_;
616 19 50       107 return unless defined $self->[REQ_CONNECTION];
617 19         94 $self->[REQ_CONNECTION]->close();
618 19         7543 $self->[REQ_CONNECTION] = undef;
619             }
620              
621              
622             sub scheme {
623 112     112 1 215 my $self = shift;
624              
625 112 100       689 $self->[REQ_USING_PROXY] ? 'http' : $self->[REQ_HTTP_REQUEST]->uri->scheme;
626             }
627              
628              
629             sub DESTROY {
630 60     60   2058 my ($self) = @_;
631              
632 60         456 delete $self->[REQ_CONNECTION];
633 60         16790 delete $self->[REQ_FACTORY];
634             }
635              
636             1;
637              
638             __END__