/[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.6 - (show annotations) (download)
Sun Feb 8 08:55:45 2004 UTC (22 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +7 -5 lines
Binary output and empty output supported

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 our $VERSION = do{my @r=(q$Revision: 1.5 $=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
17 require IO::Handle;
18
19 my %Status = (
20 200 => q(OK),
21 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 302 => q(Found),
30 303 => q(See Other),
31 304 => q(Not Modified),
32 307 => q(Moved Temporarily),
33 400 => q(Bad Request),
34 401 => q(Unauthorized),
35 403 => q(Forbidden),
36 404 => q(Not Found),
37 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 500 => q(Internal Server Error),
48 501 => q(Not Implemented),
49 503 => q(Service Unavailable),
50 505 => q(HTTP Version Not Supported),
51 507 => q(Insufficient Storage),
52 );
53 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
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 $self->{wiki} = ref $opt{wiki} ? $opt{wiki} : $opt{view}->{wiki};
86 $self->{view} = $opt{view};
87 $self->{viewobj} = $opt{viewobj};
88 $self->{-out_handle} = $opt{output_handle} || *STDOUT;
89 $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 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 }
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 my $dg = $self->{wiki}->{var}->{client}->{downgrade};
137 ## Note: CGI/1.1 draft 3 recommends ("SHOULD") that
138 ## "\x0A" should be used as new line in AmigaDOS and Un*x environments.
139 my $nl = $opt{output} eq 'http-cgi' ? "\n" : "\x0D\x0A";
140 my $out = $opt{output_handle} || $self->{-out_handle};
141 binmode $out;
142 $out->autoflush (1);
143
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 print $out "Location: $self->{redirect_uri}$nl";
149 $status_code = 302
150 if
151 ($status_code == 303 or $status_code == 307)
152 and ( ( $self->{wiki}->{input}->meta_variable ('SERVER_PROTOCOL')
153 eq 'HTTP/1.0')
154 or $dg->{http_no_see_other});
155 }
156 if ($opt{output} eq 'http-cgi') {
157 print $out "Status: $status_code $status_phrase$nl";
158 } else {
159 print $out "$status_code $status_phrase HTTP/1.1$nl";
160 }
161
162 for (@{$self->{header_field}}) {
163 print $out "$_->{name}: $_->{body}$nl";
164 }
165
166 my $time = time;
167 print $out "Date: @{[scalar gmtime $time]}$nl";
168 if (defined $self->{expires_delta}) {
169 print $out "Expires: @{[scalar gmtime ($time + $self->{expires_delta})]}$nl";
170 }
171 if (defined $self->{lastmodified_time}) {
172 print $out "Last-Modified: @{[scalar gmtime $self->{lastmodified_time}]}$nl";
173 } else {
174 print $out "Last-Modified: @{[scalar gmtime $time]}$nl";
175 }
176 print $out "Vary: ".join (', ', keys %{$self->{negotiate_header_field}})."$nl";
177
178 my $mt = $self->{entity}->{media_type} || 'application/octet-stream';
179 my $charset = $self->{entity}->{charset};
180 my $body = '' . $self->{entity}->{body};
181 if (not length $body and $self->{status_code} != 200) {
182 $mt = q<text/html>;
183 $charset = 'iso-8859-1';
184 $body = ($Body{$self->{status_code}} or sub{})->($self);
185 }
186 ## TODO:
187 $body = &main::code_convert (\$body, $self->{entity}->{charset},
188 $self->{wiki}->{config}->{charset}->{internal})
189 unless $self->{entity}->{body_is_octet_stream};
190
191 if (substr ($mt, -4) eq '+xml') {
192 if ($mt eq 'application/rdf+xml' and $dg->{media_type_no_rdf_plus_xml}) {
193 print $out "Content-Type: application/xml";
194 } elsif ($dg->{media_type_no_plus_xml}) {
195 print $out "Content-Type: application/xml";
196 } elsif ($dg->{media_type_no_xml}) {
197 print $out "Content-Type: text/plain";
198 } else {
199 print $out "Content-Type: $mt";
200 }
201 } else {
202 print $out "Content-Type: $mt";
203 }
204 if ($charset) {
205 if ($dg->{media_type_no_parameter}) {
206
207 } elsif ($dg->{charset_name_with_x}) {
208 print $out qq(; charset="@{[{qw/euc-jp x-euc-jp shift_jis x-sjis/}
209 ->{$charset} or $charset]}");
210 } else {
211 print $out qq(; charset=).$self->___quote_word ($charset);
212 }
213 }
214 print $nl;
215
216 for (join ', ', @{$self->{entity}->{language}||[]}) {
217 print $out "Content-Language: $_$nl" if $_;
218 }
219 print $out "Content-Length: @{[length $body]}$nl";
220 print $out $nl;
221 print $out $body;
222 }
223
224 ## Note: CGI/1.1 draft 3 does not allow HTTP Header Fields
225 ## when Location: field is outputed. This module does
226 ## NOT follow that requirement. The draft allows
227 ## message-body explicitly, even if there is Location:.
228 ## Therefore at least entity-header fields should be allowed.
229
230 ## TODO: REDIRECT_URI support
231
232 =item $self->exit
233
234 Declares the instance ($self) no longer considered useful.
235
236 =cut
237
238 sub exit ($) {
239 my $self = shift;
240 delete $self->{wiki};
241 delete $self->{view};
242 delete $self->{viewobj};
243 $self->{exited} = 1;
244 }
245
246 sub DESTROY ($) {
247 my $self = shift;
248 $self->exit unless $self->{exited};
249 }
250
251 sub ___quote_word ($$) {
252 my ($self, $s) = @_;
253 if ($s =~ /[^0-9A-Za-z_.+-]/) {
254 $s =~ s/([\\"])/\\$1/g;
255 return qq<"$s">;
256 } else {
257 return $s;
258 }
259 }
260
261 =item $self->{entity}->{charset}
262 =item $self->{entity}->{language} = [...]
263 =item $self->{entity}->{media_type}
264
265 =back
266
267 =head1 TODO
268
269 Use manakai.
270
271 =head1 LICENSE
272
273 Copyright 2003 Wakaba <w@suika.fam.cx>
274
275 This program is free software; you can redistribute it and/or
276 modify it under the same terms as Perl itself.
277
278 =cut
279
280 1; # $Date: 2004/01/16 08:03:39 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24