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 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