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