/[suikacvs]/messaging/manakai/lib/Message/CGI/HTTP.pm
Suika

Contents of /messaging/manakai/lib/Message/CGI/HTTP.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show annotations) (download)
Mon Nov 10 05:30:59 2008 UTC (16 years ago) by wakaba
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +3 -2 lines
++ manakai/lib/Message/CGI/ChangeLog	10 Nov 2008 05:30:33 -0000
2008-11-10  Wakaba  <wakaba@suika.fam.cx>

	* HTTP.pm (request_uri): Percent-escape non-ASCII octets in
	REQUEST_URI to avoid they become unclear whether they are bytes or
	characters in later processing.

++ manakai/lib/Message/URI/ChangeLog	10 Nov 2008 05:30:51 -0000
2008-11-10  Wakaba  <wakaba@suika.fam.cx>

	* URIReference.pm (is_relative_iri_reference_3987): Escapes in
	$ucschar was not expanded.

1 =head1 NAME
2
3 Message::CGI::HTTP - An Object-Oriented HTTP CGI Interface
4
5 =head1 DESCRIPTION
6
7 The C<Message::CGI::HTTP> module provides an object-oriented
8 interface for inputs and outputs as defined by CGI specification.
9
10 This module is part of manakai.
11
12 =cut
13
14 package Message::CGI::HTTP;
15 use strict;
16 our $VERSION = do{my @r=(q$Revision: 1.5 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
17 push our @ISA, 'Message::IF::CGIRequest', 'Message::IF::HTTPCGIRequest';
18
19 =head1 METHODS
20
21 =over 4
22
23 =item I<$cgi> = Message::CGI::HTTP->new;
24
25 Creates and returns a new instance of HTTP CGI interface object.
26
27 =cut
28
29 sub new ($;%) {
30 my $self = bless {
31 decoder => {
32 '#default' => sub {$_[1]},
33 },
34 }, shift;
35 my %opt = @_;
36 $self->{-in_handle} = *main::STDIN;
37 $self;
38 } # new
39
40 =item I<$value> = I<$cgi>->get_meta_variable (I<$name>)
41
42 Returns the value of the meta-variable I<$name>. The name
43 specified by the I<$name> SHOULD be a meta-variable name
44 defined by a CGI specification, e.g. C<CONTENT_TYPE> or
45 C<HTTP_USER_AGENT>. Otherwise, the result is implementation
46 dependent. In an environment where meta-variables are supplied
47 as envirnoment variables, specifying an environment variable
48 that is not a meta-variable, such as C<PATH>, results in the
49 value of that environment variable. However, CGI scripts
50 SHOULD NOT depend on such behavior.
51
52 This method might return C<undef> when the meta-variable
53 is not defined or is defined but its value is C<undef>.
54
55 =cut
56
57 sub get_meta_variable ($$) {
58 return $main::ENV{ $_[1] };
59 } # get_meta_variable
60
61 =item I<$list> = I<$cgi>->meta_variable_names;
62
63 Returns list of meta variables. Note that this list might contain
64 other environmental variables than CGI meta variables, since
65 they cannot distinglish unless we know what is CGI meta variable
66 and what is not. Unfortunately, there is no complete list of CGI
67 meta variables, whilst list of standarized meta variables is available.
68
69 NOTE: Some application might use an environmental variable named
70 'HTTP_HOME', which might make some confusion with CGI meta variable
71 for HTTP 'Home:' header field. Fortunately, such name of HTTP
72 header field is not intoroduced as far as I know.
73
74 This method returns a C<Message::DOM::DOMStringList>.
75
76 =cut
77
78 sub meta_variable_names ($) {
79 require Message::DOM::DOMStringList;
80 bless [keys %main::ENV], 'Message::DOM::DOMStringList::StaticList';
81 } # meta_variable_names
82
83 =item I<$value> = C<$cgi>->get_parameter ($name);
84
85 Returns parameter value if any.
86 Parameter value is set by query-string of Request-URI
87 and/or entity-body value.
88
89 When multiple values with same parameter name is specified,
90 the first one is returned in scalar context or
91 an array reference of all values is returned in array context.
92 (Note that query-string is "earlier" than entity-body.)
93
94 =cut
95
96 sub get_parameter ($$) {
97 my ($self, $name) = @_;
98 $self->__get_parameter unless $self->{param};
99
100 if (wantarray) {
101 return @{$self->{param}->{$name}||[]};
102 } else {
103 return ${$self->{param}->{$name}||[]}[0];
104 }
105 } # get_parameter
106
107 =item I<$keys> = I<$cgi>->parameter_names;
108
109 Returnes a list of parameter names provided.
110
111 This method returns a C<Message::DOM::DOMStringList>.
112
113 =cut
114
115 sub parameter_names ($) {
116 my $self = shift;
117 $self->__get_parameter unless $self->{param};
118
119 require Message::DOM::DOMStringList;
120 return bless [keys %{$self->{param}}],
121 'Message::DOM::DOMStringList::StaticList';
122 } # parameter_names
123
124 sub __get_parameter ($) {
125 my $self = shift;
126 my @src;
127
128 ## Query-string of Request-URI
129 my $qs = $self->get_meta_variable ('QUERY_STRING');
130 push @src, $qs if (index ($qs, '=') > -1);
131
132 ## Entity-body
133 if ($self->get_meta_variable ('REQUEST_METHOD') eq 'POST') {
134 my $mt = $self->get_meta_variable ('CONTENT_TYPE');
135 ## TODO: Uppercase
136 if ($mt =~ m<^application/(?:x-www|sgml)-form-urlencoded\b>) {
137 push @src, $self->entity_body;
138 }
139 ## TODO: support non-standard "charset" parameter
140 }
141
142 my %temp_params;
143 for my $src (@src) {
144 for (split /[;&]/, $src) {
145 my ($name, $val) = split '=', $_, 2;
146 for ($name, $val) {
147 tr/+/ /;
148 s/%([0-9A-Fa-f][0-9A-Fa-f])/pack 'C', hex $1/ge;
149 }
150 $temp_params{$name} ||= [];
151 push @{$temp_params{$name}}, $val;
152 }
153 }
154 for (keys %temp_params) {
155 my $name = &{$self->{decoder}->{'#name'}
156 ||$self->{decoder}->{'#default'}} ($self, $_, \%temp_params);
157 for (@{$temp_params{$name}}) {
158 push @{$self->{param}->{$name}},
159 &{$self->{decoder}->{$name}
160 ||$self->{decoder}->{'#default'}} ($self, $_, \%temp_params);
161 }
162 }
163 } # __get_parameter
164
165 =item I<$body> = I<$cgi>->entity_body;
166
167 Returns entity-body content if any.
168
169 =cut
170
171 sub entity_body ($) {
172 my $self = shift;
173 $self->__get_entity_body unless defined $self->{body};
174
175 return $self->{body};
176 } # entity_body
177
178 sub __get_entity_body ($) {
179 my $self = shift;
180 binmode $self->{-in_handle};
181 read $self->{-in_handle}, $self->{body},
182 $self->get_meta_variable ('CONTENT_LENGTH');
183 } # __get_entity_body
184 ## TODO: Entity too large
185
186 =item I<$uri> = I<$cgi>->request_uri;
187
188 Returns Request-URI as a C<Message::URI::URIReference> object.
189
190 Note that stringified value of returned value might not be same as the
191 URI specified as the Request-URI of HTTP request or (possibly pseudo-)
192 URI entered by the user, since no standarized way to get it is
193 defined by HTTP and CGI/1.1 specifications.
194
195 =cut
196
197 sub request_uri ($;%) {
198 my ($self, %opt) = @_;
199 require Message::URI::URIReference;
200 my $uri = $opt{no_path_info} ? undef
201 : $self->get_meta_variable ('REQUEST_URI'); # non-standard
202 if ($uri) {
203 $uri =~ s/\#[^#]*$//; ## Fragment identifier not allowed here
204 $uri =~ s/\?[^?]*$// if $opt{no_query};
205 $uri = $self->__uri_encode ($uri, qr([^\x00-\x7F]));
206 if ($uri =~ /^[0-9A-Za-z.%+-]+:/) { ## REQUEST_URI is an absolute URI
207 return Message::DOM::DOMImplementation->create_uri_reference ($uri);
208 }
209 } else { ## REQUEST_URI is not provided
210 my $pi = $opt{no_path_info} ? q<>
211 : $self->get_meta_variable ('PATH_INFO');
212 $uri = $self->__uri_encode ($self->get_meta_variable ('SCRIPT_NAME').$pi,
213 qr([^0-9A-Za-z_.!~*'();/:\@&=\$,-]));
214 my $qs = $self->get_meta_variable ('QUERY_STRING');
215 $uri .= '?' . $qs if not $opt{no_query} and defined $qs;
216 }
217
218 ## REQUEST_URI is a relative URI or
219 ## REQUEST_URI is not provided
220 my $scheme = 'http';
221 my $port = ':' . $self->get_meta_variable ('SERVER_PORT');
222 ## TODO: HTTPS=off
223 if ( $self->get_meta_variable ('HTTPS')
224 || $self->get_meta_variable ('CERT_SUBJECT')
225 || $self->get_meta_variable ('SSL_VERSION')) {
226 $scheme = 'https';
227 $port = '' if $port eq ':443';
228 } else {
229 $port = '' if $port eq ':80';
230 }
231
232 my $host_and_port = $self->get_meta_variable ('HTTP_HOST');
233 if ($host_and_port) {
234 $uri = $scheme . '://'
235 . $self->__uri_encode ($host_and_port, qr/[^0-9A-Za-z.:-]/)
236 . $uri; ## ISSUE: Should we allow "[" / "]" for IPv6 here?
237 } else {
238 $uri = $scheme . '://'
239 . $self->__uri_encode ($self->get_meta_variable ('SERVER_NAME'),
240 qr/[^0-9A-Za-z.-]/)
241 . $port . $uri;
242 }
243 return Message::DOM::DOMImplementation->create_uri_reference ($uri);
244 } # request_uri
245
246 sub __uri_encode ($$;$) {
247 my ($self, $s, $char) = @_;
248 $char ||= qr([^0-9A-Za-z_.!~*'();/?:\@&=+\$,-]);
249 require Encode;
250 $s = Encode::decode ('utf8', $s);
251 $s =~ s/($char)/sprintf '%%%02X', ord $1/ge;
252 return $s;
253 } # __uri_encode
254
255 =item I<$value> = I<$cgi>->path_info ([I<$new_value>]);
256
257 =item I<$value> = I<$cgi>->remote_user ([I<$new_value>]);
258
259 These methods reflect meta-variables with the same name (in
260 uppercase).
261
262 =cut
263
264 for (
265 [path_info => 'PATH_INFO'],
266 [query_string => 'QUERY_STRING'],
267 [remote_user => 'REMOTE_USER'],
268 [request_method => 'REQUEST_METHOD'],
269 [script_name => 'SCRIPT_NAME'],
270 ) {
271 eval qq{
272 sub $_->[0] (\$;\$) {
273 if (\@_ > 1) {
274 if (defined \$_[1]) {
275 \$main::ENV{'$_->[1]'} = ''.\$_[1];
276 } else {
277 delete \$main::ENV{'$_->[1]'};
278 }
279 }
280 return \$main::ENV{'$_->[1]'};
281 }
282 };
283 }
284
285 package Message::IF::CGIRequest;
286 package Message::IF::HTTPCGIRequest;
287
288 =back
289
290 =head1 TODO
291
292 =over 4
293
294 =item multipart/form-data support
295
296 =back
297
298 =head1 SEE ALSO
299
300 A draft specification for DOM CGI Module
301 <http://suika.fam.cx/gate/2005/sw/manakai/%E3%83%A1%E3%83%A2/2005-07-04>
302 (This module does not implement the interface defined in this
303 specification, however.)
304
305 =head1 AUTHOR
306
307 Wakaba <w@suika.fam.cx>
308
309 This module was originally developed as part of SuikaWiki.
310
311 =head1 LICENSE
312
313 Copyright 2003, 2007 Wakaba <w@suika.fam.cx>
314
315 This program is free software; you can redistribute it and/or
316 modify it under the same terms as Perl itself.
317
318 =cut
319
320 1;
321 # $Date: 2008/11/09 14:06:23 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24