bossan 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.o
19
+ *.so
20
+ Makefile
21
+ *.log
22
+ *.db
23
+ TAGS
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'rake'
4
+
5
+ # Specify your gem's dependencies in bossan.gemspec
6
+ gemspec
data/README.md ADDED
@@ -0,0 +1,52 @@
1
+ # Bossan
2
+
3
+ [![Build Status](https://secure.travis-ci.org/kubo39/bossan.png?branch=master)](http://travis-ci.org/kubo39/bossan)
4
+
5
+ Bossan is a high performance asynchronous ruby's rack-compliant web server.
6
+
7
+ ## Requirements
8
+
9
+ Bossan requires Ruby 1.9.2 or later.
10
+
11
+ Bossan supports Linux, FreeBSD and Mac OS X(need gcc>=4.2).
12
+
13
+ ## Installation
14
+
15
+ from rubygems
16
+
17
+ `gem install bossan`
18
+
19
+ or from source:
20
+
21
+ ```
22
+ git clone git://github.com/kubo39/bossan.git
23
+ cd bossan
24
+ rake
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ simple rack app:
30
+
31
+ ``` ruby
32
+ require 'bossan'
33
+
34
+ Bossan.run('127.0.0.1', 8000, proc {|env|
35
+ [
36
+ 200, # Status code
37
+ { # Response headers
38
+ 'Content-Type' => 'text/html',
39
+ 'Content-Length' => '13',
40
+ },
41
+ ['hello, world!'] # Response body
42
+ ]
43
+ })
44
+ ```
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rbconfig"
4
+
5
+ task :default => [:compile, :clean, :test]
6
+
7
+ task :compile do
8
+ Dir.chdir File.expand_path("../ext/bossan", __FILE__)
9
+ sh "ruby extconf.rb"
10
+ sh "make"
11
+ sh "mv bossan_ext.#{RbConfig::CONFIG['DLEXT']} ../../lib/bossan/"
12
+ end
13
+
14
+ task :clean do
15
+ Dir.chdir File.expand_path("../ext/bossan", __FILE__)
16
+ sh "rm -f *.o Makefile"
17
+ end
18
+
19
+ task :test do
20
+ Dir.chdir File.expand_path("../test", __FILE__)
21
+ sh "ruby test_rack_spec.rb"
22
+ end
data/bossan.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/bossan/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "bossan"
6
+ gem.version = Bossan::VERSION
7
+ gem.authors = ["Hiroki Noda"]
8
+ gem.email = ["kubo39@gmail.com"]
9
+ gem.description = %q{high performance asynchronous rack web server}
10
+ gem.summary = gem.description
11
+ gem.homepage = "https://github.com/kubo39/bossan"
12
+
13
+ gem.files = `git ls-files`.split($/)
14
+ gem.extensions = ["ext/bossan/extconf.rb"]
15
+ gem.require_paths = ["lib", "ext"]
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split($/)
17
+
18
+ gem.required_ruby_version = ">= 1.9.2"
19
+ gem.add_dependency "rack", ["~> 1.2"]
20
+ end
@@ -0,0 +1,19 @@
1
+ # This is a simple microblog powered by sqlite.
2
+
3
+ ### Requirements some extension
4
+
5
+ * ActiveRecord (ORM)
6
+ * eRuby (Template)
7
+ * SQLite (Database)
8
+
9
+
10
+ #### fire up a irb(pry)
11
+
12
+ require './app'; init_db
13
+
14
+
15
+ ### Run
16
+
17
+ rackup config.ru -s bossan
18
+
19
+ visit http://0.0.0.0:9292
@@ -0,0 +1,77 @@
1
+ require 'sinatra'
2
+ require 'active_record'
3
+ require 'erb'
4
+
5
+ enable :sessions
6
+
7
+ DATABASE = './db/sinatr.db'
8
+ SECRET_KEY = 'development_key'
9
+ USERNAME = 'admin'
10
+ PASSWORD = 'default'
11
+
12
+ ActiveRecord::Base.establish_connection(
13
+ :adapter => 'sqlite3',
14
+ :database => DATABASE
15
+ )
16
+
17
+ get '/' do
18
+ @entries = Entries.find(:all).map do |row|
19
+ {:title => row.title,
20
+ :text => row.text }
21
+ end
22
+ @entries ||= []
23
+ erb :show_entries
24
+ end
25
+
26
+ post '/add' do
27
+ halt(401) if session.include?([:logged_in])
28
+ Entries.create(:title => params[:title],
29
+ :text => params[:text])
30
+ redirect '/'
31
+ end
32
+
33
+ post '/login' do
34
+ @err = nil
35
+ if params[:username] != USERNAME
36
+ @err = "Invalid username #{params[:username]}"
37
+ elsif params[:password] != PASSWORD
38
+ @err = "Invalid password"
39
+ else
40
+ session[:logged_in] = params[:username]
41
+ redirect '/'
42
+ end
43
+ erb :login
44
+ end
45
+
46
+ get '/login' do
47
+ erb :login
48
+ end
49
+
50
+ get '/logout' do
51
+ session.delete :logged_in
52
+ erb :show_entries
53
+ end
54
+
55
+ class Schema < ActiveRecord::Migration
56
+ def self.up
57
+ create_table :entries do |t|
58
+ t.column :title, :string, :null => false
59
+ t.column :text, :string, :null => false
60
+ end
61
+ end
62
+
63
+ def self.down
64
+ drop_table :entries
65
+ end
66
+ end
67
+
68
+ class Entries < ActiveRecord::Base
69
+ end
70
+
71
+ def init_db
72
+ Schema.migrate :up
73
+ end
74
+
75
+ def drop_db
76
+ Schema.migrate :down
77
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require './app'
3
+
4
+ run Sinatra::Application
@@ -0,0 +1,18 @@
1
+ body { font-family: sans-serif; background: #eee; }
2
+ a, h1, h2 { color: #377BA8; }
3
+ h1, h2 { font-family: 'Georgia', serif; margin: 0; }
4
+ h1 { border-bottom: 2px solid #eee; }
5
+ h2 { font-size: 1.2em; }
6
+
7
+ .page { margin: 2em auto; width: 35em; border: 5px solid #ccc;
8
+ padding: 0.8em; background: white; }
9
+ .entries { list-style: none; margin: 0; padding: 0; }
10
+ .entries li { margin: 0.8em 1.2em; }
11
+ .entries li h2 { margin-left: -1em; }
12
+ .add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; }
13
+ .add-entry dl { font-weight: bold; }
14
+ .metanav { text-align: right; font-size: 0.8em; padding: 0.3em;
15
+ margin-bottom: 1em; background: #fafafa; }
16
+ .flash { background: #CEE5F5; padding: 0.5em;
17
+ border: 1px solid #AACBE2; }
18
+ .error { background: #F0D6D6; padding: 0.5em; }
@@ -0,0 +1,23 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Sinatr</title>
5
+ <link rel="stylesheet" type="text/css" href="/static/style.css">
6
+ </head>
7
+ <body>
8
+ <div class="page">
9
+ <h2>Login</h2>
10
+ <% if @err %>
11
+ <p class="error"><h2>Error:</h2>
12
+ <%= @err %>
13
+ <% end %>
14
+ <form action="/login" method="POST">
15
+ <h3>Username:</h3>
16
+ <input type="text" name="username">
17
+ <h3>Password:</h3>
18
+ <input type="password" name="password">
19
+ <input type="submit" value="Login">
20
+ </form>
21
+ </div>
22
+ </body>
23
+ </html>
@@ -0,0 +1,38 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>Sinatr</title>
5
+ <link rel="stylesheet" type="text/css" href="/static/style.css"/>
6
+ </head>
7
+ <body>
8
+ <div class="page">
9
+ <h1>Sinatr</h1>
10
+ <div class="metanav">
11
+ <% unless session.include?(:logged_in) %>
12
+ <a href="/login">log in</a>
13
+ <% else %>
14
+ <a href="/logout">log out</a>
15
+ <% end %>
16
+ </div>
17
+ </div>
18
+ <% if session.include?(:logged_in) %>
19
+ <form action="/add" method="POST" class="add">
20
+
21
+ <h3>Title:</h3>
22
+ <input type="text" size="30" name="title">
23
+ <h3>Text:</h3>
24
+ <textarea name="text" rows="5" cols="40"></textarea>
25
+ <input type="submit" value="Share">
26
+
27
+ </form>
28
+ <% end %>
29
+ <ul class="entries">
30
+ <% unless (@entries == [] || @entries == nil) %>
31
+ <% @entries.each do |entry| %>
32
+ <h2><%= entry[:title] %></h2>
33
+ <%= entry[:text] %>
34
+ <% end %>
35
+ <% end %>
36
+ </ul>
37
+ </body>
38
+ <html>
@@ -0,0 +1,19 @@
1
+ require 'bossan'
2
+ require 'pp'
3
+
4
+ class MyApp
5
+ def call env
6
+ body = ['hi!']
7
+ # pp env
8
+ [
9
+ 200, # Status code
10
+ { 'Content-Type' => 'text/html',
11
+ 'Content-Length' => body.join.size.to_s,
12
+ }, # Reponse headers
13
+ body # Body of the response
14
+ ]
15
+ end
16
+ end
17
+
18
+ # Rack::Handler::Bossan.run MyApp.new
19
+ run MyApp.new
data/examples/hello.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'bossan'
2
+
3
+ Bossan.run('127.0.0.1', 8000, proc {|env|
4
+ body = 'hello, world!'
5
+ [
6
+ 200, # Status code
7
+ { # Response headers
8
+ 'Content-Type' => 'text/html',
9
+ 'Content-Length' => body.size.to_s,
10
+ },
11
+ [body] # Response body
12
+ ]
13
+ })
@@ -0,0 +1,11 @@
1
+ require 'bossan'
2
+ require 'sinatra/base'
3
+
4
+ class App < Sinatra::Base
5
+ get '/' do
6
+ 'Hello world!'
7
+ end
8
+ end
9
+
10
+ Bossan.run('127.0.0.1', 8000, App)
11
+
@@ -0,0 +1,10 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title Hello, Bossan!
5
+ %body
6
+ #header
7
+ %h1 Hello, Bossan!
8
+ #content
9
+ %p
10
+ Hello, World!
@@ -0,0 +1,11 @@
1
+ require 'bossan'
2
+ require 'sinatra/base'
3
+ require 'haml'
4
+
5
+ class App < Sinatra::Base
6
+ get '/' do
7
+ haml :index
8
+ end
9
+ end
10
+
11
+ Bossan.run('127.0.0.1', 8000, App)
@@ -4,10 +4,6 @@
4
4
  #include <stddef.h>
5
5
  #include <unistd.h>
6
6
  #include <errno.h>
7
- #include <stdio.h>
8
- #include <stdlib.h>
9
- #include <string.h>
10
- #include <inttypes.h>
11
7
  #include <arpa/inet.h>
12
8
  #include <signal.h>
13
9
  #include <sys/socket.h>
@@ -24,9 +20,12 @@
24
20
  #include <netinet/in.h>
25
21
  #include <netinet/tcp.h>
26
22
  #include <netdb.h>
23
+
27
24
  #include "time_cache.h"
28
25
  #include "http_parser.h"
29
26
  #include "picoev.h"
27
+ #include "buffer.h"
28
+ #include "client.h"
30
29
 
31
30
  #define MAX_FDS 1024 * 8
32
31
  #define ACCEPT_TIMEOUT_SECS 1
@@ -38,18 +37,8 @@
38
37
  #define MAX_BUFSIZE 1024 * 8
39
38
  #define INPUT_BUF_SIZE 1024 * 8
40
39
 
41
- #define LIMIT_MAX 1024 * 1024 * 1024
42
-
43
40
  #define LIMIT_SIZE 1024 * 512
44
41
 
45
- #define LIMIT_PATH 1024 * 4
46
- #define LIMIT_FRAGMENT 1024
47
- #define LIMIT_URI 1024 * 4
48
- #define LIMIT_QUERY_STRING 1024 * 8
49
-
50
- #define LIMIT_REQUEST_FIELDS 30
51
- #define LIMIT_REQUEST_FIELD_SIZE 1024 * 8
52
-
53
42
  #define CRLF "\r\n"
54
43
  #define DELIM ": "
55
44
 
@@ -61,7 +50,7 @@
61
50
 
62
51
  #define MSG_413 ("HTTP/1.0 413 Request Entity Too Large\r\nContent-Type: text/html\r\nServer: " SERVER "\r\n\r\n<html><head><title>Request Entity Too Large</title></head><body><p>Request Entity Too Large.</p></body></html>")
63
52
 
64
- #define SERVER "bossan/0.1.6"
53
+ #define SERVER "bossan/0.1.7"
65
54
 
66
55
  VALUE server; // Bossan
67
56
 
@@ -129,74 +118,6 @@ int max_content_length = 1024 * 1024 * 16; //max_content_length
129
118
 
130
119
  static VALUE StringIO;
131
120
 
132
- typedef enum {
133
- WRITE_OK,
134
- MEMORY_ERROR,
135
- LIMIT_OVER,
136
- } buffer_result;
137
-
138
- typedef struct {
139
- char *buf;
140
- size_t buf_size;
141
- size_t len;
142
- size_t limit;
143
- } buffer;
144
-
145
- typedef enum {
146
- FIELD,
147
- VAL,
148
- } field_type;
149
-
150
- typedef struct {
151
- buffer *field;
152
- buffer *value;
153
- } header;
154
-
155
- typedef struct {
156
- buffer *path;
157
- buffer *uri;
158
- buffer *query_string;
159
- buffer *fragment;
160
- header *headers[LIMIT_REQUEST_FIELDS];
161
- uint32_t num_headers;
162
- field_type last_header_element;
163
- } request;
164
-
165
- typedef enum {
166
- BODY_TYPE_NONE,
167
- BODY_TYPE_TMPFILE,
168
- BODY_TYPE_BUFFER
169
- } request_body_type;
170
-
171
- typedef struct _client {
172
- int fd;
173
- char *remote_addr;
174
- uint32_t remote_port;
175
- uint8_t keep_alive;
176
- request *req;
177
- uint32_t body_length;
178
- int body_readed;
179
- void *body;
180
- int bad_request_code;
181
- request_body_type body_type;
182
- uint8_t complete;
183
-
184
- http_parser *http; // http req parser
185
- VALUE environ; // rack environ
186
- int status_code; // response status code
187
-
188
- VALUE http_status; // response status line
189
- VALUE headers; // http response headers
190
- uint8_t header_done; // header write status
191
- VALUE response; // rack response object
192
- VALUE response_iter; // rack response object
193
- uint8_t content_length_set; // content_length_set flag
194
- uint32_t content_length; // content_length
195
- uint32_t write_bytes; // send body length
196
- void *bucket; //write_data
197
- uint8_t response_closed; //response closed flag
198
- } client_t;
199
-
200
121
  typedef struct iovec iovec_t;
201
122
 
202
123
  typedef struct {
@@ -209,78 +130,6 @@ typedef struct {
209
130
  } write_bucket;
210
131
 
211
132
 
212
- buffer *
213
- new_buffer(size_t buf_size, size_t limit)
214
- {
215
- buffer *buf;
216
- buf = ruby_xmalloc(sizeof(buffer));
217
- memset(buf, 0, sizeof(buffer));
218
- buf->buf = ruby_xmalloc(sizeof(char) * buf_size);
219
- buf->buf_size = buf_size;
220
- if(limit){
221
- buf->limit = limit;
222
- }else{
223
- buf->limit = LIMIT_MAX;
224
- }
225
- return buf;
226
- }
227
-
228
-
229
- buffer_result
230
- write2buf(buffer *buf, const char *c, size_t l)
231
- {
232
- size_t newl;
233
- char *newbuf;
234
- buffer_result ret = WRITE_OK;
235
- newl = buf->len + l;
236
-
237
- if (newl >= buf->buf_size) {
238
- buf->buf_size *= 2;
239
- if(buf->buf_size <= newl) {
240
- buf->buf_size = (int)(newl + 1);
241
- }
242
- if(buf->buf_size > buf->limit){
243
- buf->buf_size = buf->limit + 1;
244
- }
245
- newbuf = (char*)ruby_xrealloc(buf->buf, buf->buf_size);
246
- buf->buf = newbuf;
247
- }
248
- if(newl >= buf->buf_size){
249
- l = buf->buf_size - buf->len -1;
250
- ret = LIMIT_OVER;
251
- }
252
- memcpy(buf->buf + buf->len, c , l);
253
- buf->len += (int)l;
254
- return ret;
255
- }
256
-
257
-
258
- void
259
- free_buffer(buffer *buf)
260
- {
261
- ruby_xfree(buf->buf);
262
- ruby_xfree(buf);
263
- }
264
-
265
-
266
- VALUE
267
- getRbString(buffer *buf)
268
- {
269
- VALUE o;
270
- o = rb_str_new(buf->buf, buf->len);
271
- free_buffer(buf);
272
- return o;
273
- }
274
-
275
-
276
- char *
277
- getString(buffer *buf)
278
- {
279
- buf->buf[buf->len] = '\0';
280
- return buf->buf;
281
- }
282
-
283
-
284
133
  int
285
134
  open_log_file(const char *path)
286
135
  {
@@ -858,67 +707,6 @@ response_start(client_t *client)
858
707
  }
859
708
 
860
709
 
861
- request *
862
- new_request(void)
863
- {
864
- request *req = (request *)ruby_xmalloc(sizeof(request));
865
- memset(req, 0, sizeof(request));
866
- return req;
867
- }
868
-
869
-
870
- header *
871
- new_header(size_t fsize, size_t flimit, size_t vsize, size_t vlimit)
872
- {
873
- header *h;
874
- h = ruby_xmalloc(sizeof(header));
875
- h->field = new_buffer(fsize, flimit);
876
- h->value = new_buffer(vsize, vlimit);
877
- return h;
878
- }
879
-
880
-
881
- void
882
- free_header(header *h)
883
- {
884
- ruby_xfree(h);
885
- }
886
-
887
-
888
- void
889
- free_request(request *req)
890
- {
891
- uint32_t i;
892
- header *h;
893
- if(req->path){
894
- free_buffer(req->path);
895
- req->path = NULL;
896
- }
897
- if(req->uri){
898
- free_buffer(req->uri);
899
- req->uri = NULL;
900
- }
901
- if(req->query_string){
902
- free_buffer(req->query_string);
903
- req->query_string = NULL;
904
- }
905
- if(req->fragment){
906
- free_buffer(req->fragment);
907
- req->fragment = NULL;
908
- }
909
- for(i = 0; i < req->num_headers+1; i++){
910
- h = req->headers[i];
911
- if(h){
912
- free_buffer(h->field);
913
- free_buffer(h->value);
914
- free_header(h);
915
- req->headers[i] = NULL;
916
- }
917
- }
918
- ruby_xfree(req);
919
- }
920
-
921
-
922
710
  static void
923
711
  key_upper(char *s, const char *key, size_t len)
924
712
  {
@@ -955,9 +743,10 @@ write_body2file(client_t *client, const char *buffer, size_t buffer_len)
955
743
  static int
956
744
  write_body2mem(client_t *client, const char *buffer, size_t buffer_len)
957
745
  {
746
+ VALUE obj;
958
747
  /* printf("body2mem called\n"); */
959
- VALUE obj = (VALUE)client->body;
960
- rb_str_concat(obj, rb_str_new(buffer, buffer_len));
748
+
749
+ rb_funcall((VALUE)client->body, rb_intern("write"), 1, rb_str_new(buffer, buffer_len));
961
750
  client->body_readed += buffer_len;
962
751
  #ifdef DEBUG
963
752
  printf("write_body2mem %d bytes \n", buffer_len);
@@ -1234,6 +1023,7 @@ body_cb (http_parser *p, const char *buf, size_t len, char partial)
1234
1023
  #ifdef DEBUG
1235
1024
  printf("client->body_length %d \n", client->body_length);
1236
1025
  #endif
1026
+ client->body = rb_funcall(StringIO, i_new, 1, rb_str_new2(""));
1237
1027
  client->body_type = BODY_TYPE_BUFFER;
1238
1028
  #ifdef DEBUG
1239
1029
  printf("BODY_TYPE_BUFFER \n");
@@ -1723,10 +1513,18 @@ prepare_call_rack(client_t *client)
1723
1513
  VALUE input, object, c;
1724
1514
  char *val;
1725
1515
 
1726
- object = rb_str_new2("");
1727
- input = rb_funcall(StringIO, i_new, 1, object);
1728
- rb_hash_aset(client->environ, rack_input, input);
1729
- client->body = object;
1516
+ if(client->body_type == BODY_TYPE_BUFFER) {
1517
+ /* rb_p( rb_funcall((VALUE)client->body, rb_intern("gets"), 0) ); */
1518
+ rb_p( rb_funcall((VALUE)client->body, rb_intern("seek"), 1, INT2NUM(0)) );
1519
+ rb_hash_aset(client->environ, rack_input, (VALUE)client->body);
1520
+ } else {
1521
+ object = rb_str_new2("");
1522
+ input = rb_funcall(StringIO, i_new, 1, object);
1523
+ rb_gc_register_address(&input);
1524
+ rb_hash_aset(client->environ, rack_input, input);
1525
+ /* client->body = object; */
1526
+ client->body = input;
1527
+ }
1730
1528
 
1731
1529
  if(is_keep_alive){
1732
1530
  //support keep-alive