/[pub]/suikawiki/script/lib/SuikaWiki/Output/HTTP.pm
Suika

Contents of /suikawiki/script/lib/SuikaWiki/Output/HTTP.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.4 - (hide annotations) (download)
Fri Dec 26 06:48:09 2003 UTC (22 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.3: +23 -3 lines
(output): Don't quote charset parameter value of Content-Type header field body, since legacy browser (such as WinIE 6) does not support quoted-string there

1 wakaba 1.1
2     =head1 NAME
3    
4     SuikaWiki::Output::HTTP --- SuikaWiki: HTTP or HTTP CGI output support
5    
6     =head1 DESCRIPTION
7    
8     This module provides HTTP or HTTP CGI outputing support.
9    
10     This module is part of SuikaWiki.
11    
12     =cut
13    
14     package SuikaWiki::Output::HTTP;
15     use strict;
16 wakaba 1.4 our $VERSION = do{my @r=(q$Revision: 1.3 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
17 wakaba 1.1
18     my %Status = (
19     200 => q(OK),
20 wakaba 1.2 201 => q(Created),
21     202 => q(Accepted),
22     203 => q(Non-Authoritative Information),
23     204 => q(No Content),
24     205 => q(Reset Content),
25     250 => q(Low on Storage Space), # RTSP
26     300 => q(Multiple Choices),
27     301 => q(Moved Permanently),
28 wakaba 1.1 302 => q(Found),
29     303 => q(See Other),
30 wakaba 1.2 304 => q(Not Modified),
31     307 => q(Moved Temporarily),
32     400 => q(Bad Request),
33     401 => q(Unauthorized),
34 wakaba 1.1 403 => q(Forbidden),
35     404 => q(Not Found),
36 wakaba 1.2 405 => q(Method Not Allowed),
37     406 => q(Not Acceptable),
38     408 => q(Request Timeout),
39     409 => q(Conflict),
40     410 => q(Gone),
41     413 => q(Request Entity Too Long),
42     414 => q(Request-URI Too Long),
43     415 => q(Unsupported Media Type),
44     423 => q(Locked),
45     480 => q(Temporariliy Not Available), # SIP
46 wakaba 1.1 500 => q(Internal Server Error),
47 wakaba 1.2 501 => q(Not Implemented),
48     503 => q(Service Unavailable),
49     505 => q(HTTP Version Not Supported),
50     507 => q(Insufficient Storage),
51 wakaba 1.1 );
52 wakaba 1.2 my %Body = (
53     301 => sub {
54     my $self = shift;
55     my $euri = $self->{redirect_uri};
56     $euri =~ s/&/&/g;
57     $euri =~ s/</&lt;/g;
58     $euri =~ s/>/&gt;/g;
59     $euri =~ s/"/&quot;/g;
60     qq(<!DOCTYPE p SYSTEM><p>See &lt;<a href="$euri">$euri</a>&gt;</p>);
61     },
62     );
63     $Body{302} = $Body{301};
64     $Body{303} = $Body{301};
65     $Body{307} = $Body{301};
66 wakaba 1.1
67     =head1 METHODS
68    
69     =over 4
70    
71     =item $http = SuikaWiki::Output::HTTP->new
72    
73     Constructs new instance of HTTP output implementation
74    
75     =cut
76    
77     sub new ($;%) {
78     my $self = bless {header_field => [],
79     negotiate_header_field => {Negotiate => 1},
80     entity => {language => [],
81     media_type => 'application/octet-stream'},
82     }, shift;
83     my %opt = @_;
84 wakaba 1.2 $self->{wiki} = ref $opt{wiki} ? $opt{wiki} : $opt{view}->{wiki};
85 wakaba 1.1 $self->{view} = $opt{view};
86     $self->{viewobj} = $opt{viewobj};
87     $self;
88     }
89    
90     sub add_header_field ($$$;%) {
91     my ($self, $name => $body, %opt) = @_;
92     push @{$self->{header_field}}, {name => $name, body => $body};
93     }
94    
95     sub add_negotiate_header_field ($$) {
96     my ($self, $name) = @_;
97     $self->{negotiate_header_field}->{$name} = 1;
98     }
99    
100     sub set_expires ($%) {
101     my ($self, %opt) = @_;
102     if ($opt{time}) {
103     ## TODO: use rfc1123-time
104     push @{$self->{header_field}}, {name => 'Expires',
105     body => scalar gmtime $opt{time}};
106     } elsif (defined $opt{delta}) {
107     $self->{expires_delta} = $opt{delta} + 0;
108     }
109     }
110    
111 wakaba 1.2 sub set_redirect ($%) {
112     my ($self, %opt) = @_;
113     $self->{redirect_uri} = $opt{uri};
114     if ($opt{status_code}) {
115     $self->{status_code} = $opt{status_code};
116     } else {
117     $self->{status_code} = 302;
118     }
119     $self->{status_phrase} = $opt{status_phrase};
120 wakaba 1.1 }
121    
122     sub set_last_modified ($%) {
123     my ($self, %opt) = @_;
124     $self->{lastmodified_time} = $opt{time};
125     }
126    
127     sub append_to_body ($$) {
128     my ($self, $s) = @_;
129     $self->{entity}->{body} .= $s;
130     }
131    
132     sub output ($;%) {
133     my ($self, %opt) = @_;
134 wakaba 1.4 ## Note: CGI/1.1 draft 3 recommends ("SHOULD") that
135     ## "\x0A" should be used as new line in AmigaDOS and Un*x environments.
136 wakaba 1.2 my $nl = $opt{output} eq 'http-cgi' ? "\n" : "\x0D\x0A";
137 wakaba 1.1 binmode STDOUT;
138     $| = 1;
139 wakaba 1.2
140     my $status_code = $self->{status_code} || 200;
141     my $status_phrase = $self->{status_phrase} || $Status{$status_code};
142     if ((300 < $status_code) && ($status_code < 400)
143     && (defined $self->{redirect_uri})) {
144     print "Location: $self->{redirect_uri}$nl";
145     $status_code = 302
146     if ($status_code == 303 || $status_code == 307)
147     && (($main::ENV{SERVER_PROTOCOL} eq 'HTTP/1.0') ## TODO: use common interface
148     || ($self->{wiki}->{var}->{client}->{user_agent_name}
149     =~ m#M(?:ozilla/[0-4]\.|icrosoft Internet Explorer)#));
150     }
151 wakaba 1.1 if ($opt{output} eq 'http-cgi') {
152 wakaba 1.2 print "Status: $status_code $status_phrase$nl";
153 wakaba 1.1 } else {
154 wakaba 1.2 print "$status_code $status_phrase HTTP/1.1$nl";
155 wakaba 1.1 }
156    
157     for (@{$self->{header_field}}) {
158     print "$_->{name}: $_->{body}$nl";
159     }
160    
161     my $time = time;
162     print "Date: @{[scalar gmtime $time]}$nl";
163     if (defined $self->{expires_delta}) {
164     print "Expires: @{[scalar gmtime ($time + $self->{expires_delta})]}$nl";
165     }
166     if (defined $self->{lastmodified_time}) {
167     print "Last-Modified: @{[scalar gmtime $self->{lastmodified_time}]}$nl";
168     } else {
169     print "Last-Modified: @{[scalar gmtime $time]}$nl";
170     }
171     print "Vary: ".join (', ', keys %{$self->{negotiate_header_field}})."$nl";
172    
173 wakaba 1.2 my $mt = $self->{entity}->{media_type} || 'application/octet-stream';
174     my $charset = $self->{entity}->{charset};
175 wakaba 1.1 my $body = '' . $self->{entity}->{body};
176 wakaba 1.2 unless (length $body) {
177     $mt = q<text/html>;
178     $charset = 'iso-8859-1';
179     $body = &{$Body{$self->{status_code}||200}||sub{}} ($self);
180     }
181 wakaba 1.1 ## TODO:
182     $body = &main::code_convert (\$body, $self->{entity}->{charset});
183    
184 wakaba 1.2 if (($mt eq 'application/rdf+xml')
185     && (index ($self->{wiki}->{var}->{client}->{user_agent_name},
186 wakaba 1.1 'Gecko') > -1)) {
187     print "Content-Type: application/xml";
188     } else {
189 wakaba 1.2 print "Content-Type: $mt";
190 wakaba 1.1 }
191 wakaba 1.2 if ($charset) {
192     if ($self->{wiki}->{var}->{client}->{user_agent_name}
193 wakaba 1.1 =~ m#Mozilla/[12]\.#) {
194     print qq(; charset="@{[{qw/euc-jp x-euc-jp shift_jis x-sjis/}
195 wakaba 1.2 ->{$charset}
196     || $charset]}");
197     } elsif ($self->{wiki}->{var}->{client}->{user_agent_name}
198 wakaba 1.1 =~ m#Mozilla/0\.|Infomosaic#) {
199     } else {
200 wakaba 1.4 print qq(; charset=).$self->___quote_word ($charset);
201 wakaba 1.1 }
202     }
203     print $nl;
204    
205     for (join ', ', @{$self->{entity}->{language}||[]}) {
206     print "Content-Language: $_$nl" if $_;
207     }
208     print "Content-Length: @{[length $body]}$nl";
209     print $nl;
210     print $body;
211     }
212    
213 wakaba 1.4 ## Note: CGI/1.1 draft 3 does not allow HTTP Header Fields
214     ## when Location: field is outputed. This module does
215     ## NOT follow that requirement. The draft allows
216     ## message-body explicitly, even if there is Location:.
217     ## Therefore at least entity-header fields should be allowed.
218    
219     ## TODO: REDIRECT_URI support
220    
221 wakaba 1.3 =item $self->exit
222    
223     Declares the instance ($self) no longer considered useful.
224    
225     =cut
226    
227     sub exit ($) {
228     my $self = shift;
229     delete $self->{wiki};
230     delete $self->{view};
231     delete $self->{viewobj};
232     $self->{exited} = 1;
233     }
234    
235     sub DESTROY ($) {
236     my $self = shift;
237     $self->exit unless $self->{exited};
238     }
239    
240 wakaba 1.4 sub ___quote_word ($$) {
241     my ($self, $s) = @_;
242     if ($s =~ /[^0-9A-Za-z_.+-]/) {
243     $s =~ s/([\\"])/\\$1/g;
244     return qq<"$s">;
245     } else {
246     return $s;
247     }
248     }
249    
250 wakaba 1.1 =item $self->{entity}->{charset}
251     =item $self->{entity}->{language} = [...]
252     =item $self->{entity}->{media_type}
253    
254     =back
255    
256     =head1 TODO
257    
258     Use manakai.
259    
260     =head1 LICENSE
261    
262     Copyright 2003 Wakaba <[email protected]>
263    
264     This program is free software; you can redistribute it and/or
265     modify it under the same terms as Perl itself.
266    
267     =cut
268    
269 wakaba 1.4 1; # $Date: 2003/12/06 05:38:13 $

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24