Class: JSCommander::ProxyServer

Inherits:
WEBrick::AsyncHTTPProxyServer show all
Defined in:
lib/jscmd/proxyserver.rb

Defined Under Namespace

Classes: CommandDispatcher

Constant Summary collapse

SCRIPT_DIR =
"/_remote_js_proxy/"

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from WEBrick::AsyncHTTPProxyServer

#proxy_service

Constructor Details

#initialize(broker, args = {}) ⇒ ProxyServer

Returns a new instance of ProxyServer.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/jscmd/proxyserver.rb', line 137

def initialize(broker, args = {})
  @broker = broker
  @commands = Queue.new
  @command_dispatcher = CommandDispatcher.new(self, @commands)
  @broker.subscribe("commands") do |msg|
    if msg.attributes["type"] == "ping"
      @broker.send("events", Message.new(nil, 
                                         :type => "pong", 
                                         :clients => @command_dispatcher.format_clients,
                                         "in-reply-to" => msg.attributes["id"]))
    else
      @commands.push(msg)
    end
  end
  @clients = []
  options = args[:disable_proxy_injection] ? args : {:ProxyContentHandler => method(:handle_content).to_proc}.merge(args)
  @trace_request = args[:trace_request]
  @requests = 0
  @reconnect_interval = args[:reconnect_interval]
  super(options)
end

Instance Attribute Details

#reconnect_intervalObject (readonly)

Returns the value of attribute reconnect_interval.



30
31
32
# File 'lib/jscmd/proxyserver.rb', line 30

def reconnect_interval
  @reconnect_interval
end

Instance Method Details

#do_serviceObject



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/jscmd/proxyserver.rb', line 183

def service(req, res)
  if req.path == "#{SCRIPT_DIR}agent.js"
    res.content_type = "application/x-javascript"
    res.body = File.read(File.join(File.dirname(__FILE__), "agent.js"))
  elsif req.path == "#{SCRIPT_DIR}poll"
    serve_script(req, res)
  else
    if req.header["accept-encoding"] && !req.header["accept-encoding"].empty?
      if req.header["accept-encoding"].first.split(/,/).include?("gzip")
        req.header["accept-encoding"] = ["gzip"]
      else
        req.header.delete "accept-encoding"
      end
    end
    super
  end
end

#handle_content(req, res) ⇒ Object



244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/jscmd/proxyserver.rb', line 244

def handle_content(req, res)
  # $stderr.puts "handle_content:type=#{res.content_type}, status=#{res.status}, encoding=#{res.header["content-encoding"]}"
  if res.content_type =~ %r{^text/html} && res.status == 200
    res.flush_body
    # we cannot always trust content_type, so check if the content looks like html
    body = res.body
    if res.header["content-encoding"] == "gzip"
      body = Zlib::GzipReader.new(StringIO.new(body)).read
    end
    if body =~ /^\s*</
      body = body.dup
      # puts "injecting javascript"
      script_tag = '<script type="text/javascript" src="/_remote_js_proxy/agent.js"></script>'
      unless body.sub!(%r{<head( .*?)?>}i){|s|s+script_tag}
        body = script_tag + body
      end
      if res.header["content-encoding"] == "gzip"
        io = StringIO.new
        writer = Zlib::GzipWriter.new(io)
        writer.write(body)
        writer.close
        res.body = io.string
      else
        res.body = body
      end
      res.content_length = res.body.size if res.content_length
    end
  end
end

#serve_script(req, res) ⇒ Object



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/jscmd/proxyserver.rb', line 201

def serve_script(req, res)
  # puts "serve:#{req.body}"
  begin
    @command_dispatcher.new_client(req) do |client|
      type = req.header["x-jscmd-type"].first
      if type && type != "connect"
        @broker.send("events", Message.new(URI.decode(req.body || ""), 
                                           :type => type,
                                           :clients => @command_dispatcher.format_clients,
                                           "in-reply-to" => req.header["x-jscmd-in-reply-to"].first))
        if "true" == req.header["x-jscmd-more"].first
          # immediately send empty response to wait for another event
          res.content_type = "text/plain"
          res.body = ''
          client.abort
          return
        end
      else
        # new connection
        if @command_dispatcher.last_clients != @command_dispatcher.format_clients
          @broker.send("events", Message.new(nil, :type => "connect",
                                             :clients => @command_dispatcher.format_clients))
        end
      end
      
      command = client.pop_command
      if command.is_a?(Class) # exception class
        raise command
      end
      res.content_type = "text/plain"
      res.header["X-JSCmd-Command-Id"] = command.attributes["id"]
      res.header["X-JSCmd-Type"] = command.attributes["type"]
      res.body = command.body
    end
  rescue CommandDispatcher::CLIENT_ABORTED
    @broker.send("events", Message.new(nil, :type => "abort",
                                       :clients => @command_dispatcher.format_clients))
    raise WEBrick::HTTPStatus::EOFError
  rescue CommandDispatcher::SHUTDOWN
    raise WEBrick::HTTPStatus::EOFError
  end
end

#service(req, res) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/jscmd/proxyserver.rb', line 165

def service(req, res)
  if req.path == "#{SCRIPT_DIR}agent.js"
    res.content_type = "application/x-javascript"
    res.body = File.read(File.join(File.dirname(__FILE__), "agent.js"))
  elsif req.path == "#{SCRIPT_DIR}poll"
    serve_script(req, res)
  else
    if req.header["accept-encoding"] && !req.header["accept-encoding"].empty?
      if req.header["accept-encoding"].first.split(/,/).include?("gzip")
        req.header["accept-encoding"] = ["gzip"]
      else
        req.header.delete "accept-encoding"
      end
    end
    super
  end
end

#shutdownObject



196
197
198
199
# File 'lib/jscmd/proxyserver.rb', line 196

def shutdown
  @command_dispatcher.shutdown
  super
end

#trace(msg) ⇒ Object



159
160
161
162
163
# File 'lib/jscmd/proxyserver.rb', line 159

def trace(msg)
  if @trace_request
    $stderr.puts "[TRACE] " + msg
  end
end