/[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.7 - (hide annotations) (download)
Fri Mar 19 11:24:17 2004 UTC (22 years, 3 months ago) by wakaba
Branch: MAIN
CVS Tags: suikawiki3-redirect, release-3-0-0, HEAD
Branch point for: paragraph-200404, helowiki, helowiki-2005
Changes since 1.6: +4 -3 lines
(output): Don't check SERVER_PROTOCOL when lack of wiki->input

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24