thin-fun_embed 0.0.1
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/Gemfile +4 -0
- data/LICENSE +23 -0
- data/README.rdoc +118 -0
- data/Rakefile +2 -0
- data/examples/config.ru +2 -0
- data/examples/rack_like.rb +28 -0
- data/examples/simple_200_ok.rb +16 -0
- data/examples/status_body.rb +16 -0
- data/lib/thin/fun_embed/constants.rb +18 -0
- data/lib/thin/fun_embed/version.rb +6 -0
- data/lib/thin/fun_embed.rb +179 -0
- data/thin-fun_embed.gemspec +21 -0
- metadata +74 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012 Sokolov Yura 'funny-falcon'
|
2
|
+
Copyright (c) 2011 Nils Franzén
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
= Thin::FunEmbed
|
2
|
+
|
3
|
+
This is minimalistic web server for embedding into EventMachine-enabled application
|
4
|
+
based on {idea of Nils Franzén}[http://www.franzens.org/2011/10/writing-minimalistic-web-server-using.html],
|
5
|
+
but it uses origin Thin::Request instead of custom wrapper to thin parser.
|
6
|
+
|
7
|
+
It is intentionally not full fledge rack server, but you could use +send_status_headers_body+ to return
|
8
|
+
rack application's response to client.
|
9
|
+
|
10
|
+
It is capable to serve keep-alive requests (but not pipelined).
|
11
|
+
|
12
|
+
It is faster than Thin itself cause it allows you to send very custom response, and even for Rack like
|
13
|
+
response it doesn't do many checks on your output. But it meens, that you responsible for you response :)
|
14
|
+
|
15
|
+
Rack hello world, tested with +ab -k -c 500 -n 50000 127.0.0.1:8080/+ on Core i3@1600Mhz
|
16
|
+
|
17
|
+
* with Thin::FunEmbed (see examples/rack_like.rb) ~ 14000 req/sec
|
18
|
+
* with `thin -p 8080 -e production start` ~ 6500 req/sec
|
19
|
+
|
20
|
+
(But consider, that this is for very dump response. Do not expect big speedup for heavy weight ones')
|
21
|
+
|
22
|
+
Also, it doesn't bother with keep-alive timeouts, limits and many other things that Thin do. But it is not
|
23
|
+
too hard too look for them in the Thin's sources. And, in any way, it is better to place this "server" behind
|
24
|
+
"something like" Nginx, which could use keep-alive connections to upstream since version 1.1 .
|
25
|
+
|
26
|
+
== Installation
|
27
|
+
|
28
|
+
Add this line to your application's Gemfile:
|
29
|
+
|
30
|
+
gem 'thin-fun_embed'
|
31
|
+
|
32
|
+
And then execute:
|
33
|
+
|
34
|
+
$ bundle
|
35
|
+
|
36
|
+
Or install it yourself as:
|
37
|
+
|
38
|
+
$ gem install thin-fun_embed
|
39
|
+
|
40
|
+
== Usage
|
41
|
+
|
42
|
+
You should subclass Thin::FunEmbed and override #handle_http_request method. Then you should start it with
|
43
|
+
EM.start_server as any other EM::Connection .
|
44
|
+
|
45
|
+
Abstract example:
|
46
|
+
require 'thin/fun_embed.rb'
|
47
|
+
class MyServer < Thin::FunEmbed
|
48
|
+
def handle_http_request(env)
|
49
|
+
if rack_like_response?
|
50
|
+
send_status_headers_body(status, headers, body)
|
51
|
+
elsif is_200_ok?
|
52
|
+
send_200_ok(body)
|
53
|
+
elsif is_status_body?
|
54
|
+
send_status_body(status, body)
|
55
|
+
elsif is_raw_string?
|
56
|
+
send_raw_string(full_http_response_string, try_to_keep_alive)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
host, port = '0.0.0.0', 8080
|
62
|
+
EM.run do
|
63
|
+
EM.start_server host, port, MyServer
|
64
|
+
end
|
65
|
+
|
66
|
+
200 ok example:
|
67
|
+
require 'thin/fun_embed.rb'
|
68
|
+
|
69
|
+
class Simple200ok < Thin::FunEmbed
|
70
|
+
def handle_http_request(env)
|
71
|
+
if rand(2) == 1
|
72
|
+
send_200_ok('{"hello":"world"}', 'application/javascript')
|
73
|
+
else
|
74
|
+
send_200_ok('hello world')
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
EM.run do
|
80
|
+
EM.start_server '0.0.0.0', 8080, Simple200ok
|
81
|
+
end
|
82
|
+
|
83
|
+
Rack like example with correct socket closing:
|
84
|
+
require 'thin/fun_embed.rb'
|
85
|
+
|
86
|
+
class RackLikeServer < Thin::FunEmbed
|
87
|
+
attr_accessor :app
|
88
|
+
def handle_http_request(env)
|
89
|
+
send_rack_response(*app.call(env))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
app = proc{|env| [200, {'Content-Length'=>6}, ['hello', "\n"]]}
|
94
|
+
|
95
|
+
host, port = '0.0.0.0', 8080
|
96
|
+
all_conns = {}
|
97
|
+
trap(:INT) do
|
98
|
+
EM.schedule{
|
99
|
+
all_conns.each{|conn, _| conn.close_after_writting}
|
100
|
+
EM.next_tick{ EM.stop }
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
EM.run do
|
105
|
+
EM.start_server host, port, RackLikeServer do |conn|
|
106
|
+
conn.app = app
|
107
|
+
all_conns[conn] = true
|
108
|
+
conn.unbind_callback = all_conns.method(:delete)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
== Contributing
|
113
|
+
|
114
|
+
1. Fork it
|
115
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
116
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
117
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
118
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/examples/config.ru
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'thin/fun_embed.rb'
|
3
|
+
|
4
|
+
class RackLikeServer < Thin::FunEmbed
|
5
|
+
attr_accessor :app
|
6
|
+
def handle_http_request(env)
|
7
|
+
send_rack_response(*app.call(env))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
app = proc{|env| [200, {'Content-Length'=>6}, ['hello', "\n"]]}
|
12
|
+
|
13
|
+
host, port = '0.0.0.0', 8080
|
14
|
+
all_conns = {}
|
15
|
+
trap(:INT) do
|
16
|
+
EM.schedule{
|
17
|
+
all_conns.each{|conn, _| conn.close_after_writting}
|
18
|
+
EM.next_tick{ EM.stop }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
EM.run do
|
23
|
+
EM.start_server host, port, RackLikeServer do |conn|
|
24
|
+
conn.app = app
|
25
|
+
all_conns[conn] = true
|
26
|
+
conn.unbind_callback = all_conns.method(:delete)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'thin/fun_embed.rb'
|
3
|
+
|
4
|
+
class Simple200ok < Thin::FunEmbed
|
5
|
+
def handle_http_request(env)
|
6
|
+
if false && rand(2) == 1
|
7
|
+
send_200_ok('{"hello":"world"}', 'application/javascript')
|
8
|
+
else
|
9
|
+
send_200_ok('hello world')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
EM.run do
|
15
|
+
EM.start_server '0.0.0.0', 8080, Simple200ok
|
16
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# :stopdoc:
|
2
|
+
require 'thin/fun_embed.rb'
|
3
|
+
|
4
|
+
class StatusBody < Thin::FunEmbed
|
5
|
+
def handle_http_request(env)
|
6
|
+
if env[PATH_INFO] =~ /\.js$/
|
7
|
+
send_status_body(200, '{"hello":"world"}', 'application/javascript')
|
8
|
+
else
|
9
|
+
send_status_body(200, 'hello world')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
EM.run do
|
15
|
+
EM.start_server '0.0.0.0', 8080, StatusBody
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Thin # :nodoc:
|
2
|
+
class FunEmbed
|
3
|
+
# Usefull constants for analysing Rack environmentc
|
4
|
+
module Constants
|
5
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
6
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
7
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
8
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
9
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
10
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
11
|
+
|
12
|
+
GET = 'GET'.freeze
|
13
|
+
POST = 'POST'.freeze
|
14
|
+
DELETE = 'DELETE'.freeze
|
15
|
+
PUT = 'PUT'.freeze
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require "eventmachine"
|
2
|
+
require "thin_parser"
|
3
|
+
require "thin/request"
|
4
|
+
require "thin/statuses"
|
5
|
+
require "thin/fun_embed/version"
|
6
|
+
require "thin/fun_embed/constants"
|
7
|
+
|
8
|
+
module Thin # :nodoc:
|
9
|
+
SERVER = "FunEmbed #{FunEmbed::VERSION}".freeze
|
10
|
+
module VERSION # :nodoc:
|
11
|
+
RACK = [1,1].freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
# minimalistic HTTP server for embedding into EventMachine'd applications
|
15
|
+
#
|
16
|
+
# Method #handle_http_request accept fully formed Rack environment and should
|
17
|
+
# call send http status+headers+body using one of #send_200_ok, #send_status_body,
|
18
|
+
# #send_raw_string and #send_rack_response methods or manually. If you send
|
19
|
+
# response manually, then you should call #consider_keep_alive method with your wish:
|
20
|
+
# whether you want to try keep-alive connection or not.
|
21
|
+
#
|
22
|
+
# require 'thin/fun_embed.rb'
|
23
|
+
#
|
24
|
+
# class Simple200ok < Thin::FunEmbed
|
25
|
+
# def handle_http_request(env)
|
26
|
+
# if rand(2) == 1
|
27
|
+
# send_200_ok('{"hello":"world"}', 'application/javascript')
|
28
|
+
# else
|
29
|
+
# send_200_ok('hello world')
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# EM.run do
|
35
|
+
# EM.start_server '0.0.0.0', 8080, Simple200ok
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
class FunEmbed < EM::Connection
|
39
|
+
# :stopdoc:
|
40
|
+
include Constants
|
41
|
+
FULL_KEEP_ALIVE = "Connection: keep-alive\r\n".freeze
|
42
|
+
RN = "\r\n".freeze
|
43
|
+
RNRN = "\r\n\r\n".freeze
|
44
|
+
NL = "\n".freeze
|
45
|
+
CONTENT_LENGTH = "Content-Length".freeze
|
46
|
+
CONTENT_LENGTH_DT = "\r\nContent-Length: ".freeze
|
47
|
+
CONNECTION = 'Connection'.freeze
|
48
|
+
KEEP_ALIVE = 'keep-alive'.freeze
|
49
|
+
CLOSE = 'close'.freeze
|
50
|
+
TEXT_PLAIN = 'text/plain'.freeze
|
51
|
+
HTTP_STATUS_CODES = Thin::HTTP_STATUS_CODES
|
52
|
+
# :startdoc:
|
53
|
+
|
54
|
+
# callback, which called from +#unbind+.
|
55
|
+
# Could be used for monitoring connections.
|
56
|
+
attr_accessor :unbind_callback
|
57
|
+
|
58
|
+
# signals if client may accept keep-alive connection
|
59
|
+
attr :keep_alive
|
60
|
+
|
61
|
+
def post_init
|
62
|
+
@keep_alive = nil
|
63
|
+
@parser = Thin::Request.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def receive_data(data)
|
67
|
+
if @parser.parse(data)
|
68
|
+
@keep_alive = @parser.persistent?
|
69
|
+
handle_http_request(@parser.env)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# main method for override
|
74
|
+
# you ought to put your application logic here
|
75
|
+
def handle_http_request(env)
|
76
|
+
send_200_ok "you should override #handle_http_request"
|
77
|
+
end
|
78
|
+
|
79
|
+
# send simple '200 OK' response with a body
|
80
|
+
def send_200_ok(body = '', type = TEXT_PLAIN, try_keep_alive = true)
|
81
|
+
send_data("HTTP/1.1 200 OK\r\nContent-Type: #{type}\r\n"\
|
82
|
+
"Content-Length: #{body.bytesize}\r\n"\
|
83
|
+
"#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
|
84
|
+
send_data(body)
|
85
|
+
consider_keep_alive(try_keep_alive)
|
86
|
+
end
|
87
|
+
|
88
|
+
# send simple response with status, body and type
|
89
|
+
def send_status_body(status, body = '', type = TEXT_PLAIN, try_keep_alive = true)
|
90
|
+
status = status.to_i
|
91
|
+
if status < 200 || status == 204 || status == 304
|
92
|
+
send_data("HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"\
|
93
|
+
"#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
|
94
|
+
else
|
95
|
+
send_data("HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"\
|
96
|
+
"Content-Type: #{type}\r\n"\
|
97
|
+
"Content-Length: #{body.bytesize}\r\n"\
|
98
|
+
"#{keep_alive ? FULL_KEEP_ALIVE : nil}\r\n")
|
99
|
+
send_data(body)
|
100
|
+
end
|
101
|
+
consider_keep_alive(try_keep_alive)
|
102
|
+
end
|
103
|
+
|
104
|
+
# send fully formatted HTTP response
|
105
|
+
def send_raw_string(string, try_keep_alive = true)
|
106
|
+
send_data(string)
|
107
|
+
try_keep_alive &&= @keep_alive &&
|
108
|
+
(cl = string.index(CONTENT_LENGTH_DT)) &&
|
109
|
+
(kai = string.index(FULL_KEEP_ALIVE)) &&
|
110
|
+
kai < (rn = string.index(RNRN)) &&
|
111
|
+
cl < rn
|
112
|
+
consider_keep_alive(try_keep_alive)
|
113
|
+
end
|
114
|
+
|
115
|
+
# send Rack like response (status, headers, body)
|
116
|
+
#
|
117
|
+
# This method tries to implement as much of Rack as it progmatically needed, but not more.
|
118
|
+
# Test it before use
|
119
|
+
def send_rack_response(status, headers, body, try_keep_alive = true)
|
120
|
+
status = status.to_i
|
121
|
+
out = "HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n"
|
122
|
+
|
123
|
+
if String === headers
|
124
|
+
try_keep_alive &&= headers.index(CONTENT_LENGTH_DT) && headers.index(FULL_KEEP_ALIVE)
|
125
|
+
out << headers
|
126
|
+
else
|
127
|
+
content_length = connection = nil
|
128
|
+
headers.each do |k, v|
|
129
|
+
if k == CONTENT_LENGTH
|
130
|
+
content_length = v
|
131
|
+
elsif k == CONNECTION
|
132
|
+
connection = v
|
133
|
+
end
|
134
|
+
if String === v
|
135
|
+
unless v.index(NL)
|
136
|
+
out << "#{k}: #{v}\r\n"
|
137
|
+
else
|
138
|
+
v.each_line{|l| out << "#{k}: #{l}\r\n"}
|
139
|
+
end
|
140
|
+
elsif v.respond_to?(:each)
|
141
|
+
v.each{|l| out << "#{k}: #{l}\r\n"}
|
142
|
+
else
|
143
|
+
unless (v=v.to_s).index(NL)
|
144
|
+
out << "#{k}: #{v}\r\n"
|
145
|
+
else
|
146
|
+
v.each_line{|l| out << "#{k}: #{l}\r\n"}
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
try_keep_alive &&= @keep_alive && content_length && (!connection || connection == KEEP_ALIVE)
|
152
|
+
out << FULL_KEEP_ALIVE if try_keep_alive && !connection
|
153
|
+
end
|
154
|
+
out << RN
|
155
|
+
send_data out
|
156
|
+
|
157
|
+
if String === body
|
158
|
+
send_data(body)
|
159
|
+
else
|
160
|
+
body.each{|s| send_data(s)}
|
161
|
+
end
|
162
|
+
|
163
|
+
consider_keep_alive(try_keep_alive)
|
164
|
+
end
|
165
|
+
|
166
|
+
# call this when you fully send response
|
167
|
+
def consider_keep_alive(try_keep_alive = true)
|
168
|
+
if @keep_alive && try_keep_alive
|
169
|
+
post_init
|
170
|
+
else
|
171
|
+
close_connection_after_writing
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def unbind # :nodoc:
|
176
|
+
@unbind_callback && @unbind_callback.call(self)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/thin/fun_embed/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Sokolov Yura 'funny-falcon'"]
|
6
|
+
gem.email = ["funny.falcon@gmail.com"]
|
7
|
+
gem.description = %q{trim of Thin web server for embedding into eventmachined application}
|
8
|
+
gem.summary = "Subclass of EM::Connection which uses thin internals to do http request handling.\n"\
|
9
|
+
"It is intentionally not Rack server, but could be used to build some."
|
10
|
+
|
11
|
+
gem.homepage = "https://github.com/funny-falcon/thin-fun_embed"
|
12
|
+
|
13
|
+
gem.files = Dir["examples/*"] + Dir["lib/**/*.rb"] +
|
14
|
+
%w{Gemfile LICENSE Rakefile README.rdoc thin-fun_embed.gemspec}
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.name = "thin-fun_embed"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Thin::FunEmbed::VERSION
|
19
|
+
|
20
|
+
gem.add_dependency 'thin', ['>= 1.4']
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: thin-fun_embed
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Sokolov Yura 'funny-falcon'
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thin
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.4'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.4'
|
30
|
+
description: trim of Thin web server for embedding into eventmachined application
|
31
|
+
email:
|
32
|
+
- funny.falcon@gmail.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- examples/simple_200_ok.rb
|
38
|
+
- examples/rack_like.rb
|
39
|
+
- examples/status_body.rb
|
40
|
+
- examples/config.ru
|
41
|
+
- lib/thin/fun_embed/constants.rb
|
42
|
+
- lib/thin/fun_embed/version.rb
|
43
|
+
- lib/thin/fun_embed.rb
|
44
|
+
- Gemfile
|
45
|
+
- LICENSE
|
46
|
+
- Rakefile
|
47
|
+
- README.rdoc
|
48
|
+
- thin-fun_embed.gemspec
|
49
|
+
homepage: https://github.com/funny-falcon/thin-fun_embed
|
50
|
+
licenses: []
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '0'
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.8.24
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Subclass of EM::Connection which uses thin internals to do http request handling.
|
73
|
+
It is intentionally not Rack server, but could be used to build some.
|
74
|
+
test_files: []
|