class Racknga::Middleware::JSONP

This is a middleware that provides JSONP support.

If you use this middleware, your Rack application just returns JSON response.

Usage:

require "racknga"

use Rack::ContentLength
use Racknga::Middleware::JSONP
json_application = Proc.new do |env|
  [200,
   {"Content-Type" => "application/json"},
   ['{"Hello": "World"}']]
end
run json_application

Results:

% curl 'http://localhost:9292/'
{"Hello": "World"}
% curl 'http://localhost:9292/?callback=function'
function({"Hello": "World"})

You can use this middleware with Racknga::Middleware::Cache. You should use this middleware before the cache middleware:

use Racknga::Middleawre::JSONP
use Racknga::Middleawre::Cache, :database_path => "var/cache/db"
run YourApplication

If you use this middleware after the cache middleware, the cache middleware will cache many responses that just only differ callback parameter value. Here are examples:

Recommended case:

use Racknga::Middleawre::JSONP
use Racknga::Middleawre::Cache, :database_path => "var/cache/db"
run YourApplication

Requests:

http://localhost:9292/                    -> no cache. cached.
http://localhost:9292/?callback=function1 -> use cache.
http://localhost:9292/?callback=function2 -> use cache.
http://localhost:9292/?callback=function3 -> use cache.
http://localhost:9292/?callback=function1 -> use cache.

Not recommended case:

use Racknga::Middleawre::Cache, :database_path => "var/cache/db"
use Racknga::Middleawre::JSONP
run YourApplication

Requests:

http://localhost:9292/                    -> no cache. cached.
http://localhost:9292/?callback=function1 -> no cache. cached.
http://localhost:9292/?callback=function2 -> no cache. cached.
http://localhost:9292/?callback=function3 -> no cache. cached.
http://localhost:9292/?callback=function1 -> use cache.

Public Class Methods

new(application) click to toggle source
# File lib/racknga/middleware/jsonp.rb, line 79
def initialize(application)
  @application = application
end

Public Instance Methods

call(environment) click to toggle source

For Rack.

# File lib/racknga/middleware/jsonp.rb, line 84
def call(environment)
  request = Rack::Request.new(environment)
  callback = request["callback"]
  update_cache_key(request) if callback
  status, headers, body = @application.call(environment)
  return [status, headers, body] unless callback
  header_hash = Rack::Utils::HeaderHash.new(headers)
  return [status, headers, body] unless json_response?(header_hash)
  body = Writer.new(callback, body)
  update_content_type(header_hash)
  [status, header_hash, body]
end

Private Instance Methods

json_response?(header_hash) click to toggle source
# File lib/racknga/middleware/jsonp.rb, line 116
def json_response?(header_hash)
  content_type = header_hash["Content-Type"]
  media_type = content_type.split(/\s*;\s*/, 2).first.downcase
  media_type == "application/json" or
    media_type == "application/javascript" or
    media_type == "text/javascript"
end
update_cache_key(request) click to toggle source
# File lib/racknga/middleware/jsonp.rb, line 98
def update_cache_key(request)
  return unless Middleware.const_defined?(:Cache)
  cache_key = Cache::KEY

  path = request.fullpath
  path, parameters = path.split(/\?/, 2)
  if parameters
    parameters = parameters.split(/[&;]/).reject do |parameter|
      key, value = parameter.split(/\=/, 2)
      key == "callback" or (key == "_" and value = /\A\d+\z/)
    end.join("&")
    path << "?" << parameters unless parameters.empty?
  end

  key = request.env[cache_key]
  request.env[cache_key] = [key, path].compact.join(":")
end
update_content_type(header_hash) click to toggle source
# File lib/racknga/middleware/jsonp.rb, line 124
def update_content_type(header_hash)
  content_type = header_hash["Content-Type"]
  media_type, parameters = content_type.split(/\s*;\s*/, 2)
  _ = media_type # FIXME: suppress a warning. :<
  # We should use application/javascript not
  # text/javascript when all IE <= 8 are deprecated. :<
  updated_content_type = ["text/javascript", parameters].compact.join("; ")
  header_hash["Content-Type"] = updated_content_type
end