rack-mongrel2 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
@@ -0,0 +1,35 @@
1
+ require 'zmq'
2
+ require 'mongrel2/request'
3
+ require 'mongrel2/response'
4
+
5
+ module Mongrel2
6
+ class Connection
7
+ CTX = ZMQ::Context.new(1)
8
+
9
+ def initialize(ident, sub, pub, block = true)
10
+ @ident, @sub, @pub, @block = ident, sub, pub, block
11
+
12
+ # Connect to receive requests
13
+ @reqs = CTX.socket(ZMQ::UPSTREAM)
14
+ @reqs.connect(sub)
15
+
16
+ # Connect to send responses
17
+ @resp = CTX.socket(ZMQ::PUB)
18
+ @resp.connect(pub)
19
+ @resp.setsockopt(ZMQ::IDENTITY, ident)
20
+ end
21
+
22
+ def recv
23
+ Request.parse(@reqs.recv)
24
+ end
25
+
26
+ def reply(req, body, status = 200, headers = {})
27
+ Response.new(@resp).send_http(req, body, status, headers)
28
+ end
29
+
30
+ def close
31
+ @reqs.close rescue nil
32
+ @resp.close rescue nil
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ module Mongrel2
2
+ class Request
3
+ attr_reader :headers, :body, :uuid, :conn_id
4
+
5
+ class << self
6
+ def parse(msg)
7
+ # UUID CONN_ID PATH SIZE:HEADERS,SIZE:BODY,
8
+ uuid, conn_id, path, rest = msg.split(' ', 4)
9
+ headers, rest = parse_netstring(rest)
10
+ body, _ = parse_netstring(rest)
11
+ headers = Yajl::Parser.parse(headers)
12
+ new(uuid, conn_id, path, headers, body)
13
+ end
14
+
15
+ def parse_netstring(ns)
16
+ # SIZE:HEADERS,
17
+
18
+ len, rest = ns.split(':', 2)
19
+ len = len.to_i
20
+ raise "Netstring did not end in ','" unless rest[len].chr == ','
21
+ [rest[0, len], rest[(len + 1)..-1]]
22
+ end
23
+ end
24
+
25
+ def initialize(uuid, conn_id, path, headers, body)
26
+ @uuid, @conn_id, @path, @headers, @body = uuid, conn_id, path, headers, body
27
+ @data = headers['METHOD'] == 'JSON' ? Yajl::Parser.parse(body) : {}
28
+ end
29
+
30
+ def disconnect?
31
+ headers['METHOD'] == 'JSON' && @data['type'] == 'disconnect'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,66 @@
1
+ module Mongrel2
2
+ class Response
3
+ StatusMessage = {
4
+ 100 => 'Continue',
5
+ 101 => 'Switching Protocols',
6
+ 200 => 'OK',
7
+ 201 => 'Created',
8
+ 202 => 'Accepted',
9
+ 203 => 'Non-Authoritative Information',
10
+ 204 => 'No Content',
11
+ 205 => 'Reset Content',
12
+ 206 => 'Partial Content',
13
+ 300 => 'Multiple Choices',
14
+ 301 => 'Moved Permanently',
15
+ 302 => 'Found',
16
+ 303 => 'See Other',
17
+ 304 => 'Not Modified',
18
+ 305 => 'Use Proxy',
19
+ 307 => 'Temporary Redirect',
20
+ 400 => 'Bad Request',
21
+ 401 => 'Unauthorized',
22
+ 402 => 'Payment Required',
23
+ 403 => 'Forbidden',
24
+ 404 => 'Not Found',
25
+ 405 => 'Method Not Allowed',
26
+ 406 => 'Not Acceptable',
27
+ 407 => 'Proxy Authentication Required',
28
+ 408 => 'Request Timeout',
29
+ 409 => 'Conflict',
30
+ 410 => 'Gone',
31
+ 411 => 'Length Required',
32
+ 412 => 'Precondition Failed',
33
+ 413 => 'Request Entity Too Large',
34
+ 414 => 'Request-URI Too Large',
35
+ 415 => 'Unsupported Media Type',
36
+ 416 => 'Request Range Not Satisfiable',
37
+ 417 => 'Expectation Failed',
38
+ 500 => 'Internal Server Error',
39
+ 501 => 'Not Implemented',
40
+ 502 => 'Bad Gateway',
41
+ 503 => 'Service Unavailable',
42
+ 504 => 'Gateway Timeout',
43
+ 505 => 'HTTP Version Not Supported'
44
+ }
45
+
46
+ def initialize(resp)
47
+ @resp = resp
48
+ end
49
+
50
+ def send_http(req, body, status, headers)
51
+ send_resp(req.uuid, req.conn_id, build_http_response(body, status, headers))
52
+ end
53
+
54
+ private
55
+
56
+ def send_resp(uuid, conn_id, data)
57
+ @resp.send('%s %d:%s, %s' % [uuid, conn_id.size, conn_id, data])
58
+ end
59
+
60
+ def build_http_response(body, status, headers)
61
+ headers['Content-Length'] = body.size.to_s
62
+ headers = headers.map{ |k, v| '%s: %s' % [k,v] }.join("\r\n")
63
+ "HTTP/1.1 #{status} #{StatusMessage[status.to_i]}\r\n#{headers}\r\n\r\n#{body}"
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,64 @@
1
+ require 'mongrel2/connection'
2
+ require 'stringio'
3
+
4
+ module Rack
5
+ module Handler
6
+ class Mongrel2
7
+ class << self
8
+ def run(app, options = {})
9
+ options = {
10
+ :recv => 'tcp://127.0.0.1:9997' || ENV['RACK_MONGREL2_RECV'],
11
+ :send => 'tcp://127.0.0.1:9996' || ENV['RACK_MONGREL2_SEND'],
12
+ :uuid => ENV['RACK_MONGREL2_UUID']
13
+ }.merge(options)
14
+ raise ArgumentError.new('Must specify an :uuid or set RACK_MONGREL2_UUID') if options[:uuid].nil?
15
+
16
+ conn = ::Mongrel2::Connection.new(options[:uuid], options[:recv], options[:send])
17
+
18
+ running = true
19
+ trap('SIGINT') do
20
+ running = false
21
+ conn.close
22
+ end
23
+
24
+ while running
25
+ req = conn.recv
26
+ next if req.disconnect?
27
+ return unless running
28
+
29
+ script_name = ENV['RACK_RELATIVE_URL_ROOT'] || req.headers['PATTERN'].split('(', 2).first.gsub(/\/$/, '')
30
+ env = {
31
+ 'rack.version' => Rack::VERSION,
32
+ 'rack.url_scheme' => 'http', # Only HTTP for now
33
+ 'rack.input' => StringIO.new(req.body),
34
+ 'rack.errors' => $stderr,
35
+ 'rack.multithread' => true,
36
+ 'rack.multiprocess' => true,
37
+ 'rack.run_once' => false,
38
+ 'mongrel2.pattern' => req.headers['PATTERN'],
39
+ 'REQUEST_METHOD' => req.headers['METHOD'],
40
+ 'SCRIPT_NAME' => script_name,
41
+ 'PATH_INFO' => req.headers['PATH'].gsub(script_name, ''),
42
+ 'QUERY_STRING' => req.headers['QUERY']
43
+ }
44
+
45
+ env['SERVER_NAME'], env['SERVER_PORT'] = req.headers['host'].split(':', 2)
46
+ req.headers.each do |key, val|
47
+ unless key =~ /content_(type|length)/i
48
+ key = "HTTP_#{key.upcase}"
49
+ end
50
+ env[key] = val
51
+ end
52
+
53
+ status, headers, rack_response = app.call(env)
54
+ body = ''
55
+ rack_response.each { |b| body << b }
56
+ conn.reply(req, body, status, headers)
57
+ end
58
+ ensure
59
+ conn.close if conn.respond_to?(:close)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,74 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-mongrel2}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Daniel Huckstep"]
12
+ s.date = %q{2010-10-23}
13
+ s.description = %q{A Rack handler for the Mongrel2 web server, by Zed Shaw. http://mongrel2.org/}
14
+ s.email = %q{darkhelmet@darkhelmetlive.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".rvmrc",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/mongrel2/connection.rb",
30
+ "lib/mongrel2/request.rb",
31
+ "lib/mongrel2/response.rb",
32
+ "lib/rack-mongrel2.rb",
33
+ "lib/rack/handler/mongrel2.rb",
34
+ "rack-mongrel2.gemspec",
35
+ "spec/rack-mongrel2_spec.rb",
36
+ "spec/spec.opts",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/darkhelmet/rack-mongrel2}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.7}
43
+ s.summary = %q{The only Mongrel2 Rack handler you'll ever need.}
44
+ s.test_files = [
45
+ "spec/rack-mongrel2_spec.rb",
46
+ "spec/spec_helper.rb"
47
+ ]
48
+
49
+ if s.respond_to? :specification_version then
50
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
+ s.specification_version = 3
52
+
53
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
+ s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
55
+ s.add_runtime_dependency(%q<zmq>, ["~> 2.0.9"])
56
+ s.add_development_dependency(%q<jeweler>, ["~> 1.4.0"])
57
+ s.add_development_dependency(%q<rspec>, ["~> 2.0.1"])
58
+ s.add_development_dependency(%q<yard>, ["~> 0.6.1"])
59
+ else
60
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
61
+ s.add_dependency(%q<zmq>, ["~> 2.0.9"])
62
+ s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
63
+ s.add_dependency(%q<rspec>, ["~> 2.0.1"])
64
+ s.add_dependency(%q<yard>, ["~> 0.6.1"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<yajl-ruby>, ["~> 0.7.8"])
68
+ s.add_dependency(%q<zmq>, ["~> 2.0.9"])
69
+ s.add_dependency(%q<jeweler>, ["~> 1.4.0"])
70
+ s.add_dependency(%q<rspec>, ["~> 2.0.1"])
71
+ s.add_dependency(%q<yard>, ["~> 0.6.1"])
72
+ end
73
+ end
74
+
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rack-mongrel2
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 0.0.0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Daniel Huckstep
@@ -117,7 +117,12 @@ files:
117
117
  - README.rdoc
118
118
  - Rakefile
119
119
  - VERSION
120
+ - lib/mongrel2/connection.rb
121
+ - lib/mongrel2/request.rb
122
+ - lib/mongrel2/response.rb
120
123
  - lib/rack-mongrel2.rb
124
+ - lib/rack/handler/mongrel2.rb
125
+ - rack-mongrel2.gemspec
121
126
  - spec/rack-mongrel2_spec.rb
122
127
  - spec/spec.opts
123
128
  - spec/spec_helper.rb