source: extensions/gsdl-video/trunk/installed/cmdline/lib/ruby/1.8/webrick/httpresponse.rb@ 18425

Last change on this file since 18425 was 18425, checked in by davidb, 15 years ago

Video extension to Greenstone

File size: 7.9 KB
Line 
1#
2# httpresponse.rb -- HTTPResponse Class
3#
4# Author: IPR -- Internet Programming with Ruby -- writers
5# Copyright (c) 2000, 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7# reserved.
8#
9# $IPR: httpresponse.rb,v 1.45 2003/07/11 11:02:25 gotoyuzo Exp $
10
11require 'time'
12require 'webrick/httpversion'
13require 'webrick/htmlutils'
14require 'webrick/httputils'
15require 'webrick/httpstatus'
16
17module WEBrick
18 class HTTPResponse
19 BUFSIZE = 1024*4
20
21 attr_reader :http_version, :status, :header
22 attr_reader :cookies
23 attr_accessor :reason_phrase
24 attr_accessor :body
25
26 attr_accessor :request_method, :request_uri, :request_http_version
27 attr_accessor :filename
28 attr_accessor :keep_alive
29 attr_reader :config, :sent_size
30
31 def initialize(config)
32 @config = config
33 @logger = config[:Logger]
34 @header = Hash.new
35 @status = HTTPStatus::RC_OK
36 @reason_phrase = nil
37 @http_version = HTTPVersion::convert(@config[:HTTPVersion])
38 @body = ''
39 @keep_alive = true
40 @cookies = []
41 @request_method = nil
42 @request_uri = nil
43 @request_http_version = @http_version # temporary
44 @chunked = false
45 @filename = nil
46 @sent_size = 0
47 end
48
49 def status_line
50 "HTTP/#@http_version #@status #@reason_phrase #{CRLF}"
51 end
52
53 def status=(status)
54 @status = status
55 @reason_phrase = HTTPStatus::reason_phrase(status)
56 end
57
58 def [](field)
59 @header[field.downcase]
60 end
61
62 def []=(field, value)
63 @header[field.downcase] = value.to_s
64 end
65
66 def content_length
67 if len = self['content-length']
68 return Integer(len)
69 end
70 end
71
72 def content_length=(len)
73 self['content-length'] = len.to_s
74 end
75
76 def content_type
77 self['content-type']
78 end
79
80 def content_type=(type)
81 self['content-type'] = type
82 end
83
84 def each
85 @header.each{|k, v| yield(k, v) }
86 end
87
88 def chunked?
89 @chunked
90 end
91
92 def chunked=(val)
93 @chunked = val ? true : false
94 end
95
96 def keep_alive?
97 @keep_alive
98 end
99
100 def send_response(socket)
101 begin
102 setup_header()
103 send_header(socket)
104 send_body(socket)
105 rescue Errno::EPIPE, Errno::ECONNRESET, Errno::ENOTCONN => ex
106 @logger.debug(ex)
107 @keep_alive = false
108 rescue Exception => ex
109 @logger.error(ex)
110 @keep_alive = false
111 end
112 end
113
114 def setup_header()
115 @reason_phrase ||= HTTPStatus::reason_phrase(@status)
116 @header['server'] ||= @config[:ServerSoftware]
117 @header['date'] ||= Time.now.httpdate
118
119 # HTTP/0.9 features
120 if @request_http_version < "1.0"
121 @http_version = HTTPVersion.new("0.9")
122 @keep_alive = false
123 end
124
125 # HTTP/1.0 features
126 if @request_http_version < "1.1"
127 if chunked?
128 @chunked = false
129 ver = @request_http_version.to_s
130 msg = "chunked is set for an HTTP/#{ver} request. (ignored)"
131 @logger.warn(msg)
132 end
133 end
134
135 # Determin the message length (RFC2616 -- 4.4 Message Length)
136 if @status == 304 || @status == 204 || HTTPStatus::info?(@status)
137 @header.delete('content-length')
138 @body = ""
139 elsif chunked?
140 @header["transfer-encoding"] = "chunked"
141 @header.delete('content-length')
142 elsif %r{^multipart/byteranges} =~ @header['content-type']
143 @header.delete('content-length')
144 elsif @header['content-length'].nil?
145 unless @body.is_a?(IO)
146 @header['content-length'] = @body ? @body.size : 0
147 end
148 end
149
150 # Keep-Alive connection.
151 if @header['connection'] == "close"
152 @keep_alive = false
153 elsif keep_alive?
154 if chunked? || @header['content-length']
155 @header['connection'] = "Keep-Alive"
156 end
157 else
158 @header['connection'] = "close"
159 end
160
161 # Location is a single absoluteURI.
162 if location = @header['location']
163 if @request_uri
164 @header['location'] = @request_uri.merge(location)
165 end
166 end
167 end
168
169 def send_header(socket)
170 if @http_version.major > 0
171 data = status_line()
172 @header.each{|key, value|
173 tmp = key.gsub(/\bwww|^te$|\b\w/){|s| s.upcase }
174 data << "#{tmp}: #{value}" << CRLF
175 }
176 @cookies.each{|cookie|
177 data << "Set-Cookie: " << cookie.to_s << CRLF
178 }
179 data << CRLF
180 _write_data(socket, data)
181 end
182 end
183
184 def send_body(socket)
185 case @body
186 when IO then send_body_io(socket)
187 else send_body_string(socket)
188 end
189 end
190
191 def to_s
192 ret = ""
193 send_response(ret)
194 ret
195 end
196
197 def set_redirect(status, url)
198 @body = "<HTML><A HREF=\"#{url.to_s}\">#{url.to_s}</A>.</HTML>\n"
199 @header['location'] = url.to_s
200 raise status
201 end
202
203 def set_error(ex, backtrace=false)
204 case ex
205 when HTTPStatus::Status
206 @keep_alive = false if HTTPStatus::error?(ex.code)
207 self.status = ex.code
208 else
209 @keep_alive = false
210 self.status = HTTPStatus::RC_INTERNAL_SERVER_ERROR
211 end
212 @header['content-type'] = "text/html"
213
214 if respond_to?(:create_error_page)
215 create_error_page()
216 return
217 end
218
219 if @request_uri
220 host, port = @request_uri.host, @request_uri.port
221 else
222 host, port = @config[:ServerName], @config[:Port]
223 end
224
225 @body = ''
226 @body << <<-_end_of_html_
227<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
228<HTML>
229 <HEAD><TITLE>#{HTMLUtils::escape(@reason_phrase)}</TITLE></HEAD>
230 <BODY>
231 <H1>#{HTMLUtils::escape(@reason_phrase)}</H1>
232 #{HTMLUtils::escape(ex.message)}
233 <HR>
234 _end_of_html_
235
236 if backtrace && $DEBUG
237 @body << "backtrace of `#{HTMLUtils::escape(ex.class.to_s)}' "
238 @body << "#{HTMLUtils::escape(ex.message)}"
239 @body << "<PRE>"
240 ex.backtrace.each{|line| @body << "\t#{line}\n"}
241 @body << "</PRE><HR>"
242 end
243
244 @body << <<-_end_of_html_
245 <ADDRESS>
246 #{HTMLUtils::escape(@config[:ServerSoftware])} at
247 #{host}:#{port}
248 </ADDRESS>
249 </BODY>
250</HTML>
251 _end_of_html_
252 end
253
254 private
255
256 def send_body_io(socket)
257 begin
258 if @request_method == "HEAD"
259 # do nothing
260 elsif chunked?
261 while buf = @body.read(BUFSIZE)
262 next if buf.empty?
263 data = ""
264 data << format("%x", buf.size) << CRLF
265 data << buf << CRLF
266 _write_data(socket, data)
267 @sent_size += buf.size
268 end
269 _write_data(socket, "0#{CRLF}#{CRLF}")
270 else
271 size = @header['content-length'].to_i
272 _send_file(socket, @body, 0, size)
273 @sent_size = size
274 end
275 ensure
276 @body.close
277 end
278 end
279
280 def send_body_string(socket)
281 if @request_method == "HEAD"
282 # do nothing
283 elsif chunked?
284 remain = body ? @body.size : 0
285 while buf = @body[@sent_size, BUFSIZE]
286 break if buf.empty?
287 data = ""
288 data << format("%x", buf.size) << CRLF
289 data << buf << CRLF
290 _write_data(socket, data)
291 @sent_size += buf.size
292 end
293 _write_data(socket, "0#{CRLF}#{CRLF}")
294 else
295 if @body && @body.size > 0
296 _write_data(socket, @body)
297 @sent_size = @body.size
298 end
299 end
300 end
301
302 def _send_file(output, input, offset, size)
303 while offset > 0
304 sz = BUFSIZE < offset ? BUFSIZE : offset
305 buf = input.read(sz)
306 offset -= buf.size
307 end
308
309 if size == 0
310 while buf = input.read(BUFSIZE)
311 _write_data(output, buf)
312 end
313 else
314 while size > 0
315 sz = BUFSIZE < size ? BUFSIZE : size
316 buf = input.read(sz)
317 _write_data(output, buf)
318 size -= buf.size
319 end
320 end
321 end
322
323 def _write_data(socket, data)
324 socket << data
325 end
326 end
327end
Note: See TracBrowser for help on using the repository browser.