~ubuntu-branches/ubuntu/hardy/ruby1.8/hardy-updates

« back to all changes in this revision

Viewing changes to lib/webrick/httpresponse.rb

  • Committer: Bazaar Package Importer
  • Author(s): akira yamada
  • Date: 2007-03-13 22:11:58 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20070313221158-h3oql37brlaf2go2
Tags: 1.8.6-1
* new upstream version, 1.8.6.
* libruby1.8 conflicts with libopenssl-ruby1.8 (< 1.8.6) (closes: #410018)
* changed packaging style to cdbs from dbs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
11
require 'time'
 
12
require 'webrick/httpversion'
 
13
require 'webrick/htmlutils'
 
14
require 'webrick/httputils'
 
15
require 'webrick/httpstatus'
 
16
 
 
17
module 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
 
327
end