aspirin 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
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
@@ -0,0 +1,7 @@
1
+ require "mkmf"
2
+
3
+ dir_config("event")
4
+ have_header("event.h")
5
+ have_header("evhttp.h")
6
+ have_library("event")
7
+ create_makefile("aspirin")
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Aspirin" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'aspirin'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
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