Class: Fishwife::RackServlet

Inherits:
HttpServlet
  • Object
show all
Defined in:
lib/fishwife/rack_servlet.rb

Constant Summary collapse

ASCII_8BIT =
Encoding.find( "ASCII-8BIT" )

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ RackServlet

Returns a new instance of RackServlet.



39
40
41
42
43
# File 'lib/fishwife/rack_servlet.rb', line 39

def initialize( app )
  super()
  @log = RJack::SLF4J[ self.class ]
  @app = app
end

Instance Method Details

#service(request, response) ⇒ Object

Takes an incoming request (as a Java Servlet) and dispatches it to the rack application setup via [rackup]. All this really involves is translating the various bits of the Servlet API into the Rack API on the way in, and translating the response back on the way out.

Also, we implement a common extension to the Rack api for asynchronous request processing. We supply an ‘async.callback’ parameter in env to the Rack application. If we catch an :async symbol thrown by the app, we initiate a Jetty continuation.

When ‘async.callback’ gets a response with empty headers and an empty body, we declare the async response finished.



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/fishwife/rack_servlet.rb', line 58

def service(request, response)
  # Turn the ServletRequest into a Rack env hash
  env = servlet_to_rack(request)

  # Handle asynchronous responses via Servlet continuations.
  continuation = ContinuationSupport.getContinuation(request)

  # If this is an expired connection, do nothing.
  return if continuation.isExpired

  # We should never be re-dispatched.
  raise("Request re-dispatched.") unless continuation.isInitial

  # Add our own special bits to the rack environment so that Rack
  # middleware can have access to the Java internals.
  env['rack.java.servlet'] = true
  env['rack.java.servlet.request'] = request
  env['rack.java.servlet.response'] = response
  env['rack.java.servlet.continuation'] = continuation

  # Add an callback that can be used to add results to the
  # response asynchronously.
  env['async.callback'] = lambda do |rack_response|
    servlet_response = continuation.getServletResponse
    rack_to_servlet(rack_response, servlet_response) and
      continuation.complete
  end

  # Execute the Rack request.
  catch(:async) do
    rack_response = @app.call(env)

    # For apps that don't throw :async.
    unless(rack_response[0] == -1)
      # Nope, nothing asynchronous here.
      rack_to_servlet(rack_response, response)
      return
    end
  end

  # If we got here, this is a continuation.
  continuation.suspend(response)

rescue NativeException => n
  @log.warn( "On service (native): #{n.cause.to_string}" )
  raise n.cause
rescue Exception => e
  @log.error( "On service: #{e}" )
  raise e
end