First pass at adding key files
This commit is contained in:
1430
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/http.pm
Normal file
1430
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/http.pm
Normal file
File diff suppressed because it is too large
Load Diff
649
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/http/Header.pm
Normal file
649
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/http/Header.pm
Normal file
@ -0,0 +1,649 @@
|
||||
# ====================================================================
|
||||
# Gossamer Threads Module Library - http://gossamer-threads.com/
|
||||
#
|
||||
# GT::WWW::http::Header
|
||||
# Author: Jason Rhinelander
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# $Id: Header.pm,v 1.8 2004/02/17 01:33:08 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# ====================================================================
|
||||
#
|
||||
# Description:
|
||||
# Header object for GT::WWW::http request/response headers.
|
||||
#
|
||||
|
||||
package GT::WWW::http::Header;
|
||||
use strict;
|
||||
|
||||
use Carp;
|
||||
use GT::Socket::Client qw/CRLF/;
|
||||
use overload
|
||||
'""' => \&format_headers,
|
||||
bool => \&boolean;
|
||||
|
||||
my $ctls = '\x00-\x1f\x7f'; # Control characters (CTL in HTTP 1.1 RFC 2616)
|
||||
my $ctls_without_tab = '\x00-\x08\x0a-\x1f\x7f';
|
||||
my $separators = '()<>@,;:\\\\"/\[\]?={} \t'; # Separators
|
||||
my $token = "[^$ctls$separators]"; # HTTP "token" (RFC 2616)
|
||||
my $quoted_string = qq{"((?:\\\\.|[^$ctls_without_tab"])*)"}; # HTTP 1.1 quoted-string.
|
||||
|
||||
my %Private;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
$class = ref $class if ref $class;
|
||||
my $self = [];
|
||||
bless $self, $class;
|
||||
}
|
||||
|
||||
sub boolean { 1 } # So you can you do things like: $header or die
|
||||
|
||||
# Adds one or more headers. Takes a list of headers => value pairs.
|
||||
# Without arguments, returns a list of all header names.
|
||||
# With just one argument, returns all value(s) for that header (case-
|
||||
# insensitive).
|
||||
# When setting headers, you can pass in an array reference for the header
|
||||
# value. The array will be passed as a list to join_words, and the return used
|
||||
# as the header value.
|
||||
|
||||
# Sets a _join_words separator to something other than , - typically ;
|
||||
sub _separator {
|
||||
my ($self, $sep) = @_;
|
||||
$Private{$self}->{separator} = $sep if $sep;
|
||||
}
|
||||
# Forces _join_words to put "quotes" around values. You should call this, add
|
||||
# the header that needs the quotes, then call ->_unforce_quotes;.
|
||||
sub _force_quotes {
|
||||
my $self = shift;
|
||||
$Private{$self}->{force_quotes} = 1;
|
||||
}
|
||||
sub _unforce_quotes {
|
||||
my $self = shift;
|
||||
$Private{$self}->{force_quotes} = 0;
|
||||
}
|
||||
|
||||
sub header {
|
||||
my $self = shift;
|
||||
if (@_ == 1) {
|
||||
# Requesting a header, ie. $obj->header("Content-Type")
|
||||
my $header = lc shift;
|
||||
my @return;
|
||||
for (my $i = 0; $i < @$self; $i += 2) {
|
||||
if (lc $self->[$i] eq $header) {
|
||||
push @return, $self->[$i + 1];
|
||||
last unless wantarray;
|
||||
}
|
||||
}
|
||||
return wantarray ? @return : $return[0];
|
||||
}
|
||||
elsif (@_) {
|
||||
@_ % 2 and croak "Invalid parameters to header: Odd number of elements passed to header()";
|
||||
|
||||
while (@_) {
|
||||
my ($k, $v) = splice @_, 0, 2;
|
||||
if (ref $v eq 'ARRAY') {
|
||||
$v = $self->join_words(@$v);
|
||||
}
|
||||
push @$self, $k, $v;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
my @return;
|
||||
for (my $i = 0; $i < @$self; $i++) {
|
||||
push @return, $self->[$i];
|
||||
}
|
||||
return @return;
|
||||
}
|
||||
}
|
||||
|
||||
sub header_words {
|
||||
my ($self, $header) = @_;
|
||||
$header or croak "Usage: \$header->header_words(HEADER_NAME)";
|
||||
|
||||
my @result;
|
||||
for (my $i = 0; $i < @$self; $i += 2) {
|
||||
if (lc $self->[$i] eq lc $header) {
|
||||
push @result, $self->split_words($self->[$i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return @result;
|
||||
}
|
||||
|
||||
sub split_words {
|
||||
shift if $_[0] and UNIVERSAL::isa($_[0], __PACKAGE__);
|
||||
my $str = shift or return ();
|
||||
my @result;
|
||||
# Pretend $str is: video/x-mng,image/png, foo=bar, image/gif;q=0.3,asdf/zxcv="y,uc;k";q="0.2";blah="a;b,c",*/*;q=0.1
|
||||
while (length $str) {
|
||||
if ($str =~ s/^\s*([^$ctls\s=,;]+)\s*//) { # parameter 'token' or 'attribute'
|
||||
push @result, $1;
|
||||
|
||||
my @val;
|
||||
# The goal here is to get this array containing (given the above example) undef for
|
||||
# "video/x-mng", "bar" for "foo", [undef, "q", "0.3"] for "image/gif",
|
||||
# ["y,uc;k", "q", "0.2", "blah", "a;b,c"] for "asdf/zxcv".
|
||||
|
||||
# First, handle an = clause, such as '=bar', or '="y,uc;k"'
|
||||
if ($str =~ s/^=\s*$quoted_string//) { # quoted string (e.g. "y,uc;k")
|
||||
(my $val = $1) =~ s/\\(.)/$1/g;
|
||||
push @val, $val;
|
||||
}
|
||||
elsif ($str =~ s/^=\s*([^$ctls\s;,]*)//) { # some unquoted value (e.g. bar)
|
||||
push @val, $1;
|
||||
}
|
||||
else {
|
||||
push @val, undef;
|
||||
}
|
||||
|
||||
# Now look for continuing values (e.g. ;q="0.2";blah="a;b,c")
|
||||
while ($str =~ s/^;([^$ctls\s=,;]+)\s*//) {
|
||||
push @val, $1;
|
||||
# Look for an = clause, such as ="a;b,c"
|
||||
if ($str =~ s/^=\s*$quoted_string//) { # quoted string (e.g. "a;b,c")
|
||||
(my $val = $1) =~ s/\\(.)/$1/g;
|
||||
push @val, $val;
|
||||
}
|
||||
elsif ($str =~ s/^=\s*([^$ctls\s;,]*)//) { # some unquoted value (e.g. bar)
|
||||
push @val, $1;
|
||||
}
|
||||
else {
|
||||
push @val, undef;
|
||||
}
|
||||
}
|
||||
|
||||
push @result, @val == 1 ? $val[0] : \@val;
|
||||
}
|
||||
elsif ($str !~ s/^\s*[,;\s]//) {
|
||||
local $" = "|";
|
||||
die "Invalid header encountered: '$str' (Found \@result=(@result))";
|
||||
}
|
||||
}
|
||||
|
||||
@result;
|
||||
}
|
||||
|
||||
# Takes a header and header word, and returns true if the header word is
|
||||
# present in the header. For example,
|
||||
# $header->contains(Expect => '100-continue')
|
||||
# will return true for the header:
|
||||
# Expect: foo=bar, 100-continue, bar=foo
|
||||
sub contains {
|
||||
my ($self, $header, $word) = @_;
|
||||
$header and $word or croak 'Usage: $header->contains(Header => Header_Word)';
|
||||
|
||||
my @words = $self->header_words($header);
|
||||
for (my $i = 0; $i < @words; $i += 2) {
|
||||
if ($words[$i] eq $word) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return undef;
|
||||
}
|
||||
|
||||
sub join_words {
|
||||
my $self;
|
||||
$self = shift if $_[0] and UNIVERSAL::isa($_[0], __PACKAGE__);
|
||||
my @words = @_;
|
||||
my @encoded;
|
||||
for (my $i = 0; $i < @words; $i += 2) {
|
||||
my ($k, $v) = @words[$i, $i + 1];
|
||||
my @pairs = ($k, ref $v eq 'ARRAY' ? @$v : $v);
|
||||
|
||||
@pairs % 2 and croak "Invalid composite value passed in for word '$k': Even number of elements in array ref";
|
||||
|
||||
my @str;
|
||||
while (@pairs) {
|
||||
my ($word, $value) = splice @pairs, 0, 2;
|
||||
$word =~ /^[^$ctls\s=;,]+$/
|
||||
or croak "Unable to join: word contains invalid characters: '$word'";
|
||||
my $str = $word;
|
||||
if (defined $value) {
|
||||
$value =~ /[$ctls_without_tab]/
|
||||
and croak "Unable to join: word value for word '$word' contains control characters: '$value'";
|
||||
$str .= '=';
|
||||
if ((not $self or not $Private{$self}->{force_quotes}) and $value =~ /^$token+$/) {
|
||||
# If it only contains "token" characters, we don't need to quote it
|
||||
$str .= $value;
|
||||
}
|
||||
else {
|
||||
$value =~ s/([\\"])/\\$1/g;
|
||||
$str .= qq'"$value"';
|
||||
}
|
||||
}
|
||||
|
||||
push @str, $str;
|
||||
}
|
||||
|
||||
push @encoded, join ';', @str;
|
||||
|
||||
}
|
||||
|
||||
return join "$Private{$self}->{separator} ", @encoded
|
||||
if $self and $Private{$self}->{separator};
|
||||
|
||||
return join ', ', @encoded;
|
||||
}
|
||||
|
||||
# Deletes a word from a header's value. If the word is present more than once,
|
||||
# all forms are removed. Returned is, in scalar context, an integer indicating
|
||||
# how many headers were removed (0 for no words (or no header) found). In list
|
||||
# context, you get a list of all the values removed, or undef for valueless
|
||||
# words.
|
||||
sub delete_header_word {
|
||||
my ($self, $header, $word) = @_;
|
||||
my @ret;
|
||||
$header and $word or croak 'Usage: $header->delete_header_word(HEADER, WORD)';
|
||||
for (my $i = 0; $i < @$self; $i += 2) {
|
||||
if (lc $self->[$i] eq lc $header) {
|
||||
my @words = $self->split_words($self->[$i + 1]);
|
||||
my $found;
|
||||
for (my $j = 0; $j < @words; $j += 2) {
|
||||
if ($words[$j] eq $word) {
|
||||
$found++;
|
||||
push @ret, $words[$j + 1];
|
||||
splice @words, $j, 2;
|
||||
$j -= 2;
|
||||
}
|
||||
}
|
||||
if ($found and @words) {
|
||||
$self->[$i + 1] = $self->join_words(@words);
|
||||
}
|
||||
elsif ($found) { # This header contains only the header word
|
||||
splice @$self, $i, 2;
|
||||
$i -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ret; # If the sub is called in scalar context, so is this
|
||||
}
|
||||
|
||||
# Just like header(), but first deletes the headers to be added. Hence,
|
||||
# $obj->replace_header($obj->header) should be a no-op.
|
||||
sub replace_header {
|
||||
my $self = shift;
|
||||
croak "Invalid parameters to replace_header: \$obj->replace_header(KEY => VALUE[, KEY => VALUE, ...]);"
|
||||
if !@_ or @_ % 2;
|
||||
my %headers;
|
||||
for (my $i = 0; $i < @_; $i += 2) {
|
||||
$headers{$_[$i]}++;
|
||||
}
|
||||
$self->delete_header(keys %headers);
|
||||
|
||||
$self->header(@_);
|
||||
}
|
||||
|
||||
sub format_headers {
|
||||
my $self = shift;
|
||||
return '' if !@$self;
|
||||
my $return = '';
|
||||
for (my $i = 0; $i < @$self; $i += 2) {
|
||||
my ($key, $value) = @$self[$i, $i + 1];
|
||||
# Valid characters from HTTP/1.1 RFC, section 4.2 (page 32)
|
||||
$key =~ s|([$ctls$separators()<>@,;:\\"/\[\]?={} \t])|sprintf "%%%02X", ord $1|eg;
|
||||
$value =~ s|([$ctls])|sprintf "%%%02X", ord $1|eg;
|
||||
$return .= "$key: $value" . CRLF;
|
||||
}
|
||||
$return .= CRLF;
|
||||
return $return;
|
||||
}
|
||||
|
||||
# Clears all headers set for the current object.
|
||||
sub clear_headers {
|
||||
my $self = shift;
|
||||
$#$self = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
# Deletes one or more headers. Takes a list of headers to remove.
|
||||
sub delete_header {
|
||||
my ($self, @headers) = @_;
|
||||
return 0 unless @$self;
|
||||
my $headers = join "|", map quotemeta, @headers;
|
||||
my $found;
|
||||
for (my $i = 0; $i < @$self; $i += 2) {
|
||||
if ($self->[$i] =~ /^(?:$headers)$/i) {
|
||||
splice @$self, $i, 2;
|
||||
$i -= 2;
|
||||
$found++;
|
||||
}
|
||||
}
|
||||
return $found;
|
||||
}
|
||||
|
||||
DESTROY {
|
||||
my $self = shift;
|
||||
delete $Private{$self};
|
||||
1;
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
GT::WWW::http::Header - Module for GT::WWW::http request/response headers.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Typically:
|
||||
|
||||
# Assuming $www is a GT::WWW::http object
|
||||
my $request_header = $www->header;
|
||||
|
||||
# Set a header:
|
||||
$request_header->header('Some-Http-Header' => 'Header value');
|
||||
|
||||
# After making a request:
|
||||
my $response_header = $www->response->header;
|
||||
# -- or --
|
||||
my $response_header = $response->header; # $response is the return of, e.g. $www->get
|
||||
|
||||
Much more advanced headers can be set and determined, using the various methods
|
||||
available as described below.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module provides an easy to use yet powerful header retrieval/manipulation
|
||||
object suitable for most HTTP headers.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
First, a note about the methods described which add/change/delete headers: such
|
||||
methods should only be called on a request header, and only before making a
|
||||
request. Although nothing prevents you from making changes to the request
|
||||
header after having made the request, or from changing the headers of a
|
||||
response header object, such behaviour should be considered very bad practise
|
||||
and is B<strongly> discouraged.
|
||||
|
||||
=head2 header
|
||||
|
||||
This is the most commonly used method as it is used both to add and retrieve
|
||||
headers, depending on its usage. The examples below assume the following
|
||||
header:
|
||||
|
||||
Date: Sun, 12 Jan 2003 08:21:21 GMT
|
||||
Server: Apache
|
||||
Keep-Alive: timeout=15, max=100
|
||||
Connection: Keep-Alive
|
||||
Content-Type: text/html
|
||||
Content-Encoding: gzip
|
||||
Content-Length: 3215
|
||||
X-Foo: bar1
|
||||
X-Foo: bar2, bar3
|
||||
|
||||
With no arguments, a list of all the header names is returned. Given the
|
||||
example, the following list would be returned:
|
||||
|
||||
('Date', 'Server', 'Keep-Alive', 'Connection', 'Content-Type', 'Content-Encoding', 'Content-Length', 'X-Foo', 'X-Foo')
|
||||
|
||||
With a single argument, a list of value(s) for headers of that name are
|
||||
returned. In scalar context, only the first value is returned. In list
|
||||
context, a list of all values is returned. Note that the header named passed
|
||||
in is case-insensitive.
|
||||
|
||||
my $server = $header->header('server'); # returns 'Apache'
|
||||
my $foo = $header->header('X-Foo'); # returns 'bar1'
|
||||
my @foo = $header->header('x-Foo'); # returns ('bar1', 'bar2, bar3')
|
||||
|
||||
Finally, when more than one argument is provided, header values are set. At
|
||||
its simplest level, it takes a list of key => value pairs (NOT a hash, since
|
||||
duplicate keys are possible) of headers to set. So, to set the headers
|
||||
'Server' and 'Content-Length' above at once, you could call:
|
||||
|
||||
$header->header(Server => 'Apache', 'Content-Length' => 3215);
|
||||
|
||||
Or, if you prefer:
|
||||
|
||||
$header->header(Server => 'Apache');
|
||||
$header->header('Content-Length' => 3215);
|
||||
|
||||
Note that the order in which headers are added is preserved, for times when the
|
||||
order of headers is important.
|
||||
|
||||
|
||||
B<WARNING>: Before reading the below information, you should first know that it
|
||||
describes advanced usage of the header() method and requires have a grasp of
|
||||
the intricacies of HTTP headers; the following is _not_ required knowledge for
|
||||
typical GT::WWW use.
|
||||
|
||||
Consider the above Keep-Alive header an example. Instead of specifying:
|
||||
|
||||
$header->header('Keep-Alive' => 'timeout=15, max=100');
|
||||
|
||||
you could alternately write it as:
|
||||
|
||||
$header->header('Keep-Alive' => [timeout => 15, max => 100]);
|
||||
|
||||
This allows you a more pragmatic approach when you already have some sort of
|
||||
data structure of the header options. You can go a step further with this, by
|
||||
specifying C<undef> as the value:
|
||||
|
||||
# Set the second X-Foo header in the example:
|
||||
$header->header('X-Foo' => [bar2 => undef, bar3 => undef]);
|
||||
|
||||
header() also allows you to set values such as:
|
||||
|
||||
image/gif;q=0.2
|
||||
|
||||
As can be seen in this example:
|
||||
|
||||
Accept: image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
|
||||
|
||||
To do so, specify the suboption value as another array reference. The first
|
||||
element of the array reference is usually undef, while the remaining are the
|
||||
k=v pairs in the segment. So, in the above header, the 'image/gif;q=0.2' section
|
||||
would be specified as:
|
||||
|
||||
'image/gif' => [undef, q => 0.2]
|
||||
|
||||
(If a segment such as "foo=bar;bar=foo" is ever needed, the C<undef> would be
|
||||
changed to C<"bar">.)
|
||||
|
||||
So, piecing it all together, the Accept header shown above could be specified
|
||||
like this:
|
||||
|
||||
$header->header(
|
||||
Accept => [
|
||||
'image/png' => undef,
|
||||
'image/jpeg' => undef,
|
||||
'image/gif' => [undef, q => 0.2],
|
||||
'*/*' => [undef, q => 0.1]
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
=head2 header_words
|
||||
|
||||
When you need to see it a header value contains a particular "word", this
|
||||
method is the one to use. As an example, consider this header:
|
||||
|
||||
X-Foo: bar, bar2, bar3
|
||||
|
||||
In order to determine whether or not "bar2" has been specified as an X-Foo
|
||||
value, you could attempt some sort of regex - or you could just call this
|
||||
method. The return value splits up the header in such a way as to be useful to
|
||||
determine the exact information contained within the header.
|
||||
|
||||
The method takes a case-insensitive header name, just like the single-argument
|
||||
form of header().
|
||||
|
||||
A even-numbered hash-I<like> list is always returned - though each element of
|
||||
that list depends on the content of the header. First of all, if the header
|
||||
specified does not exist, you'll get an empty list back.
|
||||
|
||||
Assuming that the header does exist, it will first be broken up by C<,>.
|
||||
|
||||
The even-indexed (0, 2, 4, ...) elements of the list are the keys, while the
|
||||
odd numbered elements are the values associated with those keys - or undef if
|
||||
there is no value (as above; an example with values is shown below).
|
||||
|
||||
So, using the above X-Foo header example, calling this method with C<'X-Foo'>
|
||||
as an argument would give you back the list:
|
||||
|
||||
(bar => undef, bar2 => undef, bar3 => undef)
|
||||
|
||||
Getting a little more complicated, consider the following header:
|
||||
|
||||
X-Foo: bar, bar2=foo, bar3
|
||||
|
||||
Because of the "=foo" part, the list returned would now be:
|
||||
|
||||
(bar => undef, bar2 => "foo", bar3 => undef)
|
||||
|
||||
Quoting of values is also permitted, so the following would be parsed correctly
|
||||
with C<'1;2,3=4"5\6'> being the value of bar2:
|
||||
|
||||
X-Foo: bar, bar2="1;2,3=4\"5\\6", bar3
|
||||
|
||||
Getting more complicated, this method also handles complex values containing
|
||||
more than one piece of information. A good example of this is in content type
|
||||
weighting used by most browsers. As a real life example (generated by
|
||||
the Phoenix web browser):
|
||||
|
||||
Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
|
||||
|
||||
Working that into the X-Foo example, consider this header:
|
||||
|
||||
X-Foo: bar, bar2=foo, bar3;foo1=24;foo2=10
|
||||
|
||||
In this case, the value for bar3 will become an array reference to handle the
|
||||
multiple pieces of information in the third part:
|
||||
|
||||
(bar => undef, bar2 => "foo", bar3 => [undef, foo1 => 24, foo2 => 10])
|
||||
|
||||
(If you've read the advanced section of the L<C<header()>|/header>
|
||||
documentation, and this looks familiar, you're right - the return value of this
|
||||
function, if put in an array reference, is completely compatible with a
|
||||
header() value.)
|
||||
|
||||
The C<undef> value at the beginning of the array reference is rarely anything other
|
||||
than C<undef>, but it I<could> be, if a header such as this were encountered:
|
||||
|
||||
X-Foo: bar=foo,foo1=10
|
||||
|
||||
That would return:
|
||||
|
||||
(bar => ["foo", foo1 => 10])
|
||||
|
||||
One additional thing to note is that header_words() returns the header words
|
||||
for B<all> matching headers. Thus if the following two headers were set:
|
||||
|
||||
X-Foo: bar, bar2=foo
|
||||
X-Foo: bar3
|
||||
|
||||
You would get the same return as if this header was set (shown above):
|
||||
|
||||
X-Foo: bar, bar2=foo, bar3
|
||||
|
||||
|
||||
A good example usage of this is for a file download. To get the filename, you
|
||||
would do something like:
|
||||
|
||||
my %cd = $header->header_words('Content-Disposition');
|
||||
my $filename;
|
||||
if ($cd{filename}) { $filename = $cd{filename} }
|
||||
else { $filename = "unknown" }
|
||||
|
||||
=head2 split_words
|
||||
|
||||
This can be called as object method, class method, or function - it takes a
|
||||
single argument, a string, which it proceeds to split up as described for the
|
||||
above header_words() method. Note that this knows nothing about header names -
|
||||
it simply knows how to break a header value into the above format.
|
||||
|
||||
This method is used internally by header_words(), but can be used separately if
|
||||
desired.
|
||||
|
||||
=head2 contains
|
||||
|
||||
This method takes two arguments: a header, and a header word. It returns true
|
||||
if the header word passed is found in the header specified. For example, the
|
||||
following would return true:
|
||||
|
||||
$header->contains('X-Foo' => 'bar2')
|
||||
|
||||
for any of these headers:
|
||||
|
||||
X-Foo: bar2
|
||||
X-Foo: bar, bar2, bar3
|
||||
X-Foo: bar, bar2=10, bar3
|
||||
X-Foo: bar, bar2=10;q=0.3, bar3
|
||||
|
||||
but not for either of these:
|
||||
|
||||
X-Foo: bar, bar3=bar2
|
||||
X-Foo: bar, bar3;bar2=10
|
||||
|
||||
=head2 join_words
|
||||
|
||||
join_words() does the opposite of split_words(). That is, it takes a value such
|
||||
as might be returned by split_words(), and joins it up properly, quoting if
|
||||
necessary. This is called internally when creating the actual header, and can
|
||||
be called separately at a method or function if desired.
|
||||
|
||||
=head2 delete_header_word
|
||||
|
||||
This takes a header and header word, and proceeds to remove any occurances of
|
||||
the header word from the header specified.
|
||||
|
||||
After calling:
|
||||
|
||||
$header->delete_header_word('X-Foo', 'bar2');
|
||||
|
||||
this header:
|
||||
|
||||
X-Foo: bar, bar2;foo=bar, bar3
|
||||
|
||||
would become:
|
||||
|
||||
X-Foo: bar, bar3
|
||||
|
||||
=head2 delete_header
|
||||
|
||||
This takes a list of header names. The headers specified are completely
|
||||
removed.
|
||||
|
||||
=head2 replace_header
|
||||
|
||||
This 2 or more arguments in exactly the same way as header(), however all the
|
||||
specified headers are deleted (assuming they exist) before being readded.
|
||||
|
||||
=head2 format_headers
|
||||
|
||||
This returns a properly formatted (lines are CRLF delimited) header. If you
|
||||
use the header as a string (i.e. C<"$header">), this method will be internally
|
||||
called, and so generally does not need to be called directly.
|
||||
|
||||
The returned string has the final blank line that identifies the end of the
|
||||
header.
|
||||
|
||||
=head2 clear_headers
|
||||
|
||||
This deletes all headers.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<GT::WWW::http>
|
||||
L<GT::WWW>
|
||||
RFC 2616: L<http://www.ietf.org/rfc/rfc2616.txt>
|
||||
|
||||
=head1 MAINTAINER
|
||||
|
||||
Jason Rhinelander
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
http://www.gossamer-threads.com/
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
Revision: $Id: Header.pm,v 1.8 2004/02/17 01:33:08 jagerman Exp $
|
||||
|
||||
=cut
|
@ -0,0 +1,263 @@
|
||||
# ====================================================================
|
||||
# Gossamer Threads Module Library - http://gossamer-threads.com/
|
||||
#
|
||||
# GT::WWW::http::Response
|
||||
# Author: Jason Rhinelander
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# $Id: Response.pm,v 1.8 2004/08/04 19:23:07 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# ====================================================================
|
||||
#
|
||||
# Description:
|
||||
# Response object for GT::WWW HTTP/HTTPS requests.
|
||||
#
|
||||
|
||||
package GT::WWW::http::Response;
|
||||
use strict;
|
||||
|
||||
use vars qw/$AUTOLOAD/;
|
||||
use overload
|
||||
'""' => \&content,
|
||||
bool => \&boolean,
|
||||
cmp => \&strcmp;
|
||||
use Carp;
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
$class = ref $class if ref $class;
|
||||
|
||||
my $self = {};
|
||||
bless $self, $class;
|
||||
}
|
||||
|
||||
AUTOLOAD {
|
||||
my ($self, @args) = @_;
|
||||
my ($attr) = $AUTOLOAD =~ /([^:]+)$/;
|
||||
if (@args) {
|
||||
$self->{$attr} = shift @args;
|
||||
}
|
||||
$self->{$attr};
|
||||
}
|
||||
|
||||
sub content { $_[0]->{content} }
|
||||
|
||||
sub boolean { 1 } # So you can you do things like: $www->get() or die
|
||||
|
||||
sub status {
|
||||
my $self = shift;
|
||||
if (@_) {
|
||||
my ($num, $str) = @_;
|
||||
$self->{status} = GT::WWW::http::Response::Status->new($num, $str);
|
||||
}
|
||||
$self->{status};
|
||||
}
|
||||
|
||||
sub header {
|
||||
my $self = shift;
|
||||
if (@_) {
|
||||
$self->{header}->header(@_);
|
||||
}
|
||||
else {
|
||||
$self->{header};
|
||||
}
|
||||
}
|
||||
|
||||
sub strcmp { $_[2] ? $_[1] cmp $_[0]->{content} : $_[0]->{content} cmp $_[1] }
|
||||
|
||||
package GT::WWW::http::Response::Status;
|
||||
|
||||
use overload
|
||||
'""' => \&string,
|
||||
bool => \&boolean,
|
||||
'0+' => \&numeric,
|
||||
'+' => \&addition,
|
||||
'<=>' => \&numcmp,
|
||||
'cmp' => \&strcmp;
|
||||
|
||||
sub new {
|
||||
my ($class, $numeric, $string) = @_;
|
||||
my $self = [$numeric, $string];
|
||||
bless $self, $class;
|
||||
}
|
||||
|
||||
sub numeric { $_[0]->[0] }
|
||||
sub string { "$_[0]->[0] $_[0]->[1]" }
|
||||
sub boolean { substr($_[0]->[0], 0, 1) eq '2' }
|
||||
sub addition { int($_[0]) + int($_[1]) }
|
||||
sub numcmp { $_[2] ? $_[1] <=> $_[0]->[0] : $_[0]->[0] <=> $_[1] }
|
||||
sub strcmp { $_[2] ? $_[1] cmp $_[0]->[1] : $_[0]->[1] cmp $_[1] }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
GT::WWW::http::Response and GT::WWW::http::Response::Status - Overloaded
|
||||
response objects for HTTP request data.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
# ($www is continued from GT::WWW::http SYNOPSIS)
|
||||
|
||||
my $response = $www->get(); # or post(), or head()
|
||||
# -- or, after having called get(), post() or head(): --
|
||||
my $response = $www->response();
|
||||
|
||||
my $status = $response->status();
|
||||
|
||||
my $content = "$response";
|
||||
my $response_code = int($status); # i.e. 200, 404, 500
|
||||
my $response_str = "$status"; # i.e. 'OK', 'Not Found', 'Internal Server Error'
|
||||
if ($status) { # True for 2xx requests, false otherwise (e.g. 404, 500, etc.)
|
||||
...
|
||||
}
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
GT::WWW::http::Response objects are returned by the L<C<get()>|GT::WWW/get>,
|
||||
L<C<post()>|GT::WWW/post>, and L<C<head()>|GT::WWW/head> methods of GT::WWW
|
||||
HTTP requests (and derivatives - i.e. HTTPS), or by calling
|
||||
L<C<response()>|GT::WWW::http/response> after having made such a request. The
|
||||
objects are overloaded in order to provide a simple interface to the response,
|
||||
while still having all the information available.
|
||||
|
||||
A response object always returns true in boolean context, allowing you to do
|
||||
things like C<$www-E<gt>get($url) or die;> - even when a page is empty, or
|
||||
contains just '0'.
|
||||
|
||||
=head1 CONTENT
|
||||
|
||||
In addition to the methods described below, the way to simply access the data
|
||||
returned by the server is to simply use it like a string - for example,
|
||||
printing it, concatenating it with another string, or quoting it.
|
||||
|
||||
You should, however, take note that when using the L<C<chunk()>|GT::WWW/chunk>
|
||||
option for an HTTP request, the content will not be available.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
For simple requests, often the content alone is enough. The following methods
|
||||
are used to determine any other information available about the response.
|
||||
|
||||
=head2 content
|
||||
|
||||
Returns the content of the HTTP response. Note that this returns the exact
|
||||
same value as using the object in double quotes.
|
||||
|
||||
=head2 status
|
||||
|
||||
Returns the response status object for the request. This object provides three
|
||||
pieces of information, and has no public methods. Instead, the data is
|
||||
retrieved based on the context of the object.
|
||||
|
||||
my $status = $response->status;
|
||||
|
||||
(N.B. Though the examples below use a C<$status> variable, there is no reason
|
||||
they couldn't be written to use C<$response-E<gt>status> instead.)
|
||||
|
||||
=over 4
|
||||
|
||||
=item numeric status
|
||||
|
||||
The numeric status of an HTTP request (e.g. 200, 404, 500) is available simply
|
||||
by using the status object as a number.
|
||||
|
||||
my $numeric_status = int $status;
|
||||
|
||||
=item string status
|
||||
|
||||
The string status of an HTTP request (e.g. "OK", "Not Found", "Internal Server
|
||||
Error") is available by using the status object as a string (e.g. printing it,
|
||||
or concatenating it with another string).
|
||||
|
||||
# Assign the status string to a variable:
|
||||
my $status_string = "$status";
|
||||
|
||||
# Print out the status string:
|
||||
print $status;
|
||||
|
||||
# To get a string such as "500 Internal Server Error":
|
||||
my $string = int($status) . " " . $status;
|
||||
|
||||
=item boolean status
|
||||
|
||||
In order to quickly determine whether or not a request was successful, you can
|
||||
use the status object in a boolean context.
|
||||
|
||||
Success is determined by the numeric status of the response. Any 2xx status
|
||||
(usually 200 OK, but there are others) counts as a successful response, while
|
||||
any other status counts as a failure.
|
||||
|
||||
if ($status) { print "Request successful!" }
|
||||
else { print "Request failed!" }
|
||||
|
||||
=back
|
||||
|
||||
=head2 header
|
||||
|
||||
This method, called without arguments, returns the
|
||||
L<header|GT::WWW::http::Header> object for the response.
|
||||
|
||||
my $header = $response->header;
|
||||
|
||||
If this method is called with arguments, those arguments are passed to the
|
||||
L<C<header()>|GT::WWW::http::Header/header> method of the header object. This
|
||||
allows this useful shortcut:
|
||||
|
||||
my $some_header_value = $response->header("Some-Header");
|
||||
|
||||
instead of the alternative (which also works):
|
||||
|
||||
my $some_header_value = $response->header->header("Some-Header");
|
||||
|
||||
Information on header object usage is contained in L<GT::WWW::http::Header>.
|
||||
|
||||
Note that although a header object allows for header manipulation, changing the
|
||||
headers of a response object should be considered bad practise, and is strongly
|
||||
discouraged.
|
||||
|
||||
=head1 CAVEATS
|
||||
|
||||
Although the response object _works_ like a string, keep in mind that it is
|
||||
still an object, and thus a reference. If you intend to pass the data to
|
||||
another subroutine expecting a string, it is recommended that you force the
|
||||
content into string form, either by quoting the variable (C<"$var">) or by
|
||||
calling the content() method (C<$var-E<gt>content>). Not doing so can lead to
|
||||
unexpected results, particularly in cases where another subroutine may
|
||||
differentiate between a string and a reference, and not just use the value as a
|
||||
string.
|
||||
|
||||
Also, in terms of speed, obtaining the content (not the object) into another
|
||||
variable (either via C<"$var"> or C<$var-E<gt>content>) can make quite a
|
||||
substantial difference when several string comparison operations are performed.
|
||||
The reason is simply that every time the object is used is a string, the
|
||||
content method is called, which can amount to a significant slowdown.
|
||||
|
||||
Although string operations that change the string (i.e. s///) appear to work,
|
||||
they in fact clobber the reference and turn your variable into an ordinary
|
||||
string. This should not be done - if the string needs to be modified, take a
|
||||
copy of it first, and modify the copy.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<GT::WWW>
|
||||
L<GT::WWW::http>
|
||||
L<GT::WWW::http::Header>
|
||||
RFC 2616: L<http://www.ietf.org/rfc/rfc2616.txt>
|
||||
|
||||
=head1 MAINTAINER
|
||||
|
||||
Jason Rhinelander
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
http://www.gossamer-threads.com/
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
Revision: $Id: Response.pm,v 1.8 2004/08/04 19:23:07 jagerman Exp $
|
||||
|
||||
=cut
|
63
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/https.pm
Normal file
63
site/slowtwitch.com/cgi-bin/articles/admin/GT/WWW/https.pm
Normal file
@ -0,0 +1,63 @@
|
||||
# ====================================================================
|
||||
# Gossamer Threads Module Library - http://gossamer-threads.com/
|
||||
#
|
||||
# GT::WWW::http
|
||||
# Author: Jason Rhinelander
|
||||
# CVS Info : 087,071,086,086,085
|
||||
# $Id: https.pm,v 1.3 2004/01/13 01:35:20 jagerman Exp $
|
||||
#
|
||||
# Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
# ====================================================================
|
||||
#
|
||||
# Description:
|
||||
# GT::WWW::http subclass to handle HTTPS connections
|
||||
#
|
||||
# This class has only one methods of its own - the default port. Everything
|
||||
# else is inherited directly from GT::WWW::http. It does, however, have the
|
||||
# SSLHandle use, which will err fatally if Net::SSLeay is not installed.
|
||||
|
||||
package GT::WWW::https;
|
||||
use GT::WWW::http;
|
||||
use GT::Socket::Client::SSLHandle;
|
||||
|
||||
@GT::WWW::https::ISA = 'GT::WWW::http';
|
||||
|
||||
sub default_port { 443 }
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
GT::WWW::https - HTTPS handling for GT::WWW
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This module is a simple subclass of GT::WWW::http used by GT::WWW to enable
|
||||
HTTPS access as opposed to HTTP access. Thus GT::WWW::http should be consulted
|
||||
instead of this documentation.
|
||||
|
||||
=head1 REQUIREMENTS
|
||||
|
||||
GT::WWW HTTPS support requires GT::Socket::Client::SSLHandle, which in turn
|
||||
requires the Net::SSLeay library.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<GT::WWW::http>
|
||||
|
||||
=head1 MAINTAINER
|
||||
|
||||
Jason Rhinelander
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved.
|
||||
http://www.gossamer-threads.com/
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
Revision: $Id: https.pm,v 1.3 2004/01/13 01:35:20 jagerman Exp $
|
||||
|
||||
=cut
|
Reference in New Issue
Block a user