Skip to content

Commit

Permalink
Reorganized code that closes the Rack response if needed.
Browse files Browse the repository at this point in the history
  • Loading branch information
crohr committed Oct 19, 2011
1 parent fe935d9 commit 127da39
Showing 1 changed file with 29 additions and 25 deletions.
54 changes: 29 additions & 25 deletions lib/rack/jsonp.rb
Expand Up @@ -13,58 +13,62 @@ def initialize(app, options = {})
@callback_param = options[:callback_param] || 'callback' @callback_param = options[:callback_param] || 'callback'
end end


# Proxies the request to the application, stripping out the JSON-P callback # Proxies the request to the application, stripping out the JSON-P
# method and padding the response with the appropriate callback format. # callback method and padding the response with the appropriate callback
# format.
# #
# Changes nothing if no <tt>callback</tt> param is specified. # Changes nothing if no <tt>callback</tt> param is specified.
# #
def call(env) def call(env)
# remove the callback and _ parameters BEFORE calling the backend, # remove the callback and _ parameters BEFORE calling the backend, so
# so that caching middleware does not store a copy for each value of the callback parameter # that caching middleware does not store a copy for each value of the
# callback parameter
request = Rack::Request.new(env) request = Rack::Request.new(env)
callback = request.params.delete(@callback_param) callback = request.params.delete(@callback_param)
env['QUERY_STRING'] = env['QUERY_STRING'].split("&").delete_if{|param| param =~ /^(_|#{@callback_param})=/}.join("&") env['QUERY_STRING'] = env['QUERY_STRING'].split("&").delete_if{|param|
param =~ /^(_|#{@callback_param})=/
}.join("&")


status, headers, app_response = @app.call(env) status, headers, response = @app.call(env)
response = if callback && headers['Content-Type'] =~ /json/i
json = pad(callback, app_response) if callback && headers['Content-Type'] =~ /json/i
headers['Content-Length'] = json.first.bytesize.to_s response = pad(callback, response)
headers['Content-Length'] = response.first.bytesize.to_s
headers['Content-Type'] = 'application/javascript' headers['Content-Type'] = 'application/javascript'
json
elsif @carriage_return && headers['Content-Type'] =~ /json/i elsif @carriage_return && headers['Content-Type'] =~ /json/i
# add a \n after the response if this is a json (not JSONP) response # add a \n after the response if this is a json (not JSONP) response
json = carriage_return(app_response) response = carriage_return(response)
headers['Content-Length'] = json.first.bytesize.to_s headers['Content-Length'] = response.first.bytesize.to_s
json
else
nil
end

# Close original response if it was Rack::BodyProxy (or anything else responding to close,
# as we're going to lose it anyway), or it will cause thread failures with newer Rack
if app_response.respond_to?(:close) && response
app_response.close
end end


[status, headers, response || app_response] [status, headers, response]
end end


# Pads the response with the appropriate callback format according to the # Pads the response with the appropriate callback format according to the
# JSON-P spec/requirements. # JSON-P spec/requirements.
# #
# The Rack response spec indicates that it should be enumerable. The method # The Rack response spec indicates that it should be enumerable. The
# of combining all of the data into a single string makes sense since JSON # method of combining all of the data into a single string makes sense
# is returned as a full string. # since JSON is returned as a full string.
# #
def pad(callback, response, body = "") def pad(callback, response, body = "")
response.each{ |s| body << s.to_s } response.each{ |s| body << s.to_s }
close(response)
["#{callback}(#{body})"] ["#{callback}(#{body})"]
end end


def carriage_return(response, body = "") def carriage_return(response, body = "")
response.each{ |s| body << s.to_s } response.each{ |s| body << s.to_s }
close(response)
["#{body}\n"] ["#{body}\n"]
end end

# Close original response if it was Rack::BodyProxy (or anything else
# responding to close, as we're going to lose it anyway), or it will cause
# thread failures with newer Rack.
def close(io)
io.close if io.respond_to?(:close)
end
end end


end end

0 comments on commit 127da39

Please sign in to comment.