Skip to content

Instantly share code, notes, and snippets.

@dylanz
Created January 27, 2010 06:57
Show Gist options
  • Save dylanz/287610 to your computer and use it in GitHub Desktop.
Save dylanz/287610 to your computer and use it in GitHub Desktop.
require 'resolv'
require 'memcached'
module Rack
module JSONP
# This supports page caching JSONP requests with Memcached as the cache store.
# It assumes that the json data is already being stored / page cached in
# Memcached. This adapter simply grabs the json data from Memcached, pads it
# with the current requests callback value, and, viola. If the data isn't found
# in Memcached, the request simply continues through the chain to your application.
#
# Caveats:
# - It currently assumes the memcached key will be the current requests uri,
# minus the callback parameter (which is chopped off).
# - It doesn't support hashing/multiple memcached servers, so it only
# supports one machine at the moment.
# - If this was Rails specific, we could grab the memcached details
# from ActionController::Base.cache_store, but, this is trying to
# be platform agnostic. So, remember if you change memcached
# configurations, you'll want to update the middleware declaration
# as well!
#
# Usage:
# use Rack::JSONP::MemcacheDee, :memcached => "127.0.0.1:11211",
# :headers => {}, :callback => "callback", :namespace => "something"
#
# Options:
# headers: Custom headers to add to the response (default = {})
# memcached: Location of memcached instance (default = 127.0.0.1:11211)
# callback: Custom name for callback parameter (default = "callback")
# namespace: Custom memcached namespace (default = none)
#
class MemcacheDee
def initialize(app, opts = {})
@app = app
@namespace = opts[:namespace]
@headers = opts[:headers] || {}
@callback = opts[:callback] || "callback"
# resolve the ip from host if provided, and initialize memcached.
memcached_host, memcached_port = (opts[:memcached] || "127.0.0.1:11211").split(":")
@memcached = Memcached.new(Resolv.getaddress(memcached_host) + ":" + memcached_port)
end
def call(env)
begin
request = Rack::Request.new(env)
if request.params.include?(@callback)
request_uri = env['REQUEST_URI']
# remove the callback parameter, extracting its value
@callback_value = request_uri.slice!(/(.?)#{@callback}=([a-zA-Z\d]*)/)
@callback_value = @callback_value.split("=").at(1)
# allow for a custom namespace, or none
memcached_key = @namespace ? "#{@namespace}:#{request_uri}" : request_uri
# request the data from memcached
response = "#{@callback_value}(#{@memcached.get(memcached_key, false)})"
# set headers and send back the response
@headers['Content-Length'] = response.length.to_s
@headers['Content-Type'] = 'application/json'
[200, @headers, response]
else
status, headers, response = @app.call(env)
end
rescue Memcached::NotFound
status, headers, response = @app.call(env)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment