thin 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of thin might be problematic. Click here for more details.

Files changed (57) hide show
  1. data/CHANGELOG +15 -0
  2. data/README +10 -4
  3. data/benchmark/abc +0 -0
  4. data/benchmark/runner +0 -0
  5. data/bin/thin +0 -0
  6. data/example/thin_solaris_smf.erb +36 -0
  7. data/example/thin_solaris_smf.readme.txt +150 -0
  8. data/ext/thin_parser/common.rl +3 -2
  9. data/ext/thin_parser/parser.c +15 -38
  10. data/lib/rack/adapter/loader.rb +69 -0
  11. data/lib/rack/adapter/rails.rb +17 -8
  12. data/lib/thin.rb +1 -0
  13. data/lib/thin/backends/base.rb +17 -0
  14. data/lib/thin/backends/swiftiply_client.rb +3 -2
  15. data/lib/thin/backends/tcp_server.rb +1 -1
  16. data/lib/thin/connection.rb +38 -6
  17. data/lib/thin/controllers/controller.rb +43 -17
  18. data/lib/thin/logging.rb +1 -1
  19. data/lib/thin/request.rb +8 -1
  20. data/lib/thin/runner.rb +29 -7
  21. data/lib/thin/server.rb +58 -28
  22. data/lib/thin/version.rb +3 -3
  23. data/lib/thin_backend.bundle +0 -0
  24. data/lib/thin_parser.bundle +0 -0
  25. data/spec/connection_spec.rb +7 -0
  26. data/spec/controllers/controller_spec.rb +9 -1
  27. data/spec/rack/loader_spec.rb +29 -0
  28. data/spec/{rack_rails_spec.rb → rack/rails_adapter_spec.rb} +15 -3
  29. data/spec/rails_app/public/dispatch.cgi +0 -0
  30. data/spec/rails_app/public/dispatch.fcgi +0 -0
  31. data/spec/rails_app/public/dispatch.rb +0 -0
  32. data/spec/rails_app/script/about +0 -0
  33. data/spec/rails_app/script/console +0 -0
  34. data/spec/rails_app/script/destroy +0 -0
  35. data/spec/rails_app/script/generate +0 -0
  36. data/spec/rails_app/script/performance/benchmarker +0 -0
  37. data/spec/rails_app/script/performance/profiler +0 -0
  38. data/spec/rails_app/script/performance/request +0 -0
  39. data/spec/rails_app/script/plugin +0 -0
  40. data/spec/rails_app/script/process/inspector +0 -0
  41. data/spec/rails_app/script/process/reaper +0 -0
  42. data/spec/rails_app/script/process/spawner +0 -0
  43. data/spec/rails_app/script/runner +0 -0
  44. data/spec/rails_app/script/server +0 -0
  45. data/spec/request/parser_spec.rb +22 -0
  46. data/spec/request/processing_spec.rb +9 -10
  47. data/spec/runner_spec.rb +14 -1
  48. data/spec/server/builder_spec.rb +3 -2
  49. data/spec/server/robustness_spec.rb +34 -0
  50. data/spec/server/swiftiply_spec.rb +1 -1
  51. data/spec/server/threaded_spec.rb +27 -0
  52. data/spec/server_spec.rb +69 -0
  53. data/spec/spec_helper.rb +10 -5
  54. data/tasks/gem.rake +2 -2
  55. data/tasks/spec.rake +24 -16
  56. metadata +13 -5
  57. data/doc/benchmarks.txt +0 -86
data/CHANGELOG CHANGED
@@ -1,3 +1,18 @@
1
+ == 0.8.0 Dodgy Dentist release
2
+ * Fix server crash when header too large.
3
+ * Add --require (-r) option to require a library, before executing your script.
4
+ * Rename --rackup short option to -R, warn and load as rackup when file ends with .ru.
5
+ * List supported adapters in command usage.
6
+ * Add file adapter to built-in adapter, serve static files in current directory.
7
+ * Allow disabling signal handling in Server with :signals => false
8
+ * Make Server.new arguments more flexible, can now specify any of host, port, app or hash options.
9
+ * Add --backend option to specified which backend to use, closes #55
10
+ * Serve static file only on GET and HEAD requests in Rails adapter, fixes #58
11
+ * Add threaded option to run server in threaded mode, calling the application in a
12
+ thread allowing for concurrency in the Rack adapter, closes #46
13
+ * Guess which adapter to use from directory (chdir option)
14
+ or use specified one in 'adapter' option, re #47.
15
+
1
16
  == 0.7.1 Fancy Pants release
2
17
  * Clean stale PID files when starting as daemon, fixes #53 [Chu Yeow]
3
18
  * Require EventMachine 0.11.0 for UNIX domain sockets. Until it's released, install from:
data/README CHANGED
@@ -9,11 +9,13 @@ Thin is a Ruby web server that glues together 3 of the best Ruby libraries in we
9
9
  Which makes it, with all humility, the most secure, stable, fast and extensible Ruby web server
10
10
  bundled in an easy to use gem for your own pleasure.
11
11
 
12
- === Installation
13
- IMPORTANT: Until EventMachine 0.11.0 is out, you have to install it from trunk or from my gem server:
14
-
15
- sudo gem install eventmachine --source http://code.macournoyer.com
12
+ Site: http://code.macournoyer.com/thin/
13
+ Group: http://groups.google.com/group/thin-ruby/topics
14
+ Bugs: http://thin.lighthouseapp.com/projects/7212-thin
15
+ Code: http://github.com/macournoyer/thin
16
+ IRC: #thin on freenode
16
17
 
18
+ === Installation
17
19
  For the latest stable version:
18
20
 
19
21
  sudo gem install thin
@@ -28,6 +30,10 @@ Or from source:
28
30
  cd thin
29
31
  rake install
30
32
 
33
+ To use Thin with UNIX domain sockets you need EventMachine 0.11.0 from my gem server:
34
+
35
+ gem install eventmachine --source http://code.macournoyer.com
36
+
31
37
  === Usage
32
38
  A +thin+ script offers an easy way to start your Rails application:
33
39
 
data/benchmark/abc CHANGED
File without changes
data/benchmark/runner CHANGED
File without changes
data/bin/thin CHANGED
File without changes
@@ -0,0 +1,36 @@
1
+ <?xml version='1.0'?>
2
+ <!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
3
+ <service_bundle type='manifest' name='thin/<%= service_name %>-production'>
4
+ <service name='network/thin/<%= service_name %>-production' type='service' version='0'>
5
+ <!-- Dependancies for all Thin servers. -->
6
+ <dependency name='fs' grouping='require_all' restart_on='none' type='service'>
7
+ <service_fmri value='svc:/system/filesystem/local'/>
8
+ </dependency>
9
+ <dependency name='net' grouping='require_all' restart_on='none' type='service'>
10
+ <service_fmri value='svc:/network/loopback'/>
11
+ <!-- uncomment the following line if you are on an L+ Accelerator since /home is mounted through nfs -->
12
+ <!--<service_fmri value='svc:/network/nfs/client'/>-->
13
+ </dependency>
14
+ <% 0.upto(thin_max_instances - 1) do |instance| %>
15
+ <!-- instance names can't start with digits. Bummer. -->
16
+ <instance name='i_<%= instance.to_s %>' enabled='false'>
17
+ <!-- Cause the multi-user milestone to bring these services up -->
18
+ <dependent name='<%= service_name %>_<%= instance.to_s %>_multi-user' restart_on='none' grouping='optional_all'>
19
+ <service_fmri value='svc:/milestone/multi-user'/>
20
+ </dependent>
21
+ <exec_method name='start' type='method'
22
+ exec='/opt/csw/bin/thin -C config/thin.yml --only <%= instance.to_s %> start'
23
+ timeout_seconds='10'>
24
+ <method_context working_directory='<%= working_directory %>'>
25
+ <method_credential user='<%= user %>' group='<%= group %>' />
26
+ <method_environment>
27
+ <envvar name='PATH' value='/usr/bin:/bin:/opt/csw/bin' />
28
+ </method_environment>
29
+ </method_context>
30
+ </exec_method>
31
+ <exec_method name='stop' type='method' exec=':kill' timeout_seconds='10' />
32
+ </instance>
33
+ <% end %>
34
+ </service>
35
+ </service_bundle>
36
+
@@ -0,0 +1,150 @@
1
+ Using Thin with Solaris' SMF Monitoring Framework
2
+ - - - - - - - - - - - - - - - - - - - - - - - - -
3
+
4
+ Solaris uses the Service Management Framework (SMF) at the OS level to manage, monitor, and restart long running processes. This replaces init scripts, and tools like monit and god.
5
+
6
+ The sample XML file (thin_solaris_smf.erb) is an example SMF manifest which I use on a Joyent accelerator which runs on OpenSolaris.
7
+
8
+ This setup will:
9
+
10
+ - ensure the right dependencies are loaded
11
+ - start n instances of Thin, and monitor each individually. If any single one dies it will be restarted instantly (test it by killing a single thin instance and it will be back alive before you can type 'ps -ef').
12
+
13
+ This is better than using clustering since if you start the cluster with SMF it will only notice a problem and restart individual Thin's if ALL of them are dead, at which point it will restart the whole cluster. This approach makes sure that all of your Thins start together and are monitored and managed independant of each other. This problem likely exists if you are using god or monit to monitor only the start of the master cluster, and don't then monitor the individual processes started.
14
+
15
+ This example is in .erb format instead of plain XML since I dynamically generate this file as part of a Capistrano deployment. In my deploy.rb file I define the variables found in this erb. Of course you don't need to use this with Capistrano. Just replace the few ERB variables from the xml file, change its extension, and load that directly in Solaris if you prefer.
16
+
17
+ Here are some examples for usage to get you started with Capistrano, and Thin:
18
+
19
+ FILE : config/deploy.rb
20
+ --
21
+
22
+ require 'config/accelerator/accelerator_tasks'
23
+
24
+ set :application, "yourapp"
25
+ set :svcadm_bin, "/usr/sbin/svcadm"
26
+ set :svccfg_bin, "/usr/sbin/svccfg"
27
+ set :svcs_bin, "/usr/bin/svcs"
28
+
29
+ # gets the list of remote service SMF names that we need to start
30
+ # like (depending on thin_max_instances settings):
31
+ # svc:/network/thin/yourapp-production:i_0
32
+ # svc:/network/thin/yourapp-production:i_1
33
+ # svc:/network/thin/yourapp-production:i_2
34
+ set :service_list, "`svcs -H -o FMRI svc:network/thin/#{application}-production`"
35
+
36
+ # how many Thin instances should be setup to run?
37
+ # this affects the generated thin smf file, and the nginx vhost conf
38
+ # need to re-run setup for thin smf and nginx vhost conf when changed
39
+ set :thin_max_instances, 3
40
+
41
+ # OVERRIDE STANDARD TASKS
42
+ desc "Restart the entire application"
43
+ deploy.task :restart do
44
+ accelerator.thin.restart
45
+ accelerator.nginx.restart
46
+ end
47
+
48
+ desc "Start the entire application"
49
+ deploy.task :start do
50
+ accelerator.thin.restart
51
+ accelerator.nginx.restart
52
+ end
53
+
54
+ desc "Stop the entire application"
55
+ deploy.task :stop do
56
+ accelerator.thin.disable
57
+ accelerator.nginx.disable
58
+ end
59
+
60
+
61
+ FILE : config/accelerator/accelerator_tasks.rb
62
+ --
63
+
64
+ desc "Create and deploy Thin SMF config"
65
+ task :create_thin_smf, :roles => :app do
66
+ service_name = application
67
+ working_directory = current_path
68
+ template = File.read("config/accelerator/thin_solaris_smf.erb")
69
+ buffer = ERB.new(template).result(binding)
70
+ put buffer, "#{shared_path}/#{application}-thin-smf.xml"
71
+ sudo "#{svccfg_bin} import #{shared_path}/#{application}-thin-smf.xml"
72
+ end
73
+
74
+ desc "Delete Thin SMF config"
75
+ task :delete_thin_smf, :roles => :app do
76
+ accelerator.thin.disable
77
+ sudo "#{svccfg_bin} delete /network/thin/#{application}-production"
78
+ end
79
+
80
+ desc "Show all SMF services"
81
+ task :svcs do
82
+ run "#{svcs_bin} -a" do |ch, st, data|
83
+ puts data
84
+ end
85
+ end
86
+
87
+ desc "Shows all non-functional SMF services"
88
+ task :svcs_broken do
89
+ run "#{svcs_bin} -vx" do |ch, st, data|
90
+ puts data
91
+ end
92
+ end
93
+
94
+
95
+ namespace :thin do
96
+
97
+ desc "Disable all Thin servers"
98
+ task :disable, :roles => :app do
99
+ # temporarily disable, until next reboot (-t)
100
+ sudo "#{svcadm_bin} disable -t #{service_list}"
101
+ end
102
+
103
+ desc "Enable all Thin servers"
104
+ task :enable, :roles => :app do
105
+ # start the app with all recursive dependencies
106
+ sudo "#{svcadm_bin} enable -r #{service_list}"
107
+ end
108
+
109
+ desc "Restart all Thin servers"
110
+ task :restart, :roles => :app do
111
+ # svcadm restart doesn't seem to work right, so we'll brute force it
112
+ disable
113
+ enable
114
+ end
115
+
116
+ end # namespace thin
117
+
118
+
119
+ FILE : config/thin.yml
120
+ --
121
+
122
+ ---
123
+ pid: tmp/pids/thin.pid
124
+ socket: /tmp/thin.sock
125
+ log: log/thin.log
126
+ max_conns: 1024
127
+ timeout: 30
128
+ chdir: /your/app/dir/rails/root
129
+ environment: production
130
+ max_persistent_conns: 512
131
+ daemonize: true
132
+ servers: 3
133
+
134
+
135
+ FILE : config/accelerator/thin_solaris_smf.erb
136
+ --
137
+ This is of course an example. It works for me, but YMMV
138
+
139
+ You may need to change this line to match your environment and config:
140
+ exec='/opt/csw/bin/thin -C config/thin.yml --only <%= instance.to_s %> start'
141
+
142
+
143
+ CONTRIBUTE:
144
+
145
+ If you see problems or enhancements for this approach please send me an email at glenn [at] rempe dot us. Sadly, I won't be able to provide support for this example as time and my limited Solaris admin skills won't allow.
146
+
147
+ Cheers,
148
+
149
+ Glenn Rempe
150
+ 2008/03/20
@@ -11,11 +11,12 @@
11
11
  safe = ("$" | "-" | "_" | ".");
12
12
  extra = ("!" | "*" | "'" | "(" | ")" | ",");
13
13
  reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
14
- unsafe = (CTL | " " | "\"" | "#" | "%" | "<" | ">");
14
+ sorta_safe = ("\"" | "<" | ">");
15
+ unsafe = (CTL | " " | "#" | "%" | sorta_safe);
15
16
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
16
17
  unreserved = (alpha | digit | safe | extra | national);
17
18
  escape = ("%" xdigit xdigit);
18
- uchar = (unreserved | escape);
19
+ uchar = (unreserved | escape | sorta_safe);
19
20
  pchar = (uchar | ":" | "@" | "&" | "=" | "+");
20
21
  tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");
21
22
 
@@ -484,15 +484,11 @@ case 20:
484
484
  #line 485 "parser.c"
485
485
  switch( (*p) ) {
486
486
  case 32: goto tr30;
487
+ case 35: goto st0;
487
488
  case 37: goto tr31;
488
- case 60: goto st0;
489
- case 62: goto st0;
490
489
  case 127: goto st0;
491
490
  }
492
- if ( (*p) > 31 ) {
493
- if ( 34 <= (*p) && (*p) <= 35 )
494
- goto st0;
495
- } else if ( (*p) >= 0 )
491
+ if ( 0 <= (*p) && (*p) <= 31 )
496
492
  goto st0;
497
493
  goto tr29;
498
494
  tr29:
@@ -503,18 +499,14 @@ st21:
503
499
  if ( ++p == pe )
504
500
  goto _out21;
505
501
  case 21:
506
- #line 507 "parser.c"
502
+ #line 503 "parser.c"
507
503
  switch( (*p) ) {
508
504
  case 32: goto tr30;
505
+ case 35: goto st0;
509
506
  case 37: goto st22;
510
- case 60: goto st0;
511
- case 62: goto st0;
512
507
  case 127: goto st0;
513
508
  }
514
- if ( (*p) > 31 ) {
515
- if ( 34 <= (*p) && (*p) <= 35 )
516
- goto st0;
517
- } else if ( (*p) >= 0 )
509
+ if ( 0 <= (*p) && (*p) <= 31 )
518
510
  goto st0;
519
511
  goto st21;
520
512
  tr31:
@@ -525,7 +517,7 @@ st22:
525
517
  if ( ++p == pe )
526
518
  goto _out22;
527
519
  case 22:
528
- #line 529 "parser.c"
520
+ #line 521 "parser.c"
529
521
  if ( (*p) < 65 ) {
530
522
  if ( 48 <= (*p) && (*p) <= 57 )
531
523
  goto st23;
@@ -556,7 +548,7 @@ st24:
556
548
  if ( ++p == pe )
557
549
  goto _out24;
558
550
  case 24:
559
- #line 560 "parser.c"
551
+ #line 552 "parser.c"
560
552
  switch( (*p) ) {
561
553
  case 43: goto st24;
562
554
  case 58: goto st25;
@@ -581,14 +573,11 @@ st25:
581
573
  if ( ++p == pe )
582
574
  goto _out25;
583
575
  case 25:
584
- #line 585 "parser.c"
576
+ #line 577 "parser.c"
585
577
  switch( (*p) ) {
586
578
  case 32: goto tr8;
587
- case 34: goto st0;
588
579
  case 35: goto tr9;
589
580
  case 37: goto st26;
590
- case 60: goto st0;
591
- case 62: goto st0;
592
581
  case 127: goto st0;
593
582
  }
594
583
  if ( 0 <= (*p) && (*p) <= 31 )
@@ -628,15 +617,12 @@ st28:
628
617
  if ( ++p == pe )
629
618
  goto _out28;
630
619
  case 28:
631
- #line 632 "parser.c"
620
+ #line 621 "parser.c"
632
621
  switch( (*p) ) {
633
622
  case 32: goto tr40;
634
- case 34: goto st0;
635
623
  case 35: goto tr41;
636
624
  case 37: goto st29;
637
625
  case 59: goto tr43;
638
- case 60: goto st0;
639
- case 62: goto st0;
640
626
  case 63: goto tr44;
641
627
  case 127: goto st0;
642
628
  }
@@ -681,14 +667,11 @@ st31:
681
667
  if ( ++p == pe )
682
668
  goto _out31;
683
669
  case 31:
684
- #line 685 "parser.c"
670
+ #line 671 "parser.c"
685
671
  switch( (*p) ) {
686
672
  case 32: goto tr8;
687
- case 34: goto st0;
688
673
  case 35: goto tr9;
689
674
  case 37: goto st32;
690
- case 60: goto st0;
691
- case 62: goto st0;
692
675
  case 63: goto st34;
693
676
  case 127: goto st0;
694
677
  }
@@ -733,14 +716,11 @@ st34:
733
716
  if ( ++p == pe )
734
717
  goto _out34;
735
718
  case 34:
736
- #line 737 "parser.c"
719
+ #line 720 "parser.c"
737
720
  switch( (*p) ) {
738
721
  case 32: goto tr51;
739
- case 34: goto st0;
740
722
  case 35: goto tr52;
741
723
  case 37: goto tr53;
742
- case 60: goto st0;
743
- case 62: goto st0;
744
724
  case 127: goto st0;
745
725
  }
746
726
  if ( 0 <= (*p) && (*p) <= 31 )
@@ -754,14 +734,11 @@ st35:
754
734
  if ( ++p == pe )
755
735
  goto _out35;
756
736
  case 35:
757
- #line 758 "parser.c"
737
+ #line 738 "parser.c"
758
738
  switch( (*p) ) {
759
739
  case 32: goto tr55;
760
- case 34: goto st0;
761
740
  case 35: goto tr56;
762
741
  case 37: goto st36;
763
- case 60: goto st0;
764
- case 62: goto st0;
765
742
  case 127: goto st0;
766
743
  }
767
744
  if ( 0 <= (*p) && (*p) <= 31 )
@@ -775,7 +752,7 @@ st36:
775
752
  if ( ++p == pe )
776
753
  goto _out36;
777
754
  case 36:
778
- #line 779 "parser.c"
755
+ #line 756 "parser.c"
779
756
  if ( (*p) < 65 ) {
780
757
  if ( 48 <= (*p) && (*p) <= 57 )
781
758
  goto st37;
@@ -1205,7 +1182,7 @@ case 56:
1205
1182
  if(parser->body_start) {
1206
1183
  /* final \r\n combo encountered so stop right here */
1207
1184
 
1208
- #line 1209 "parser.c"
1185
+ #line 1186 "parser.c"
1209
1186
  #line 130 "parser.rl"
1210
1187
  parser->nread++;
1211
1188
  }
@@ -1218,7 +1195,7 @@ int http_parser_finish(http_parser *parser)
1218
1195
  int cs = parser->cs;
1219
1196
 
1220
1197
 
1221
- #line 1222 "parser.c"
1198
+ #line 1199 "parser.c"
1222
1199
  #line 141 "parser.rl"
1223
1200
 
1224
1201
  parser->cs = cs;
@@ -0,0 +1,69 @@
1
+ module Rack
2
+ class AdapterNotFound < RuntimeError; end
3
+
4
+ # Hash used to guess which adapter to use in <tt>Adapter.for</tt>.
5
+ # Framework name => file unique to this framework.
6
+ # +nil+ for value to never guess.
7
+ ADAPTERS = {
8
+ :rails => "config/environment.rb",
9
+ :ramaze => "start.rb",
10
+ :merb => "config/init.rb",
11
+ :halcyon => 'runner.ru',
12
+ :file => nil
13
+ }
14
+
15
+ module Adapter
16
+ # Guess which adapter to use based on the directory structure
17
+ # or file content.
18
+ # Returns a symbol representing the name of the adapter to use
19
+ # to load the application under <tt>dir/</tt>.
20
+ def self.guess(dir)
21
+ ADAPTERS.each_pair do |adapter, file|
22
+ return adapter if file && ::File.exist?(::File.join(dir, file))
23
+ end
24
+ raise AdapterNotFound, "No adapter found for #{dir}"
25
+ end
26
+
27
+ # Loads an adapter identified by +name+ using +options+ hash.
28
+ def self.for(name, options={})
29
+ case name.to_sym
30
+ when :rails
31
+ return Rails.new(options.merge(:root => options[:chdir]))
32
+
33
+ when :ramaze
34
+ require "#{options[:chdir]}/start"
35
+
36
+ Ramaze.trait[:essentials].delete Ramaze::Adapter
37
+ Ramaze.start :force => true
38
+
39
+ return Ramaze::Adapter::Base
40
+
41
+ when :merb
42
+ require 'merb-core'
43
+
44
+ Merb::Config.setup(:merb_root => options[:chdir],
45
+ :environment => options[:environment])
46
+ Merb.environment = Merb::Config[:environment]
47
+ Merb.root = Merb::Config[:merb_root]
48
+ Merb::BootLoader.run
49
+
50
+ return Merb::Rack::Application.new
51
+
52
+ when :halcyon
53
+ require 'halcyon'
54
+
55
+ $:.unshift(Halcyon.root/'lib')
56
+ Halcyon::Runner.load_config Halcyon.root/'config'/'config.yml'
57
+
58
+ return Halcyon::Runner.new
59
+
60
+ when :file
61
+ return Rack::File.new(options[:chdir])
62
+
63
+ else
64
+ raise AdapterNotFound, "Adapter not found: #{name}"
65
+
66
+ end
67
+ end
68
+ end
69
+ end