• Stars
    star
    113
  • Rank 310,115 (Top 7 %)
  • Language
    Ruby
  • License
    University of Ill...
  • Created over 14 years ago
  • Updated almost 14 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Makes middleware that ships with Rack bullet-proof for async responses.

AsyncRack

Note upfront

Still experimental. Feel free to play around.

Introduction

So, have you been amazed by thin's async.callback? If not, go check it out. Come back here when you start missing your middleware.

So what is the issue with Rack and async.callback? Currently there are two ways of triggering a async responds. The first is to throw :async, the latter to return a status code of -1 (even though thin and ebb do disagree on that). Opposed to what others say, I would recommend using throw, as it simply skips middleware not able to handle :async. Also, it works on all servers supporting async.callback โ€“ thin, ebb, rainbows! and zbatery โ€“ about the same and copes better with middleware that is unable to handle an async respond.

That's the issue with async.callback: Most middleware is not aware of it. Let's say you got an app somewhat like that:

class Farnsworth
  def when_there_is_good_news
    Thread.new do # Well, actually, you want to hook into your event loop instead, I guess.
      wait_for_good_news
      yield
    end
  end
  
  def call(env)
    when_there_is_good_news do
      env["async.callback"].call [200, {'Content-Type' => 'text/plain'}, ['Good news, everyone!']]
    end
    throw :async
  end
end

Ok, now, since this app could end up on Reddit, you better prepare yourself for some heavy traffic. Say, you want to use the Rack::Deflate middleware, so you set it up in your config.ru and add the link to reddit yourself. The next day you get a call from your server admin. Why don't you at least compress your http response? Well what happened? The problem is, that by sending your response via env["async.callback"].call you talk directly to your web server (i.e. thin), bypassing all potential middleware.

Well, how do you avoid that? Simple: By just using middleware that plays well with async.callback. However, most middleware does not play well with it. In fact, most middleware that ships with rack does not play well with it. That's what I wrote this little library for. If you load async-rack it modifies all middleware that ships with rack, so it will work just fine with you throwing around your :async.

How does that work? Simple, whenever necessary, async-rack will replace async.callback with an appropriate proc object, so it has the chance to do it's response modifications whenever you feel like answering the http request.

Note: This library only 'fixes' the middleware that ships with rack, not other rack middleware. However, you can use the included helper classes to easily make other libraries handle async.callback.

What's in this package?

Rack middleware made async-proof

This middleware now works well with throw :async:

  • Rack::Chunked
  • Rack::CommonLogger
  • Rack::ConditionalGet
  • Rack::ContentLength
  • Rack::ContentType
  • Rack::Deflater
  • Rack::ETag
  • Rack::Head
  • Rack::Logger
  • Rack::Runtime
  • Rack::Sendfile
  • Rack::ShowStatus

Middleware that is async-proof out of the box

No changes where necessary for:

  • Rack::Cascade
  • Rack::Config
  • Rack::Directory
  • Rack::File
  • Rack::MethodOverride
  • Rack::Mime
  • Rack::NullLogger
  • Rack::Recursive
  • Rack::Reloader
  • Rack::Static
  • Rack::URLMap

Middleware not (yet) made async-proof

  • Rack::Lint (might not check async responses)
  • Rack::ShowExceptions (might not show exceptions for async responses)
  • Rack::Lock (might raise an exception)

How to make a middleware async-proof?

There are three types of middleware:

Middleware doing stuff before handing on the request

Example: Rack:::MethodOverride

Such middleware already works fine with async.callback. Also, from our perspective, middleware either creating a own response and not calling your app at all, or calling your app without modifying neither request nor response falls into this category, too.

Such middleware can easily be identified by having @app.call(env) or something similar as last line or always prefixed with a return inside the call method.

Middleware doing stuff after handing on the request

Example: Rack:::ETag

Here it is a bit tricky. Essentially what you want is running #call again on an async.callback but replace @app.call(env) with the parameter passed to async.callback. Well, apparently this is the most common case inside rack, so I created a mixin for that:

# Ok, Rack::FancyStuff does currently not work with async responses
require 'rack/fancy_stuff'

class FixedFancyStuff < AsyncRack::AsyncCallback(:FancyStuff)
  include AsyncRack::AsyncCallback::SimpleWrapper
end

See below to get an idea what actually happens here.

Middleware meddling with both your request and your response

Example: Rack::Runtime

# Let's assume there is some not so async middleware.
module Rack
  class FancyStuff
    def initialize(app)
      @app = app
    end
    
    def call(env)
      prepare_fancy_stuff env
      result = @app.call env
      perform_fancy_stuff result
    end
    
    def prepare_fancy_stuff(env)
      # ...
    end
    
    def perform_fancy_stuff(result)
      # ...
    end
  end
end

# What happens here is the following: We will subclass Rack::FancyStuff
# and then set Rack::FancyStuff = FixedFancyStuff. AsyncRack::AsyncCallback
# makes sure we don't screw that up.
class FixedFancyStuff < AsyncRack::AsyncCallback(:FancyStuff)
  # this method will handle async.callback
  def async_callback(result)
    # pass it on to thin / ebb / other middleware
    super perform_fancy_stuff(result)
  end
end

Rack::FancyStuff == FixedFancyStuff # => true

Setup

In general: Place a require 'async-rack' before setting up any middleware or you will end up with the synchronous version!

Please keep in mind that it only "fixes" middleware that ships with rack. Read: It works very well with Sinatra. With Rails and Merb, not so much!

With Rack

In your config.ru:

require 'async-rack'
require 'your-app'

use Rack::SomeMiddleware
run YourApp

With Sinatra

In your application file:

require 'async-rack'
require 'sinatra'

get '/' do
  # do some async stuff here
end

With Rails 2.x

In your config/environment.rb, add inside the Rails::Initializer.run block:

config.gem 'async-rack'

With Rails 3.x

In your Gemfile, add:

gem 'async-rack'

More Repositories

1

almost-sinatra

Sinatra refactored, only six lines now. More popular than a pair of socks.
Ruby
519
star
2

income-tax

Ruby library to calculate the income tax for any country
Ruby
337
star
3

rbenv-update

update rbenv and plugins
Shell
213
star
4

big_band

Making Sinatra swing.
Ruby
69
star
5

sinatra-reloader

Advanced code reloader for Sinatra
Ruby
68
star
6

rbenv-use

rbenv use rbx
Shell
67
star
7

yard-sinatra

Display sinatra routes in yard documentation.
Ruby
67
star
8

hansi

Your Hipster ANSI color library.
Ruby
65
star
9

coder

Handle encodings, no matter the Ruby version, Operating System and installed libraries
Ruby
64
star
10

Reak

Smalltalk on Rubinius
Ruby
57
star
11

sinatra-advanced-routes

Make Sinatra routes first class objects (extracted from BigBand).
Ruby
38
star
12

almost-rack-protection

Ruby
29
star
13

atom-sonic

Sonic Pi Atom integration
CoffeeScript
27
star
14

presentations

Ruby
26
star
15

rbenv-whatis

Shell
24
star
16

unpatched

Yet another WTF library!
Ruby
23
star
17

sinatra-namespace

Adds namespaces to Sinatra, allows namespaces to have local helpers.
Ruby
22
star
18

nii

Modern internationalization and localization solution for Ruby
Ruby
22
star
19

bithug

Yet Another Open Source GitHub Clone
Ruby
20
star
20

haml-more

Adds more functionality to Haml and Sass (part of BigBand).
Ruby
19
star
21

chainable

never use alias_method_chain, again
Ruby
18
star
22

convinius

Convenience library for Rubinius-only projects.
Ruby
17
star
23

sinatra-compass

Better Compass integration for Sinatra (extracted from BigBand).
Ruby
17
star
24

hpi

Ruby
16
star
25

almost-rack

Rack in three lines of code
Ruby
15
star
26

sinatra2

15
star
27

mixico

mixin hijinks โ€” enable and disable mixins
Ruby
14
star
28

dotfiles

my dotfiles
JavaScript
14
star
29

gemerator

Like NewGem, but way simpler, leaves no traces, extremely minimal boiler plate. Automatically handles extensions for Rack, Yard, Sinatra, etc correctly.
Ruby
14
star
30

tool

Ruby
14
star
31

brirb

IRB in your browser, via WebSockets.
Ruby
14
star
32

sinatra-config-file

Load Sinatra settings from a yaml file.
Ruby
13
star
33

papers

papers for hpi
Shell
13
star
34

travis-encrypt

proof of concept in browser encryption of travis settings
HTML
13
star
35

hadoop-scripting

Scripting Languages on Hadoop: Jaql vs. Pig Latin (MapReduce stuff)
Java
12
star
36

sinatra-sugar

Some extensions to the sinatra default behavior (usefull for other Sintatra extensions, extracted from BigBand).
Ruby
12
star
37

sinatra-coffeescript-example

Ruby
11
star
38

teresa

Ruby
11
star
39

otnetstring

Ruby
11
star
40

rack-async-stream

Ever tried streaming with Thin? Didn't work? Use this middleware.
Ruby
10
star
41

gem_tools

Ruby
9
star
42

sinatra-template

base template for classic sinatra apps
Ruby
9
star
43

rack-graph

Generate a tree displaying all your Rack middleware
Ruby
9
star
44

refine

Start using refine, today. Be ready for Ruby 2.0!
Ruby
9
star
45

sinatra-more-server

Add more servers to Sinatra::Base#run! (part of BigBand).
Ruby
9
star
46

monkey-lib

Making ruby extension frameworks pluggable (extracted from BigBand).
Ruby
8
star
47

gerippe

Monk skeleton with compass and rspec.
JavaScript
7
star
48

sinatra-test-helper

Test helper for Sinatra (extracted from BigBand).
Ruby
7
star
49

astro-data

Pre-calculated lunar and solar cycles.
7
star
50

rkh.im

click here to add a description
JavaScript
7
star
51

sinatra.fy

Sinatra in Fancy
Fancy
7
star
52

ruby_installer

Depraced, I switched to rvm.
7
star
53

twp

TWP3 in Ruby
Ruby
7
star
54

json-csrf

Ruby
6
star
55

ruby-xlib

Some base libraries for writing a windowmanager in ruby.
C
6
star
56

wonko-web-server

experimental preforking rack handler. and by experimental I mean: no tests.
Ruby
6
star
57

serv

simple, threaded rack handler (webserver)
Ruby
6
star
58

sinatra-web-inspector

DEPRECATED
Ruby
5
star
59

proposals

contact me if you want me to give a talk somewhere
Ruby
5
star
60

donewunder

Ruby
5
star
61

priest

Priest is a more advanced command line tool for your monk projects.
Ruby
5
star
62

slaml

C
4
star
63

mw_api

Fast ruby client library for using MediaWiki's API.
Ruby
4
star
64

stored_hash

Synchronize a hash with a yaml file.
Ruby
4
star
65

circleci-ios

iOS app for Circle CI
Objective-C
3
star
66

prefix

Ruby
3
star
67

github-moderator

Allows someone who is not an owner to add users to a Github team.
Ruby
3
star
68

sinatra-extension

Mixin to ease Sinatra extension development (part of BigBand).
Ruby
3
star
69

dart

Dash on Rubinius
Ruby
3
star
70

cool-app

Ruby
3
star
71

deferrable

Callback indirection for JavaScript
JavaScript
3
star
72

redcar-align

Auto-align a text section in Redcar (Ctrl+Q)
Ruby
3
star
73

minimal-redjs

js + ruby
Ruby
2
star
74

socialshareprivacy

this is just a mirror
JavaScript
2
star
75

reloader-shootout

RSoC 2010: Ruby Reloader Benchmarks
Ruby
2
star
76

bundler_test

Ruby
2
star
77

maglev-experiments

Ruby
2
star
78

responsive

Responsive CSS breakpoints that respect a user's font size settings, and uses layers (no more !important).
SCSS
2
star
79

es-lab

Unofficial git mirror of the es-lab svn repo.
JavaScript
2
star
80

sinatra-decompile

Recreate string patterns from Sinatra routes
Ruby
2
star
81

playground

Ruby
1
star
82

travis-test-staging

Test repo on travis-staging
Ruby
1
star
83

test

1
star
84

travis-encrypt-file-example

1
star
85

maxcube.rb

Ruby
1
star
86

travis-surveillance

Veille sur un projet.
Ruby
1
star
87

travis-osx-test

1
star
88

reset

Yet another CSS/Sass reset.
SCSS
1
star
89

play

1
star