bossan 0.1.6 → 0.1.7

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