Class: SCGI::SCGIProcessor

Inherits:
Monitor
  • Object
show all
Defined in:
lib/nitro/adapter/scgi.rb

Overview

:nodoc: all

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(server, settings = {}) ⇒ SCGIProcessor



43
44
45
46
47
48
49
50
51
# File 'lib/nitro/adapter/scgi.rb', line 43

def initialize(server, settings = {})
    @conns = 0
    @shutdown = false
    @dead = false
      @server = server
    super()

    configure(settings)
end

Instance Attribute Details

#settingsObject (readonly)

Returns the value of attribute settings.



41
42
43
# File 'lib/nitro/adapter/scgi.rb', line 41

def settings
  @settings
end

Instance Method Details

#configure(settings) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/nitro/adapter/scgi.rb', line 53

def configure(settings)
    @settings = settings
    #@log = LogFactory.instance.create(settings[:logfile])
    @log = Logger
      @log = Logger.new(settings[:logfile]) if settings[:logfile] 

    @maxconns = settings[:maxconns]
    @started = Time.now
    
    if settings[:socket]
        @socket = settings[:socket]
    else
        @host = settings[:host]
        @port = settings[:port]
    end
    
    @throttle_sleep = 1.0/settings[:conns_second] if settings[:conns_second]
end

#handle_client(socket) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/nitro/adapter/scgi.rb', line 93

def handle_client(socket)
    Thread.new do
        begin
            # remember if we were doing a shutdown so we can avoid increment later
            in_shutdown = @shutdown
            synchronize { @conns += 1 if not in_shutdown }
        
            len = ""
            # we only read 10 bytes of the length.  any request longer than this is invalid
            while len.length <= 10
                c = socket.read(1)
                if c == ':'
                    # found the terminal, len now has a length in it so read the payload
                    break
                else
                    len << c
                end
            end
        
            # we should now either have a payload length to get
            payload = socket.read(len.to_i)
            if (c = socket.read(1)) != ','
                @log.warn("SCGI: Malformed request, does not end with ','")
            else
                read_header(socket, payload, @conns)
            end
        rescue IOError
            @log.warn("SCGI: received IOError #$! when handling client.  Your web server doesn't like me.")
        rescue Object
            @log.warn("SCGI: after accepting client #@host:#@port -- #{$!.class} #$! #{$!.backtrace.join("\n")}")
        ensure
            synchronize { @conns -= 1 if not in_shutdown}
            socket.close if not socket.closed?
        end
    end
end

#listenObject



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/nitro/adapter/scgi.rb', line 72

def listen
    @socket = TCPServer.new(@host, @port)
    
    begin
        while true
            handle_client(@socket.accept)
            sleep @throttle_sleep if @throttle_sleep
            break if @shutdown and @conns <= 0
        end
    rescue Interrupt
        @log.info("SCGI: Shutting down from SIGINT.")
    rescue Object
        @log.warn("SCGI: while listening for connections on #@host:#@port -- #{$!.class} #$! #{$!.backtrace.join("\n")}" )
    end
    
    @socket.close if not @socket.closed?
    @dead = true
    @log.info("SCGI: Exited accept loop. Shutdown complete.")
end

#process_request(request, body, socket) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/nitro/adapter/scgi.rb', line 154

def process_request(request, body, socket)
    return if socket.closed?
    cgi = SCGIFixed.new(request, body, socket)
    begin
  #--
  # TODO: remove sync, Nitro *is* thread safe!
  #++
  # guill: and why not ? ;)
  #synchronize do
      #--
      # FIXME: this is uggly, something better?
      #++
      cgi.stdinput.rewind
      cgi.env["QUERY_STRING"] = (cgi.env["REQUEST_URI"] =~ /^[^?]+\?(.+)$/ and $1).to_s
      Nitro::Cgi.process(@server, cgi, cgi.stdinput, cgi.stdoutput)
  #end
    ensure
      Og.manager.put_store if defined?(Og) and Og.respond_to?(:manager) and Og.manager
    end
end

#read_header(socket, payload, conns) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/nitro/adapter/scgi.rb', line 131

def read_header(socket, payload, conns)
    return if socket.closed?
    request = split_body(payload)
    if request["CONTENT_LENGTH"]
        length = request["CONTENT_LENGTH"].to_i
        if length > 0
            body = socket.read(length)
        else
            body = ""
        end

        if @shutdown or @conns > @maxconns
            socket.write("Location: /busy.html\r\n")
            socket.write("Cache-control: no-cache, must-revalidate\r\n")
            socket.write("Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n")
            socket.write("Status: 307 Temporary Redirect\r\n\r\n")
        else
            process_request(request, body, socket)
        end
    end
end

#shutdown(force = false) ⇒ Object

Graceful shutdown is done by setting the maxconns to 0 so that all new requests get the 503 Service Unavailable status. The handler code checks if @shutdown is set and if so it will not increase the @conns count, but it will decrease. Once the @conns count is down to 0 it will exit the loop.



202
203
204
205
206
207
208
209
210
211
# File 'lib/nitro/adapter/scgi.rb', line 202

def shutdown(force = false)
    @shutdown = true;

    if force
        @socket.close
        @log.info("SCGI: FORCED shutdown requested.  Oh well.")
    else
        @log.info("SCGI: Shutdown requested.  Beginning graceful shutdown with #@conns connected.")
    end
end

#split_body(data) ⇒ Object



175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/nitro/adapter/scgi.rb', line 175

def split_body(data)
    result = {}
    el = data.split("\0")
    i = 0
    len = el.length
    while i < len
        result[el[i]] = el[i+1]
        i += 2
    end
    
    return result
end

#statusObject



189
190
191
192
193
194
195
196
# File 'lib/nitro/adapter/scgi.rb', line 189

def status
    { 
    :time => Time.now,  :pid => Process.pid, :settings => @settings,
    :env => @settings[:env], :started => @started,
    :max_conns => @maxconns, :conns => @conns, :systimes => Process.times,
    :conns_second => @throttle, :shutdown => @shutdown, :dead => @dead
    }
end