robertodecurnex/rack-jsonp-middleware

View on GitHub
lib/rack/jsonp.rb

Summary

Maintainability
A
0 mins
Test Coverage

require 'pathname'

module Rack

  class JSONP

    def initialize(app)
      @app = app
    end

    def call(env)
      request = Rack::Request.new(env)
      requesting_jsonp = Pathname(request.env['PATH_INFO']).extname =~ /^\.jsonp$/i
      callback = request.params['callback']

      return [400,{},[]] if requesting_jsonp && !self.valid_callback?(callback)

      if requesting_jsonp
        env['PATH_INFO'] = env['PATH_INFO'].sub(/\.jsonp/i, '.json')
        env['REQUEST_URI'] = env['PATH_INFO']
      end

      status, headers, body = @app.call(env)

      if requesting_jsonp && self.json_response?(headers['Content-Type'])
        json = ""
        body.each { |s| json << s }
        body = ["/**/#{callback}(#{json});"]
        headers['Content-Length'] = Rack::Utils.bytesize(body[0]).to_s
        headers['Content-Type'] = headers['Content-Type'].sub(/^[^;]+(;?)/, "#{MIME_TYPE}\\1")
      end

      [status, headers, body]
    end

  protected
    
    # Do not allow arbitrary Javascript in the callback.
    #
    # @return [Regexp]
    VALID_CALLBACK_PATTERN = /^[a-zA-Z0-9\._]+$/

    # @return [String] the JSONP response mime type.
    MIME_TYPE = 'application/javascript'

    # Checks if the callback function name is safe/valid.
    #
    # @param [String] callback the string to be used as the JSONP callback function name.
    # @return [TrueClass|FalseClass]
    def valid_callback?(callback)
      !callback.nil? && !callback.match(VALID_CALLBACK_PATTERN).nil?
    end

    # Check if the response Content Type is JSON.
    #
    # @param [Hash] content_type the response Content Type
    # @return [TrueClass|FalseClass]
    def json_response?(content_type)
      !content_type.nil? && !content_type.match(/^application\/json/i).nil?
    end

  end

end