aspirin 0.0.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/example/mong.rb +84 -0
- data/example/test.rb +59 -0
- data/example/thn.rb +87 -0
- data/ext/aspirin.c +566 -0
- data/ext/extconf.rb +7 -0
- data/spec/aspirin_spec.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +90 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 fistfvck
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= aspirin
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 fistfvck. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "aspirin"
|
8
|
+
gem.summary = %Q{evhttp wrapper and Rack interface}
|
9
|
+
gem.description = %Q{evhttp wrapper and Rack interface}
|
10
|
+
gem.email = "fistfvck@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/fistfvck/aspirin"
|
12
|
+
gem.authors = ["fistfvck"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'spec/rake/spectask'
|
22
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
23
|
+
spec.libs << 'lib' << 'spec'
|
24
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
30
|
+
spec.rcov = true
|
31
|
+
end
|
32
|
+
|
33
|
+
task :spec => :check_dependencies
|
34
|
+
|
35
|
+
task :default => :spec
|
36
|
+
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
Rake::RDocTask.new do |rdoc|
|
39
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
40
|
+
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = "aspirin #{version}"
|
43
|
+
rdoc.rdoc_files.include('README*')
|
44
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
data/example/mong.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rack'
|
4
|
+
|
5
|
+
class HelloApp
|
6
|
+
def call(env)
|
7
|
+
# env.keys.sort.each do |k|
|
8
|
+
# v = env[k]
|
9
|
+
# puts "#{k.inspect} => #{v.inspect}"
|
10
|
+
# end
|
11
|
+
[200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
Rack::Handler::Mongrel.run HelloApp.new
|
15
|
+
__END__
|
16
|
+
∴ curl "localhost:8080/path/to/file?arg1=1&arg2=2"
|
17
|
+
"GATEWAY_INTERFACE" => "CGI/1.2"
|
18
|
+
"HTTP_ACCEPT" => "*/*"
|
19
|
+
"HTTP_HOST" => "localhost:8080"
|
20
|
+
"HTTP_USER_AGENT" => "curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15"
|
21
|
+
"HTTP_VERSION" => "HTTP/1.1"
|
22
|
+
"PATH_INFO" => "/path/to/file"
|
23
|
+
"QUERY_STRING" => "arg1=1&arg2=2"
|
24
|
+
"REMOTE_ADDR" => "127.0.0.1"
|
25
|
+
"REQUEST_METHOD" => "GET"
|
26
|
+
"REQUEST_PATH" => "/path/to/file"
|
27
|
+
"REQUEST_URI" => "/path/to/file?arg1=1&arg2=2"
|
28
|
+
"SCRIPT_NAME" => ""
|
29
|
+
"SERVER_NAME" => "localhost"
|
30
|
+
"SERVER_PORT" => "8080"
|
31
|
+
"SERVER_PROTOCOL" => "HTTP/1.1"
|
32
|
+
"SERVER_SOFTWARE" => "Mongrel 1.1.5"
|
33
|
+
"rack.errors" => #<IO:0x816a550>
|
34
|
+
"rack.input" => #<StringIO:0x8c3e440>
|
35
|
+
"rack.multiprocess" => false
|
36
|
+
"rack.multithread" => true
|
37
|
+
"rack.run_once" => false
|
38
|
+
"rack.url_scheme" => "http"
|
39
|
+
"rack.version" => [1, 1]
|
40
|
+
|
41
|
+
∴ ab -n 10000 -c 100 http://127.0.0.1:8080/ 2>/dev/null
|
42
|
+
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
|
43
|
+
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
44
|
+
Licensed to The Apache Software Foundation, http://www.apache.org/
|
45
|
+
|
46
|
+
Benchmarking 127.0.0.1 (be patient)
|
47
|
+
|
48
|
+
|
49
|
+
Server Software:
|
50
|
+
Server Hostname: 127.0.0.1
|
51
|
+
Server Port: 8080
|
52
|
+
|
53
|
+
Document Path: /
|
54
|
+
Document Length: 11 bytes
|
55
|
+
|
56
|
+
Concurrency Level: 100
|
57
|
+
Time taken for tests: 10.356 seconds
|
58
|
+
Complete requests: 10000
|
59
|
+
Failed requests: 0
|
60
|
+
Write errors: 0
|
61
|
+
Total transferred: 1320000 bytes
|
62
|
+
HTML transferred: 110000 bytes
|
63
|
+
Requests per second: 965.64 [#/sec] (mean)
|
64
|
+
Time per request: 103.558 [ms] (mean)
|
65
|
+
Time per request: 1.036 [ms] (mean, across all concurrent requests)
|
66
|
+
Transfer rate: 124.48 [Kbytes/sec] received
|
67
|
+
|
68
|
+
Connection Times (ms)
|
69
|
+
min mean[+/-sd] median max
|
70
|
+
Connect: 0 0 0.7 0 10
|
71
|
+
Processing: 28 103 12.7 98 138
|
72
|
+
Waiting: 28 103 12.7 98 138
|
73
|
+
Total: 37 103 12.5 98 138
|
74
|
+
|
75
|
+
Percentage of the requests served within a certain time (ms)
|
76
|
+
50% 98
|
77
|
+
66% 102
|
78
|
+
75% 116
|
79
|
+
80% 118
|
80
|
+
90% 121
|
81
|
+
95% 124
|
82
|
+
98% 131
|
83
|
+
99% 133
|
84
|
+
100% 138 (longest request)
|
data/example/test.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require "#{File.dirname __FILE__}/../ext/aspirin"
|
3
|
+
|
4
|
+
class HelloApp
|
5
|
+
def call(env)
|
6
|
+
# env.keys.sort.each do |k|
|
7
|
+
# v = env[k]
|
8
|
+
# $stderr.puts "#{k.inspect} => #{v.inspect}"
|
9
|
+
# end
|
10
|
+
[200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
Rack::Handler::Aspirin.run HelloApp.new
|
14
|
+
__END__
|
15
|
+
∴ curl "localhost:9292/path/to/file?arg1=1&arg2=2"
|
16
|
+
∴ ab -n 10000 -c 100 http://127.0.0.1:9292/ 2>/dev/null
|
17
|
+
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
|
18
|
+
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
19
|
+
Licensed to The Apache Software Foundation, http://www.apache.org/
|
20
|
+
|
21
|
+
Benchmarking 127.0.0.1 (be patient)
|
22
|
+
|
23
|
+
|
24
|
+
Server Software:
|
25
|
+
Server Hostname: 127.0.0.1
|
26
|
+
Server Port: 9292
|
27
|
+
|
28
|
+
Document Path: /
|
29
|
+
Document Length: 11 bytes
|
30
|
+
|
31
|
+
Concurrency Level: 100
|
32
|
+
Time taken for tests: 3.184 seconds
|
33
|
+
Complete requests: 10000
|
34
|
+
Failed requests: 0
|
35
|
+
Write errors: 0
|
36
|
+
Total transferred: 750000 bytes
|
37
|
+
HTML transferred: 110000 bytes
|
38
|
+
Requests per second: 3141.16 [#/sec] (mean)
|
39
|
+
Time per request: 31.835 [ms] (mean)
|
40
|
+
Time per request: 0.318 [ms] (mean, across all concurrent requests)
|
41
|
+
Transfer rate: 230.07 [Kbytes/sec] received
|
42
|
+
|
43
|
+
Connection Times (ms)
|
44
|
+
min mean[+/-sd] median max
|
45
|
+
Connect: 0 0 0.7 0 9
|
46
|
+
Processing: 5 32 3.1 31 43
|
47
|
+
Waiting: 5 31 3.1 31 43
|
48
|
+
Total: 13 32 2.8 31 43
|
49
|
+
|
50
|
+
Percentage of the requests served within a certain time (ms)
|
51
|
+
50% 31
|
52
|
+
66% 32
|
53
|
+
75% 33
|
54
|
+
80% 33
|
55
|
+
90% 35
|
56
|
+
95% 36
|
57
|
+
98% 40
|
58
|
+
99% 41
|
59
|
+
100% 43 (longest request)
|
data/example/thn.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'rubygems'
|
3
|
+
require 'thin'
|
4
|
+
require 'rack'
|
5
|
+
|
6
|
+
class HelloApp
|
7
|
+
def call(env)
|
8
|
+
# env.keys.sort.each do |k|
|
9
|
+
# v = env[k]
|
10
|
+
# puts "#{k.inspect} => #{v.inspect}"
|
11
|
+
# end
|
12
|
+
[200, {"Content-Type" => "text/plain"}, ["Hello, Rack"]]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
Rack::Handler::Thin.run HelloApp.new
|
16
|
+
__END__
|
17
|
+
∴ curl "localhost:8080/path/to/file?arg1=1&arg2=2"
|
18
|
+
"GATEWAY_INTERFACE" => "CGI/1.2"
|
19
|
+
"HTTP_ACCEPT" => "*/*"
|
20
|
+
"HTTP_HOST" => "localhost:8080"
|
21
|
+
"HTTP_USER_AGENT" => "curl/7.19.7 (i486-pc-linux-gnu) libcurl/7.19.7 OpenSSL/0.9.8k zlib/1.2.3.3 libidn/1.15"
|
22
|
+
"HTTP_VERSION" => "HTTP/1.1"
|
23
|
+
"PATH_INFO" => "/path/to/file"
|
24
|
+
"QUERY_STRING" => "arg1=1&arg2=2"
|
25
|
+
"REMOTE_ADDR" => "127.0.0.1"
|
26
|
+
"REQUEST_METHOD" => "GET"
|
27
|
+
"REQUEST_PATH" => "/path/to/file"
|
28
|
+
"REQUEST_URI" => "/path/to/file?arg1=1&arg2=2"
|
29
|
+
"SCRIPT_NAME" => ""
|
30
|
+
"SERVER_NAME" => "localhost"
|
31
|
+
"SERVER_PORT" => "8080"
|
32
|
+
"SERVER_PROTOCOL" => "HTTP/1.1"
|
33
|
+
"SERVER_SOFTWARE" => "thin 1.2.7 codename No Hup"
|
34
|
+
"async.callback" => #<Method: Thin::Connection#post_process>
|
35
|
+
"async.close" => #<EventMachine::DefaultDeferrable:0x966bb5c>
|
36
|
+
"rack.errors" => #<IO:0x92f4550>
|
37
|
+
"rack.input" => #<StringIO:0x966bdb4>
|
38
|
+
"rack.multiprocess" => false
|
39
|
+
"rack.multithread" => false
|
40
|
+
"rack.run_once" => false
|
41
|
+
"rack.url_scheme" => "http"
|
42
|
+
"rack.version" => [1, 0]
|
43
|
+
|
44
|
+
∴ ab -n 10000 -c 100 http://127.0.0.1:8080/ 2>/dev/null
|
45
|
+
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
|
46
|
+
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
47
|
+
Licensed to The Apache Software Foundation, http://www.apache.org/
|
48
|
+
|
49
|
+
Benchmarking 127.0.0.1 (be patient)
|
50
|
+
|
51
|
+
|
52
|
+
Server Software: thin
|
53
|
+
Server Hostname: 127.0.0.1
|
54
|
+
Server Port: 8080
|
55
|
+
|
56
|
+
Document Path: /
|
57
|
+
Document Length: 11 bytes
|
58
|
+
|
59
|
+
Concurrency Level: 100
|
60
|
+
Time taken for tests: 6.314 seconds
|
61
|
+
Complete requests: 10000
|
62
|
+
Failed requests: 0
|
63
|
+
Write errors: 0
|
64
|
+
Total transferred: 1310000 bytes
|
65
|
+
HTML transferred: 110000 bytes
|
66
|
+
Requests per second: 1583.76 [#/sec] (mean)
|
67
|
+
Time per request: 63.141 [ms] (mean)
|
68
|
+
Time per request: 0.631 [ms] (mean, across all concurrent requests)
|
69
|
+
Transfer rate: 202.61 [Kbytes/sec] received
|
70
|
+
|
71
|
+
Connection Times (ms)
|
72
|
+
min mean[+/-sd] median max
|
73
|
+
Connect: 0 0 0.6 0 10
|
74
|
+
Processing: 19 63 23.0 56 224
|
75
|
+
Waiting: 11 58 22.0 52 215
|
76
|
+
Total: 28 63 23.0 57 224
|
77
|
+
|
78
|
+
Percentage of the requests served within a certain time (ms)
|
79
|
+
50% 57
|
80
|
+
66% 60
|
81
|
+
75% 65
|
82
|
+
80% 66
|
83
|
+
90% 71
|
84
|
+
95% 90
|
85
|
+
98% 170
|
86
|
+
99% 188
|
87
|
+
100% 224 (longest request)
|
data/ext/aspirin.c
ADDED
@@ -0,0 +1,566 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#ifdef HAVE_RUBY_ST_H
|
3
|
+
#include <ruby/st.h>
|
4
|
+
#else
|
5
|
+
#include <st.h>
|
6
|
+
#endif
|
7
|
+
#include <signal.h>
|
8
|
+
#include <sys/queue.h>
|
9
|
+
#include <stdio.h>
|
10
|
+
#include <ctype.h>
|
11
|
+
|
12
|
+
#include <event.h>
|
13
|
+
#include <evhttp.h>
|
14
|
+
#include <evutil.h>
|
15
|
+
|
16
|
+
#define INSPECT(obj) {VALUE __obj__ = rb_inspect((obj)); fprintf(stderr, "%d:%s\n", __LINE__, RSTRING_PTR(__obj__));}
|
17
|
+
|
18
|
+
static VALUE rb_mAspirin;
|
19
|
+
static VALUE rb_cAspirin_Server;
|
20
|
+
static VALUE rb_cStringIO;
|
21
|
+
static struct st_table *status_code_tbl;
|
22
|
+
static const int const status_code_int[] = {
|
23
|
+
100,101,102,200,201,202,203,204,205,206,207,208,226,300,301,302,303,304,305,306,307,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,420,421,422,423,424,425,426,500,501,502,503,504,505,506,507,508,510
|
24
|
+
};
|
25
|
+
static const char* const status_code_str[] = {
|
26
|
+
"Continue","Switching Protocols","Processing","OK","Created","Accepted","Non-Authoritative Information","No Content","Reset Content","Partial Content","Multi-Status","Already Reported","IM Used","Multiple Choices","Moved Permanently","Found","See Other","Not Modified","Use Proxy","","Temporary Redirect","Bad Request","Unauthorized","Payment Required","Forbidden","Not Found","Method Not Allowed","Not Acceptable","Proxy Authentication Required","Request Timeout","Conflict","Gone","Length Required","Precondition Failed","Request Entity Too Large","Request-URI Too Long","Unsupported Media Type","Requested Range Not Satisfiable","Expectation Failed","","","Unprocessable Entity","Locked","Failed Dependency","","Upgrade Required","Internal Server Error","Not Implemented","Bad Gateway","Service Unavailable","Gateway Timeout","HTTP Version Not Supported","Variant Also Negotiates","Insufficient Storage","Loop Detected","Not Extended"
|
27
|
+
};
|
28
|
+
static VALUE default_env;
|
29
|
+
|
30
|
+
typedef struct
|
31
|
+
{
|
32
|
+
struct event_base* base;
|
33
|
+
struct evhttp* http;
|
34
|
+
struct event signal;
|
35
|
+
VALUE app;
|
36
|
+
VALUE options;
|
37
|
+
VALUE env;
|
38
|
+
} Aspirin_Server;
|
39
|
+
|
40
|
+
// Aspirin::Server.new
|
41
|
+
static VALUE aspirin_server_initialize(VALUE, VALUE, VALUE);
|
42
|
+
// Aspirin::Server.start
|
43
|
+
static VALUE aspirin_server_start(VALUE);
|
44
|
+
// Rack::Handler::Aspirin.run
|
45
|
+
static VALUE rack_handler_aspirin_run(int, VALUE*, VALUE);
|
46
|
+
|
47
|
+
static VALUE aspirin_server_address(VALUE);
|
48
|
+
static int aspirin_server_port(VALUE);
|
49
|
+
|
50
|
+
static VALUE aspirin_server_alloc(VALUE);
|
51
|
+
static void aspirin_server_mark(Aspirin_Server*);
|
52
|
+
static void aspirin_server_free(Aspirin_Server*);
|
53
|
+
|
54
|
+
static void aspirin_server_http_request(struct evhttp_request*, void*);
|
55
|
+
static VALUE aspirin_server_create_env(struct evhttp_request*, Aspirin_Server*);
|
56
|
+
|
57
|
+
static void aspirin_server_base_initialize(Aspirin_Server*);
|
58
|
+
static void aspirin_server_signal_initialize(Aspirin_Server*);
|
59
|
+
static void aspirin_server_sigint(int, short, void*);
|
60
|
+
static void aspirin_server_http_initialize(Aspirin_Server*);
|
61
|
+
|
62
|
+
static void set_http_version(VALUE, struct evhttp_request*);
|
63
|
+
static void set_path_info(VALUE, struct evhttp_request*);
|
64
|
+
static void set_response_header(struct evhttp_request*, VALUE);
|
65
|
+
static void set_additional_header(struct evhttp_request*);
|
66
|
+
|
67
|
+
static void init_default_env();
|
68
|
+
static VALUE dupe_default_env();
|
69
|
+
static void init_status_code_tbl();
|
70
|
+
static char* get_status_code_message(int);
|
71
|
+
|
72
|
+
static char* upcase(char*);
|
73
|
+
static char* hyphen_to_under(char*);
|
74
|
+
|
75
|
+
static void
|
76
|
+
init_default_env()
|
77
|
+
{
|
78
|
+
VALUE rack_url_scheme, rack_version, gateway_interface, server_protocol, script_name;
|
79
|
+
|
80
|
+
rack_url_scheme = rb_str_new2("http");
|
81
|
+
rack_version = rb_ary_new3(2, INT2FIX(1), INT2FIX(1));
|
82
|
+
gateway_interface = rb_str_new2("CGI/1.2");
|
83
|
+
server_protocol = rb_str_new2("HTTP/1.1");
|
84
|
+
script_name = rb_str_new2("");
|
85
|
+
|
86
|
+
OBJ_FREEZE(rack_url_scheme);
|
87
|
+
OBJ_FREEZE(rack_version);
|
88
|
+
OBJ_FREEZE(gateway_interface);
|
89
|
+
OBJ_FREEZE(server_protocol);
|
90
|
+
OBJ_FREEZE(script_name);
|
91
|
+
|
92
|
+
default_env = rb_hash_new();
|
93
|
+
|
94
|
+
rb_hash_aset(default_env, rb_str_new2("rack.multiprocess"), Qfalse);
|
95
|
+
rb_hash_aset(default_env, rb_str_new2("rack.multithread"), Qfalse);
|
96
|
+
rb_hash_aset(default_env, rb_str_new2("rack.run_once"), Qfalse);
|
97
|
+
rb_hash_aset(default_env, rb_str_new2("rack.url_scheme"), rack_url_scheme);
|
98
|
+
rb_hash_aset(default_env, rb_str_new2("rack.version"), rack_version);
|
99
|
+
rb_hash_aset(default_env, rb_str_new2("GATEWAY_INTERFACE"), gateway_interface);
|
100
|
+
rb_hash_aset(default_env, rb_str_new2("SCRIPT_NAME"), script_name);
|
101
|
+
rb_hash_aset(default_env, rb_str_new2("SERVER_PROTOCOL"), server_protocol);
|
102
|
+
}
|
103
|
+
|
104
|
+
static VALUE
|
105
|
+
dupe_default_env()
|
106
|
+
{
|
107
|
+
return rb_funcall(default_env, rb_intern("dup"), 0);
|
108
|
+
}
|
109
|
+
|
110
|
+
static void
|
111
|
+
init_status_code_tbl()
|
112
|
+
{
|
113
|
+
int i, n = sizeof(status_code_int)/sizeof(int);
|
114
|
+
status_code_tbl = st_init_numtable();
|
115
|
+
for(i=0; i<n; i++)
|
116
|
+
{
|
117
|
+
st_add_direct(status_code_tbl, (st_data_t)status_code_int[i], (st_data_t)status_code_str[i]);
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
static char*
|
122
|
+
get_status_code_message(int status_code)
|
123
|
+
{
|
124
|
+
char* result = NULL;
|
125
|
+
st_lookup(status_code_tbl, (st_data_t)status_code, (st_data_t*)&result);
|
126
|
+
return result;
|
127
|
+
}
|
128
|
+
|
129
|
+
static void
|
130
|
+
set_http_version(VALUE env, struct evhttp_request *req)
|
131
|
+
{
|
132
|
+
char buf[8 + 1]; // HTTP/x.y
|
133
|
+
snprintf(buf, 9, "HTTP/%d.%d", req->major, req->minor);
|
134
|
+
rb_hash_aset(env, rb_str_new2("HTTP_VERSION"), rb_str_new2(buf));
|
135
|
+
}
|
136
|
+
|
137
|
+
static void
|
138
|
+
set_path_info(VALUE env, struct evhttp_request *req)
|
139
|
+
{
|
140
|
+
VALUE request_uri, query_string;
|
141
|
+
char *buf, *query;
|
142
|
+
int len = strlen(evhttp_request_uri(req));
|
143
|
+
|
144
|
+
buf = xmalloc(len + 1);
|
145
|
+
strcpy(buf, evhttp_request_uri(req));
|
146
|
+
|
147
|
+
query = strrchr(buf, '?');
|
148
|
+
if(query != NULL)
|
149
|
+
{
|
150
|
+
*query++ = '\0';
|
151
|
+
}
|
152
|
+
else
|
153
|
+
{
|
154
|
+
query = "";
|
155
|
+
}
|
156
|
+
|
157
|
+
request_uri = rb_str_new2(buf);
|
158
|
+
query_string = rb_str_new2(query);
|
159
|
+
|
160
|
+
OBJ_FREEZE(request_uri);
|
161
|
+
OBJ_FREEZE(query_string);
|
162
|
+
|
163
|
+
rb_hash_aset(env, rb_str_new2("PATH_INFO"), request_uri);
|
164
|
+
rb_hash_aset(env, rb_str_new2("REQUEST_PATH"), request_uri);
|
165
|
+
rb_hash_aset(env, rb_str_new2("QUERY_STRING"), query_string);
|
166
|
+
|
167
|
+
xfree(buf);
|
168
|
+
}
|
169
|
+
|
170
|
+
static void
|
171
|
+
set_http_header(VALUE env, struct evhttp_request *req)
|
172
|
+
{
|
173
|
+
char *buf;
|
174
|
+
size_t len;
|
175
|
+
struct evkeyval *header;
|
176
|
+
VALUE key, val;
|
177
|
+
|
178
|
+
TAILQ_FOREACH(header, req->input_headers, next)
|
179
|
+
{
|
180
|
+
len = strlen(header->key);
|
181
|
+
if(len > 0)
|
182
|
+
{
|
183
|
+
buf = xmalloc(5 + len + 1);
|
184
|
+
|
185
|
+
strncpy(buf, "HTTP_", 5);
|
186
|
+
strncpy(buf + 5, header->key, len);
|
187
|
+
buf[len + 5] = '\0';
|
188
|
+
hyphen_to_under(upcase(buf));
|
189
|
+
|
190
|
+
key = rb_str_new2(buf);
|
191
|
+
val = rb_str_new2(header->value);
|
192
|
+
OBJ_FREEZE(val);
|
193
|
+
rb_hash_aset(env, key, val);
|
194
|
+
|
195
|
+
xfree(buf);
|
196
|
+
}
|
197
|
+
}
|
198
|
+
}
|
199
|
+
|
200
|
+
static VALUE
|
201
|
+
aspirin_server_create_env(struct evhttp_request *req, Aspirin_Server *srv)
|
202
|
+
{
|
203
|
+
VALUE env, rack_input, strio, remote_host, request_uri, method;
|
204
|
+
|
205
|
+
env = rb_funcall(srv->env, rb_intern("dup"), 0);
|
206
|
+
|
207
|
+
rb_hash_aset(env, rb_str_new2("rack.errors"), rb_gv_get("$stderr"));
|
208
|
+
|
209
|
+
rack_input = rb_str_new((const char*)EVBUFFER_DATA(req->input_buffer),
|
210
|
+
EVBUFFER_LENGTH(req->input_buffer));
|
211
|
+
OBJ_FREEZE(rack_input);
|
212
|
+
strio = rb_funcall(rb_cStringIO, rb_intern("new"), 1, rack_input);
|
213
|
+
rb_hash_aset(env, rb_str_new2("rack.input"), strio);
|
214
|
+
|
215
|
+
remote_host = rb_str_new2(req->remote_host);
|
216
|
+
OBJ_FREEZE(remote_host);
|
217
|
+
rb_hash_aset(env, rb_str_new2("REMOTE_ADDR"), remote_host);
|
218
|
+
|
219
|
+
request_uri = rb_str_new2(evhttp_request_uri(req));
|
220
|
+
OBJ_FREEZE(request_uri);
|
221
|
+
rb_hash_aset(env, rb_str_new2("REQUEST_URI"), request_uri);
|
222
|
+
|
223
|
+
method = rb_str_new2("REQUEST_METHOD");
|
224
|
+
switch(req->type)
|
225
|
+
{
|
226
|
+
case EVHTTP_REQ_GET:
|
227
|
+
rb_hash_aset(env, method, rb_str_new2("GET"));
|
228
|
+
break;
|
229
|
+
case EVHTTP_REQ_POST:
|
230
|
+
rb_hash_aset(env, method, rb_str_new2("POST"));
|
231
|
+
break;
|
232
|
+
case EVHTTP_REQ_HEAD:
|
233
|
+
rb_hash_aset(env, method, rb_str_new2("HEAD"));
|
234
|
+
break;
|
235
|
+
}
|
236
|
+
|
237
|
+
set_http_version(env, req);
|
238
|
+
set_path_info(env, req);
|
239
|
+
set_http_header(env, req);
|
240
|
+
|
241
|
+
return env;
|
242
|
+
}
|
243
|
+
|
244
|
+
static void
|
245
|
+
set_response_header(struct evhttp_request *req, VALUE headers)
|
246
|
+
{
|
247
|
+
VALUE header_keys, key, val;
|
248
|
+
int i, n;
|
249
|
+
|
250
|
+
header_keys = rb_funcall(headers, rb_intern("keys"), 0);
|
251
|
+
|
252
|
+
n = RARRAY_LEN(header_keys);
|
253
|
+
for(i=0; i<n; i++)
|
254
|
+
{
|
255
|
+
key = rb_ary_entry(header_keys, i);
|
256
|
+
val = rb_hash_aref(headers, key);
|
257
|
+
StringValue(key);
|
258
|
+
StringValue(val);
|
259
|
+
if(RSTRING_LEN(key) > 0 && RSTRING_LEN(val) > 0)
|
260
|
+
{
|
261
|
+
evhttp_remove_header(req->output_headers, StringValueCStr(key));
|
262
|
+
evhttp_add_header(req->output_headers, StringValueCStr(key), StringValueCStr(val));
|
263
|
+
}
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
267
|
+
static void
|
268
|
+
set_additional_header(struct evhttp_request *req)
|
269
|
+
{
|
270
|
+
evhttp_remove_header(req->output_headers, "Connection");
|
271
|
+
evhttp_add_header(req->output_headers, "Connection", "close");
|
272
|
+
}
|
273
|
+
|
274
|
+
static void
|
275
|
+
aspirin_server_http_request(struct evhttp_request *req, void *arg)
|
276
|
+
{
|
277
|
+
static VALUE args[][1] = {{INT2FIX(0)},{INT2FIX(1)},{INT2FIX(2)}};
|
278
|
+
struct evbuffer *buf;
|
279
|
+
Aspirin_Server *srv;
|
280
|
+
VALUE env, result, bodies;
|
281
|
+
int status_code;
|
282
|
+
char *status_code_msg;
|
283
|
+
|
284
|
+
srv = arg;
|
285
|
+
env = aspirin_server_create_env(req, srv);
|
286
|
+
result = rb_funcall(srv->app, rb_intern("call"), 1, env);
|
287
|
+
|
288
|
+
status_code = NUM2INT(rb_ary_aref(1, args[0], result));
|
289
|
+
status_code_msg = get_status_code_message(status_code);
|
290
|
+
if(status_code_msg == NULL)
|
291
|
+
{
|
292
|
+
status_code_msg = "";
|
293
|
+
}
|
294
|
+
|
295
|
+
set_response_header(req, rb_ary_aref(1, args[1], result));
|
296
|
+
set_additional_header(req);
|
297
|
+
|
298
|
+
bodies = rb_funcall(rb_ary_aref(1, args[2], result), rb_intern("to_s"), 0);
|
299
|
+
|
300
|
+
buf = evbuffer_new();
|
301
|
+
evbuffer_add(buf, RSTRING_PTR(bodies), RSTRING_LEN(bodies));
|
302
|
+
evhttp_send_reply(req, status_code, status_code_msg, buf);
|
303
|
+
evbuffer_free(buf);
|
304
|
+
}
|
305
|
+
|
306
|
+
static Aspirin_Server*
|
307
|
+
check_aspirin_server(VALUE obj)
|
308
|
+
{
|
309
|
+
Check_Type(obj, T_DATA);
|
310
|
+
return DATA_PTR(obj);
|
311
|
+
}
|
312
|
+
|
313
|
+
static Aspirin_Server*
|
314
|
+
alloc_aspirin_server()
|
315
|
+
{
|
316
|
+
Aspirin_Server *srv = ALLOC(Aspirin_Server);
|
317
|
+
memset(srv, 0, sizeof(Aspirin_Server));
|
318
|
+
return srv;
|
319
|
+
}
|
320
|
+
|
321
|
+
|
322
|
+
static VALUE
|
323
|
+
aspirin_server_initialize(VALUE obj, VALUE app, VALUE options)
|
324
|
+
{
|
325
|
+
Aspirin_Server *srv = check_aspirin_server(obj);
|
326
|
+
|
327
|
+
if(!srv)
|
328
|
+
{
|
329
|
+
DATA_PTR(obj) = srv = alloc_aspirin_server();
|
330
|
+
}
|
331
|
+
|
332
|
+
srv->app = app;
|
333
|
+
srv->options = options;
|
334
|
+
srv->env = dupe_default_env();
|
335
|
+
|
336
|
+
aspirin_server_base_initialize(srv);
|
337
|
+
aspirin_server_signal_initialize(srv);
|
338
|
+
aspirin_server_http_initialize(srv);
|
339
|
+
|
340
|
+
return obj;
|
341
|
+
}
|
342
|
+
|
343
|
+
static VALUE
|
344
|
+
aspirin_server_start(VALUE obj)
|
345
|
+
{
|
346
|
+
Aspirin_Server *srv = DATA_PTR(obj);
|
347
|
+
event_base_loop(srv->base, 0);
|
348
|
+
return Qnil;
|
349
|
+
}
|
350
|
+
|
351
|
+
static void
|
352
|
+
aspirin_server_mark(Aspirin_Server* srv)
|
353
|
+
{
|
354
|
+
if(!srv)
|
355
|
+
{
|
356
|
+
return;
|
357
|
+
}
|
358
|
+
rb_gc_mark(srv->app);
|
359
|
+
rb_gc_mark(srv->options);
|
360
|
+
rb_gc_mark(srv->env);
|
361
|
+
}
|
362
|
+
|
363
|
+
static void
|
364
|
+
aspirin_server_free(Aspirin_Server* srv)
|
365
|
+
{
|
366
|
+
if(!srv)
|
367
|
+
{
|
368
|
+
return;
|
369
|
+
}
|
370
|
+
|
371
|
+
event_del(&srv->signal);
|
372
|
+
|
373
|
+
if(srv->http)
|
374
|
+
{
|
375
|
+
evhttp_free(srv->http);
|
376
|
+
srv->http = NULL;
|
377
|
+
}
|
378
|
+
|
379
|
+
if(srv->base)
|
380
|
+
{
|
381
|
+
event_base_loopbreak(srv->base);
|
382
|
+
event_base_free(srv->base);
|
383
|
+
srv->base = NULL;
|
384
|
+
}
|
385
|
+
|
386
|
+
xfree(srv);
|
387
|
+
}
|
388
|
+
|
389
|
+
static VALUE
|
390
|
+
aspirin_server_alloc(VALUE klass)
|
391
|
+
{
|
392
|
+
return Data_Wrap_Struct(klass, aspirin_server_mark, aspirin_server_free, 0);
|
393
|
+
}
|
394
|
+
|
395
|
+
static void
|
396
|
+
aspirin_server_sigint(int fd, short event, void *arg)
|
397
|
+
{
|
398
|
+
Aspirin_Server *srv = arg;
|
399
|
+
struct timeval delay = {1, 0};
|
400
|
+
event_del(&srv->signal);
|
401
|
+
event_base_loopexit(srv->base, &delay);
|
402
|
+
}
|
403
|
+
|
404
|
+
static int
|
405
|
+
aspirin_server_port(VALUE options)
|
406
|
+
{
|
407
|
+
VALUE port = Qnil;
|
408
|
+
if(TYPE(options) == T_HASH)
|
409
|
+
{
|
410
|
+
port = rb_hash_aref(options, ID2SYM(rb_intern("port")));
|
411
|
+
if(!NIL_P(port)){
|
412
|
+
return NUM2INT(port);
|
413
|
+
}
|
414
|
+
port = rb_hash_aref(options, rb_str_new2("port"));
|
415
|
+
if(!NIL_P(port)){
|
416
|
+
return NUM2INT(port);
|
417
|
+
}
|
418
|
+
port = rb_hash_aref(options, ID2SYM(rb_intern("Port")));
|
419
|
+
if(!NIL_P(port)){
|
420
|
+
return NUM2INT(port);
|
421
|
+
}
|
422
|
+
port = rb_hash_aref(options, rb_str_new2("Port"));
|
423
|
+
if(!NIL_P(port)){
|
424
|
+
return NUM2INT(port);
|
425
|
+
}
|
426
|
+
port = rb_hash_aref(options, ID2SYM(rb_intern("PORT")));
|
427
|
+
if(!NIL_P(port)){
|
428
|
+
return NUM2INT(port);
|
429
|
+
}
|
430
|
+
port = rb_hash_aref(options, rb_str_new2("PORT"));
|
431
|
+
if(!NIL_P(port)){
|
432
|
+
return NUM2INT(port);
|
433
|
+
}
|
434
|
+
}
|
435
|
+
return 9292;
|
436
|
+
}
|
437
|
+
|
438
|
+
static VALUE
|
439
|
+
aspirin_server_address(VALUE options)
|
440
|
+
{
|
441
|
+
VALUE address = Qnil;
|
442
|
+
if(TYPE(options) == T_HASH)
|
443
|
+
{
|
444
|
+
address = rb_hash_aref(options, ID2SYM(rb_intern("address")));
|
445
|
+
if(!NIL_P(address)){
|
446
|
+
return address;
|
447
|
+
}
|
448
|
+
address = rb_hash_aref(options, rb_str_new2("address"));
|
449
|
+
if(!NIL_P(address)){
|
450
|
+
return address;
|
451
|
+
}
|
452
|
+
address = rb_hash_aref(options, ID2SYM(rb_intern("Address")));
|
453
|
+
if(!NIL_P(address)){
|
454
|
+
return address;
|
455
|
+
}
|
456
|
+
address = rb_hash_aref(options, rb_str_new2("Address"));
|
457
|
+
if(!NIL_P(address)){
|
458
|
+
return address;
|
459
|
+
}
|
460
|
+
address = rb_hash_aref(options, ID2SYM(rb_intern("ADDRESS")));
|
461
|
+
if(!NIL_P(address)){
|
462
|
+
return address;
|
463
|
+
}
|
464
|
+
address = rb_hash_aref(options, rb_str_new2("ADDRESS"));
|
465
|
+
if(!NIL_P(address)){
|
466
|
+
return address;
|
467
|
+
}
|
468
|
+
}
|
469
|
+
return rb_str_new2("0.0.0.0");
|
470
|
+
}
|
471
|
+
|
472
|
+
static void
|
473
|
+
aspirin_server_base_initialize(Aspirin_Server *srv)
|
474
|
+
{
|
475
|
+
srv->base = event_base_new();
|
476
|
+
}
|
477
|
+
|
478
|
+
static void
|
479
|
+
aspirin_server_signal_initialize(Aspirin_Server *srv)
|
480
|
+
{
|
481
|
+
event_set(&srv->signal, SIGINT, EV_SIGNAL|EV_PERSIST, aspirin_server_sigint, srv);
|
482
|
+
event_base_set(srv->base, &srv->signal);
|
483
|
+
event_add(&srv->signal, NULL);
|
484
|
+
}
|
485
|
+
|
486
|
+
static void
|
487
|
+
aspirin_server_http_initialize(Aspirin_Server *srv)
|
488
|
+
{
|
489
|
+
VALUE addr = aspirin_server_address(srv->options);
|
490
|
+
int port = aspirin_server_port(srv->options);
|
491
|
+
|
492
|
+
StringValue(addr);
|
493
|
+
rb_hash_aset(srv->env, rb_str_new2("SERVER_NAME"), addr);
|
494
|
+
|
495
|
+
char port_str[6];
|
496
|
+
snprintf(port_str, 5, "%d", port);
|
497
|
+
rb_hash_aset(srv->env, rb_str_new2("SERVER_PORT"), rb_str_new2(port_str));
|
498
|
+
|
499
|
+
srv->http = evhttp_new(srv->base);
|
500
|
+
evhttp_bind_socket(srv->http, RSTRING_PTR(addr), port);
|
501
|
+
evhttp_set_gencb(srv->http, aspirin_server_http_request, srv);
|
502
|
+
}
|
503
|
+
|
504
|
+
static char*
|
505
|
+
upcase(char* str)
|
506
|
+
{
|
507
|
+
if(str){
|
508
|
+
int i, n=strlen(str);
|
509
|
+
for(i=0; i<n; i++)
|
510
|
+
{
|
511
|
+
str[i] = toupper(str[i]);
|
512
|
+
}
|
513
|
+
}
|
514
|
+
return str;
|
515
|
+
}
|
516
|
+
|
517
|
+
static char*
|
518
|
+
hyphen_to_under(char* str)
|
519
|
+
{
|
520
|
+
if(str){
|
521
|
+
int i, n=strlen(str);
|
522
|
+
for(i=0; i<n; i++)
|
523
|
+
{
|
524
|
+
if(str[i] == '-')
|
525
|
+
{
|
526
|
+
str[i] = '_';
|
527
|
+
}
|
528
|
+
}
|
529
|
+
}
|
530
|
+
return str;
|
531
|
+
}
|
532
|
+
|
533
|
+
static VALUE
|
534
|
+
rack_handler_aspirin_run(int argc, VALUE *argv, VALUE obj)
|
535
|
+
{
|
536
|
+
VALUE app, options;
|
537
|
+
if(rb_scan_args(argc, argv, "11", &app, &options) == 1)
|
538
|
+
{
|
539
|
+
options = rb_hash_new();
|
540
|
+
}
|
541
|
+
aspirin_server_start(rb_funcall(rb_cAspirin_Server, rb_intern("new"), 2, app, options));
|
542
|
+
return Qnil;
|
543
|
+
}
|
544
|
+
|
545
|
+
void Init_aspirin(void)
|
546
|
+
{
|
547
|
+
init_default_env();
|
548
|
+
init_status_code_tbl();
|
549
|
+
|
550
|
+
rb_require("stringio");
|
551
|
+
rb_cStringIO = rb_const_get(rb_cObject, rb_intern("StringIO"));
|
552
|
+
|
553
|
+
rb_mAspirin = rb_define_module("Aspirin");
|
554
|
+
rb_cAspirin_Server = rb_define_class_under(rb_mAspirin, "Server", rb_cObject);
|
555
|
+
|
556
|
+
// Aspirin::Server
|
557
|
+
rb_define_alloc_func(rb_cAspirin_Server, aspirin_server_alloc);
|
558
|
+
rb_define_method(rb_cAspirin_Server, "initialize", aspirin_server_initialize, 2);
|
559
|
+
rb_define_method(rb_cAspirin_Server, "start", aspirin_server_start, 0);
|
560
|
+
|
561
|
+
// Rack::Handler::Aspirin
|
562
|
+
VALUE rb_mRack = rb_define_module("Rack");
|
563
|
+
VALUE rb_mRack_Handler = rb_define_module_under(rb_mRack, "Handler");
|
564
|
+
VALUE rb_mRack_Handler_Aspirin = rb_define_module_under(rb_mRack_Handler, "Aspirin");
|
565
|
+
rb_define_singleton_method(rb_mRack_Handler_Aspirin, "run", rack_handler_aspirin_run, -1);
|
566
|
+
}
|
data/ext/extconf.rb
ADDED
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aspirin
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 0.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- fistfvck
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-06-29 00:00:00 +09:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 2
|
30
|
+
- 9
|
31
|
+
version: 1.2.9
|
32
|
+
type: :development
|
33
|
+
version_requirements: *id001
|
34
|
+
description: evhttp wrapper and Rack interface
|
35
|
+
email: fistfvck@gmail.com
|
36
|
+
executables: []
|
37
|
+
|
38
|
+
extensions:
|
39
|
+
- ext/extconf.rb
|
40
|
+
extra_rdoc_files:
|
41
|
+
- LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .document
|
45
|
+
- .gitignore
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- VERSION
|
50
|
+
- example/mong.rb
|
51
|
+
- example/test.rb
|
52
|
+
- example/thn.rb
|
53
|
+
- ext/aspirin.c
|
54
|
+
- ext/extconf.rb
|
55
|
+
- spec/aspirin_spec.rb
|
56
|
+
- spec/spec.opts
|
57
|
+
- spec/spec_helper.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/fistfvck/aspirin
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
requirements:
|
76
|
+
- - ">="
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
version: "0"
|
81
|
+
requirements: []
|
82
|
+
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.3.6
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: evhttp wrapper and Rack interface
|
88
|
+
test_files:
|
89
|
+
- spec/aspirin_spec.rb
|
90
|
+
- spec/spec_helper.rb
|