1#
2# cgihandler.rb -- CGIHandler Class
3#
4# Author: IPR -- Internet Programming with Ruby -- writers
5# Copyright (c) 2001 TAKAHASHI Masayoshi, GOTOU Yuuzou
6# Copyright (c) 2002 Internet Programming with Ruby writers. All rights
7# reserved.
8#
9# $IPR: cgihandler.rb,v 1.27 2003/03/21 19:56:01 gotoyuzo Exp $
10
11require 'rbconfig'
12require 'tempfile'
13require 'webrick/config'
14require 'webrick/httpservlet/abstract'
15
16module WEBrick
17  module HTTPServlet
18
19    ##
20    # Servlet for handling CGI scripts
21    #
22    # Example:
23    #
24    #  server.mount('/cgi/my_script', WEBrick::HTTPServlet::CGIHandler,
25    #               '/path/to/my_script')
26
27    class CGIHandler < AbstractServlet
28      Ruby = RbConfig.ruby # :nodoc:
29      CGIRunner = "\"#{Ruby}\" \"#{WEBrick::Config::LIBDIR}/httpservlet/cgi_runner.rb\"" # :nodoc:
30
31      ##
32      # Creates a new CGI script servlet for the script at +name+
33
34      def initialize(server, name)
35        super(server, name)
36        @script_filename = name
37        @tempdir = server[:TempDir]
38        @cgicmd = "#{CGIRunner} #{server[:CGIInterpreter]}"
39      end
40
41      # :stopdoc:
42
43      def do_GET(req, res)
44        data = nil
45        status = -1
46
47        cgi_in = IO::popen(@cgicmd, "wb")
48        cgi_out = Tempfile.new("webrick.cgiout.", @tempdir, mode: IO::BINARY)
49        cgi_out.set_encoding("ASCII-8BIT")
50        cgi_err = Tempfile.new("webrick.cgierr.", @tempdir, mode: IO::BINARY)
51        cgi_err.set_encoding("ASCII-8BIT")
52        begin
53          cgi_in.sync = true
54          meta = req.meta_vars
55          meta["SCRIPT_FILENAME"] = @script_filename
56          meta["PATH"] = @config[:CGIPathEnv]
57          if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
58            meta["SystemRoot"] = ENV["SystemRoot"]
59          end
60          dump = Marshal.dump(meta)
61
62          cgi_in.write("%8d" % cgi_out.path.bytesize)
63          cgi_in.write(cgi_out.path)
64          cgi_in.write("%8d" % cgi_err.path.bytesize)
65          cgi_in.write(cgi_err.path)
66          cgi_in.write("%8d" % dump.bytesize)
67          cgi_in.write(dump)
68
69          if req.body and req.body.bytesize > 0
70            cgi_in.write(req.body)
71          end
72        ensure
73          cgi_in.close
74          status = $?.exitstatus
75          sleep 0.1 if /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
76          data = cgi_out.read
77          cgi_out.close(true)
78          if errmsg = cgi_err.read
79            if errmsg.bytesize > 0
80              @logger.error("CGIHandler: #{@script_filename}:\n" + errmsg)
81            end
82          end
83          cgi_err.close(true)
84        end
85
86        if status != 0
87          @logger.error("CGIHandler: #{@script_filename} exit with #{status}")
88        end
89
90        data = "" unless data
91        raw_header, body = data.split(/^[\xd\xa]+/, 2)
92        raise HTTPStatus::InternalServerError,
93          "Premature end of script headers: #{@script_filename}" if body.nil?
94
95        begin
96          header = HTTPUtils::parse_header(raw_header)
97          if /^(\d+)/ =~ header['status'][0]
98            res.status = $1.to_i
99            header.delete('status')
100          end
101          if header.has_key?('location')
102            # RFC 3875 6.2.3, 6.2.4
103            res.status = 302 unless (300...400) === res.status
104          end
105          if header.has_key?('set-cookie')
106            header['set-cookie'].each{|k|
107              res.cookies << Cookie.parse_set_cookie(k)
108            }
109            header.delete('set-cookie')
110          end
111          header.each{|key, val| res[key] = val.join(", ") }
112        rescue => ex
113          raise HTTPStatus::InternalServerError, ex.message
114        end
115        res.body = body
116      end
117      alias do_POST do_GET
118
119      # :startdoc:
120    end
121
122  end
123end
124