thin-1.6.3/0000755000004100000410000000000012432275201012511 5ustar www-datawww-datathin-1.6.3/Rakefile0000644000004100000410000000070412432275201014157 0ustar www-datawww-datarequire 'rake' require 'rake/clean' load 'thin.gemspec' # Load tasks in tasks/ Dir['tasks/**/*.rake'].each { |rake| load rake } task :default => :spec desc "Build gem packages" task :build do sh "gem build thin.gemspec" end desc "Push gem packages" task :push => :build do sh "gem push thin-*.gem" end task :install => :build do sh "gem install thin-*.gem" end desc "Release version #{Thin::VERSION::STRING}" task :release => [:tag, :push] thin-1.6.3/CHANGELOG0000644000004100000410000004471112432275201013732 0ustar www-datawww-data== 1.6.3 Protein Powder * Add HTTP 422 status code [rajcybage] * Add warning about EM reactor still running when stopping. * Remove version number from "Server" HTTP header. [benbasson] * Adding `--ssl-disable-verify` to allow disabling of client cert requests when SSL enabled [brucek] * Ensure Tempfiles created by a large request are closed and deleted. [Tonkpils] == 1.6.2 Doc Brown * No longer replace response's body on HEAD request. Ensuring body.close will be called. * Remove `---ssl-verify` option as EventMachine doesn't verify the certificate. * Fix env['rack.peer_cert'] to return SSL certifcate. == 1.6.1 Death Proof * Regression: Default logger to STDOUT when using outside of CLI. * Regression: Downgrade Rack required version back to 1.0 to work w/ prior Rails versions. == 1.6.0 Greek Yogurt * Accept absolute URL in request line, eg.: 'GET http://site.com/he/lo HTTP/1.1'. * HEAD request no longer return a body in the response. * No longer stop EventMachine's reactor loop unless it was started by Thin. * Make request env keys upcasing locale-agnostic. * Use Ruby's `Logger` for logging. [Akshay Moghe]. The logger can now be set using `Thin::Logging.logger=`. Tracing of request is handled by a second logger, `Thin::Logging.trace_logger=`. * Add --threadpool-size option to configure EM's thread pool size (default: 20). * Pipelining is no longer supported. == 1.5.1 Straight Razor * Fix issue when running as another user/group without a PID file. * Allow overriding Connection & Server response headers. * Update vlad example [Mathieu Lemoine] * Keep connections in a Hash to speedup deletion [slivu] * Force kill using already known pid. Prevents "thin stop" from leaving a process that removed its pid file, but is still running (e.g. hung on some at_exit callback) [Michal Kwiatkowski] == 1.5.0 Knife * Fix compilation under Ubuntu 12.04 with -Werror=format-security option. * Raise an error when no PID file. * Prevent duplicate response headers. * Make proper response on exception [MasterLambaster]. * Automatically close idling pipeline connections on server stop [MasterLambaster]. == 1.4.1 Chromeo Fix * Fix error when sending USR1 signal and no log file is supplied. == 1.4.0 Chromeo * kill -USR1 $PID for log rotation [catwell]. * Fix HUP signal being reseted after deamonization [atotic]. * Fix error with nil addresses in Connection#socket_address. == 1.3.2 Low-bar Squat * Remove mack and halcyon Rack adapters from automatic detection. == 1.3.1 Triple Espresso * Fix service not working pre 1.9. == 1.3.0 Double Espresso * BREAKING CHANGE: Thin no longer ships with fat Windows binaries. From now on, to install on Windows, install https://github.com/oneclick/rubyinstaller/wiki/Development-Kit. * BREAKING CHANGE: Remove automatic Content-Length setting. It is now the responsibility of the app (or a middleware) to set the Content-Length. * Log errors to STDERR [textgoeshere] * Shut down gracefully when receiving SIGTERM [ddollar] Processes are allowed a chance to shut down gracefully when receiving SIGTERM (http://en.wikipedia.org/wiki/SIGTERM). On Heroku, when shutting down a process, we send a SIGTERM followed 10 seconds later with a SIGKILL, similar to the behavior of the init daemon on most Unix systems. This patch will allow Heroku apps to shut down gracefully when they need to be terminated / moved. == 1.2.11 Bat-Shit Crazy * Fix pure Ruby gem to not include binary. == 1.2.10 I'm dumb (BAD RELEASE, DON'T USE) * I really am (bad release fix) == 1.2.9 Black Keys Extra Plus Wow (BAD RELEASE, DON'T USE) * Improve fat binary loading. == 1.2.8 Black Keys * Allow the connection to remain open for 1xx statuses [timshadel] Both the 100 and 101 status codes require that the connection to the server remain open. The 100 status code is used to tell the client that the server is still receiving its request, and will continue to read request input on the connection. The 101 status code is used to upgrade the existing connection to another protocol, and specifically is NOT used to upgrade a separate connection. Therefore, the connection must remain open after this response in order to facilitate that. * Accept IE7 badly encoded URL (eg.: %uEEEE) * Fix gemspec to work w/ Bundler [smparkes] * Add SSL support [tmm1] * Catch Errno::EPERM in Process.running? [Tony Kemp] On some systems (e.g. OpenBSD) you receive an EPERM exception if you try to Process.getpgid on a process you do not own (even if you are root). But it does mean that the process exists, so return true. * Fix Rails version check that select which Rack adapter to use. Was using CGI adapter in Rails 3. * Ignore SIGHUP when no restart block is given * Add SSL options to thin command line tool [goldmann] --ssl Enables SSL --ssl-key-file PATH Path to private key --ssl-cert-file PATH Path to certificate --ssl-verify Enables SSL certificate verification * Expose peer SSL certificate in env (rack.peer_cert) [fd] * Adjusting unix socket permissions to be more open [mbj] == 1.2.7 No Hup * Support multiple Ruby version (fat binaries under windows) * Do not trap unsupported HUP signal on Windows == 1.2.6 Crazy Delicious * Make work with Rails 3 out-of-the-box. * Auto-detect and load config.ru files on start. Makes Rails 3 work. * Fix signals being ignored under 1.9 when daemonized. == 1.2.5 This Is Not A Web Server * Add rolling restart support (--onebyone option) [sikachu] * Force external_encoding of request's body to ASCII_8BIT [jeremyz] * Ensure Rack base API is used in Rails adapter only if version >= 2.3.2 [#111 state:resolved] == 1.2.4 Flaming Astroboy * Fix a few issues in thin to make it a better "gem citizen" [josh] * Fix test for rack based Rails in adapter under Ruby >= 1.8.7 [#109 state:resolved] * Fix Remote address spoofing vulnerability in Connection#remote_address [Alexey Borzenkov] * Fix uninitialized constant ActionController::Dispatcher error with Rails 1.2.3 [Chris Anderton] [#103 state:resolved] == 1.2.2 I Find Your Lack of Sauce Disturbing release * Fix force kill under 1.9 [Alexey Chebotar] * Fix regression when --only option is used w/ --socket. * Add process name 'tag' functionality. Easier to distinguish thin daemons from eachother in process listing [ctcherry] == 1.2.1 Asynctilicious Ultra Supreme release * Require Rack 1.0.0 * Require EventMachine 0.12.6 * Use Rails Rack based dispatcher when available * Allow String for response body * Require openssl before eventmachine to prevent crash in 1.9 == 1.2.0 Asynctilicious Supreme release * Add support for Windows mingw Ruby distro [Juan C. Rodriguez] * Add async response support, see example/async_*.ru [raggi] == 1.1.1 Super Disco Power Plus release * Fix bug when running with only options [hasimo] == 1.1.0 Super Disco Power release * Require EventMachine 0.12.4 * Remove Thin handler, now part of Rack 0.9.1 * Fix Rack protocol version to 0.1 in environment hash. * Fix error when passing no_epoll option to a cluster. * Omit parsing #defined strings [Jérémy Zurcher] * Defaults SERVER_NAME to localhost like webrick does [#87 state:resolved] * Namespace parser to prevent error when mongrel is required [cliffmoon] * Set RACK_ENV based on environment option when loading rackup file [Curtis Summers] [#83 state:resolved] * Fixes a warning RE relative_url_root when using a prefix with Rails 2.1.1 [seriph] [#85 state:resolved] * --only can work as a sequence number (if < 80) or a port number (if >= 80) [jmay] [#81 state:resolved] == 1.0.0 That's What She Said release * Fixed vlad.rake to allow TCP or socket [hellekin] * Updated Mack adapter to handle both <0.8.0 and >0.8.0 [Mark Bates] * rails rack adapter uses File.readable_real? so it recognizes ACL permissions [Ricardo Chimal] * Log a warning if Rack application returns nil body [Michael S. Klishin] * Handle nil and Time header values correctly [#76 state:resolved] [tmm1] * Add Content-Length header to response automatically when possible [#74 state:resolved] [dkubb] * Runner now remembers -r, -D and -V parameters so that clustered servers inherit those and `restart` keep your parameters. * Make Set-Cookie header, in Rails adapter, compatible with current Rack spec [Pedro Belo] [#73, state:resolved] * Add --no-epoll option to disable epoll usage on Linux [#61 state:resolved] * Add --force (-f) option to force stopping of a daemonized server [#72 state:resolved] * Update halycon adapter loader [mtodd] == 0.8.2 Double Margarita release * Require EventMachine 0.12.0 * [bug] Fix timeout handling when running command * [bug] Fix hanging when restarting and no process is running in single server move, fixes #67 * Added Mack adapter [markbates] * Allow rackup .rb files by getting a conventionally named constant as the app [bmizerany] == 0.8.1 Rebel Porpoise release * [bug] Rescue all types of errors when processing request, fixes #62 * [bug] Use Swiftiply backend when -y option is specified, fixes #63 and #64 * Allow passing port as a string in Server.new * Define deferred?(env) in your Rack application to set if a request is handled in a thread (return true) or not (return false). == 0.8.0 Dodgy Dentist release * [bug] Fix server crash when header too large. * Add --require (-r) option to require a library, before executing your script. * Rename --rackup short option to -R, warn and load as rackup when file ends with .ru. * List supported adapters in command usage. * Add file adapter to built-in adapter, serve static files in current directory. * Allow disabling signal handling in Server with :signals => false * Make Server.new arguments more flexible, can now specify any of host, port, app or hash options. * Add --backend option to specified which backend to use, closes #55 * [bug] Serve static file only on GET and HEAD requests in Rails adapter, fixes #58 * Add threaded option to run server in threaded mode, calling the application in a thread allowing for concurrency in the Rack adapter, closes #46 * Guess which adapter to use from directory (chdir option) or use specified one in 'adapter' option, re #47. == 0.7.1 Fancy Pants release * Clean stale PID files when starting as daemon, fixes #53 [Chu Yeow] * Require EventMachine 0.11.0 for UNIX domain sockets. Until it's released, install from: gem install eventmachine --source http://code.macournoyer.com * Ruby 1.8.5 compatibility, closes #49 [Wincent Colaiuta] * Move all EventMachine stuff out of Server, you can now create a Thin Backend that does not depend on EventMachine. * Rename Connector to Backend. Extend Thin::Backends::Base to implement your own. * Fix high memory usage with big POST body, fixes #48 == 0.7.0 Spherical Cow release * Add --max-persistent-conns option to sets the maximum number of persistent connections. Set to 0 to disable Keep-Alive. * INT signal now force stop and QUIT signal gracefully stops. * Warn when descriptors table size can't be set as high as expected. * Eval Rackup config file using top level bindings. * Remove daemons gem dependency on Windows plateform, fixes #45. * Change default timeout from 60 to 30 seconds. * Add --max-conns option to sets the maximum number of file or socket descriptors that your process may open, defaults to 1024. * Tail logfile when stopping and restarting a demonized server, fixes #26. * Wrap application in a Rack::CommonLogger adapter in debug mode. * --debug (-D) option no longer set $DEBUG so logging will be less verbose and Ruby won't be too strict, fixes #36. * Deprecate Server#silent in favour of Logging.silent. * Persistent connection (keep-alive) support. * Fix -s option not being included in generated config file, fixes #37. * Add Swiftiply support. Use w/ the --swiftiply (-y) option in the thin script, closes #28 [Alex MacCaw] == 0.6.4 Sexy Lobster release * Fix error when stopping server on UNIX domain socket, fixes #42 * Rescue errors in Connection#get_peername more gracefully, setting REMOTE_ADDR to nil, fixes #43 == 0.6.3 Ninja Cookie release * Add tasks for Vlad the Deployer in example/vlad.rake [cnantais] * Add Ramaze Rackup config file in example dir [tmm1] Use like this from you Ramaze app dir: thin start -r /path/to/thin/example/ramaze.ru * Add the --rackup option to load a Rack config file instead of the Rails adapter. So you can use any framework with the thin script and start cluster and stuff like that. A Rack config file is one that is usable through the rackup command and looks like this: use Rack::CommonLogger run MyCrazyRackAdapter.new(:uterly, 'cool') Then use it with thin like this: thin start --rackup config.ru * thin config --chrdir ... -C thin/yml do not change current directory anymore, fixes #33. * Add a better sample god config file in example/thin.god that loads all info from config files in /etc/thin. Drop-in replacement for the thin runlevel service [Gump]. * Add support for specifying a custom Connector to the server and add more doc about Server configuration. * Add a script to run thin as a runlevel service that can start at startup, closes #31 [Gump] Setup the service like this: sudo thin install /etc/thin This will install the boot script under /etc/init.d/thin. Then copy your config files to /etc/thin. Works only under Linux. * Set process name to 'thin server (0.0.0.0:3000)' when running as a daemon, closes #32. * Make sure chdir option from config file is used when present. * Raise an error when starting a server as a daemon and pid file already exist, fixes #27. == 0.6.2 Rambo release * Server now let current connections finish before stopping, fixes #18 * Fix uploading hanging bug when body is moved to a tempfile, also delete the tempfile properly upon completion, fixes #25 * 'thin restart' now sends HUP signals rather then stopping & starting, closes #17 * HUP signal now launches a new process with the same options. * Add PID and more info from the last request to the Stats adapter mostly taken from Rack::ShowException. * pid and log files in cluster are no longer required to be relative to the app directory (chdir option), fixes #24 * Revert to using #each when building response headers under Ruby 1.8, solves an issue w/ Camping adapter, fixes #22 * Restructure thin script options in 3 sections: server, daemon and cluster * Add --only (-o) option to control only one server of a cluster. * Stylize stats page and make the url configurable from the thin script. * Raise error if attempting to use unix sockets on windows. * Add example config files for http://www.tildeslash.com/monit usage. Include the example file using "include /path/to/thin/monit/file" in your monitrc file. The group settings let you do this to manage your clusters: sudo monit -g blog restart all There are examples of thin listening on sockets and thin listening on unix sockets. == 0.6.1 Cheesecake release * Remove socket file when server stops. * Set back cluster to use 'thin' command to launch servers. == 0.6.0 Big Pony release * Add support for connection through UNIX domain socket. Use the --socket (-S) option w/ the thin script to configure the socket filename. Nginx support binding to a UNIX socket like this: upstream backend { server unix:/tmp/thin.0.sock; server unix:/tmp/thin.1.sock; server unix:/tmp/thin.2.sock; } Start your servers like this: thin start -s3 -S/tmp/thin.sock * Remove Server#listen! method. Use Server#start instead. * Server can now yield a Rack::Builder to allow building an app in one call: Server.start '0.0.0.0', 3000 do use Rack::CommonLogger use Rack::ShowExceptions map "/lobster" do use Rack::Lint run Rack::Lobster.new end end * Add a very basic stats page through Stats adapter, load w/ --stats and browse to /stats. * Add --trace (-V) option to trace request/response and get backtrace w/out all Ruby debug stuff. * Add --config (-C) option to load options from a config file in thin script [Matt Todd]. * Alter response headers to output directly to a string. * Improve specs stability. * Move request body to a Tempfile if too big (> 112 KB) * Remove useless check for max header size in Request (already done in the parser) == 0.5.4 Flying Mustard release * Don't read the full body, use direct streaming when sending response. See: Response#each As a result, the Content-Length can not be calculated anymore. You have to do set this in your adapter. All frameworks do it anyway. It improve memory usage and boost speed for low concurrency. Thanks to Kent Sibilev and Ezra for their help on that one. * Add 'Server' response header * Fix --user and --group option not changing daemon process privileges == 0.5.3 Purple Yogurt release * win32 pre-compiled gem now available * change rake task configuration to allow win32 gem build * Add prefix option to thin script to mount app under a given path. == 0.5.2 Cheezburger release * Add cluster support through the -s option in the thin script, start 3 thins like this: thin start -s3 -p3000 3 thin servers will be started on port 3000, 3001, 3002, also the port number will be injected in the pid and log filenames. * Fix IOError when writing to logger when starting server as a daemon. * Really change directory when the -c option is specified. * Add restart command to thin script. * Fix typos in thin script usage message and expand chdir path. * Rename thin script options to be the same as mongrel_rails script [thronedrk]: -o --host => -a --address --log-file => --log --pid-file => --pid --env => --environment == 0.5.1 LOLCAT release * Add URL rewriting to Rails adapter so that page caching works and / fetches index.html if present. * Fix bug in multiline response header parsing. * Add specs for the Rails adapter. * Fix Set-Cookie headers in Rails adapter to handle multiple values correctly. * Fix Ruby 1.9 incompatibility in Response#headers= and Rakefile. * Fix parser to be Ruby 1.9 compatible [Aman Gupta] * Set gemspec to use EventMachine version 0.8.1 as it's the latest one having precompiled windows binaries. [Francis Cianfrocca]. * Add -D option to thin script to set debugging on. * Output incoming data and response when debugging is on. == 0.5.0 * Full rewrite to use EventMachine, Rack and Mongrel parser == 0.4.1 * Fix Rails environment option not being used in thin script. == 0.4.0 * First alphaish release as a gem. thin-1.6.3/bin/0000755000004100000410000000000012432275201013261 5ustar www-datawww-datathin-1.6.3/bin/thin0000755000004100000410000000022012432275201014143 0ustar www-datawww-data#!/usr/bin/env ruby # Thin command line interface script. # Run thin -h to get more usage. require 'thin' Thin::Runner.new(ARGV).run! thin-1.6.3/lib/0000755000004100000410000000000012432275201013257 5ustar www-datawww-datathin-1.6.3/lib/thin/0000755000004100000410000000000012432275201014221 5ustar www-datawww-datathin-1.6.3/lib/thin/headers.rb0000644000004100000410000000200612432275201016157 0ustar www-datawww-datamodule Thin # Store HTTP header name-value pairs direcly to a string # and allow duplicated entries on some names. class Headers HEADER_FORMAT = "%s: %s\r\n".freeze ALLOWED_DUPLICATES = %w(set-cookie set-cookie2 warning www-authenticate).freeze def initialize @sent = {} @out = [] end # Add key: value pair to the headers. # Ignore if already sent and no duplicates are allowed # for this +key+. def []=(key, value) downcase_key = key.downcase if !@sent.has_key?(downcase_key) || ALLOWED_DUPLICATES.include?(downcase_key) @sent[downcase_key] = true value = case value when Time value.httpdate when NilClass return else value.to_s end @out << HEADER_FORMAT % [key, value] end end def has_key?(key) @sent[key.downcase] end def to_s @out.join end end end thin-1.6.3/lib/thin/backends/0000755000004100000410000000000012432275201015773 5ustar www-datawww-datathin-1.6.3/lib/thin/backends/tcp_server.rb0000644000004100000410000000124512432275201020476 0ustar www-datawww-datamodule Thin module Backends # Backend to act as a TCP socket server. class TcpServer < Base # Address and port on which the server is listening for connections. attr_accessor :host, :port def initialize(host, port) @host = host @port = port super() end # Connect the server def connect @signature = EventMachine.start_server(@host, @port, Connection, &method(:initialize_connection)) end # Stops the server def disconnect EventMachine.stop_server(@signature) end def to_s "#{@host}:#{@port}" end end end endthin-1.6.3/lib/thin/backends/unix_server.rb0000644000004100000410000000306612432275201020676 0ustar www-datawww-datamodule Thin module Backends # Backend to act as a UNIX domain socket server. class UnixServer < Base # UNIX domain socket on which the server is listening for connections. attr_accessor :socket def initialize(socket) raise PlatformNotSupported, 'UNIX domain sockets not available on Windows' if Thin.win? @socket = socket super() end # Connect the server def connect at_exit { remove_socket_file } # In case it crashes old_umask = File.umask(0) begin EventMachine.start_unix_domain_server(@socket, UnixConnection, &method(:initialize_connection)) # HACK EventMachine.start_unix_domain_server doesn't return the connection signature # so we have to go in the internal stuff to find it. @signature = EventMachine.instance_eval{@acceptors.keys.first} ensure File.umask(old_umask) end end # Stops the server def disconnect EventMachine.stop_server(@signature) end # Free up resources used by the backend. def close remove_socket_file end def to_s @socket end protected def remove_socket_file File.delete(@socket) if @socket && File.exist?(@socket) end end end # Connection through a UNIX domain socket. class UnixConnection < Connection protected def socket_address '127.0.0.1' # Unix domain sockets can only be local end end end thin-1.6.3/lib/thin/backends/base.rb0000644000004100000410000001273012432275201017235 0ustar www-datawww-datamodule Thin module Backends # A Backend connects the server to the client. It handles: # * connection/disconnection to the server # * initialization of the connections # * manitoring of the active connections. # # == Implementing your own backend # You can create your own minimal backend by inheriting this class and # defining the +connect+ and +disconnect+ method. # If your backend is not based on EventMachine you also need to redefine # the +start+, +stop+, stop! and +config+ methods. class Base # Server serving the connections throught the backend attr_accessor :server # Maximum time for incoming data to arrive attr_accessor :timeout # Maximum number of file or socket descriptors that the server may open. attr_accessor :maximum_connections # Maximum number of connections that can be persistent attr_accessor :maximum_persistent_connections #allows setting of the eventmachine threadpool size attr_reader :threadpool_size def threadpool_size=(size) @threadpool_size = size EventMachine.threadpool_size = size end # Allow using threads in the backend. attr_writer :threaded def threaded?; @threaded end # Allow using SSL in the backend. attr_writer :ssl, :ssl_options def ssl?; @ssl end # Number of persistent connections currently opened attr_accessor :persistent_connection_count # Disable the use of epoll under Linux attr_accessor :no_epoll def initialize @connections = {} @timeout = Server::DEFAULT_TIMEOUT @persistent_connection_count = 0 @maximum_connections = Server::DEFAULT_MAXIMUM_CONNECTIONS @maximum_persistent_connections = Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS @no_epoll = false @ssl = nil @threaded = nil @started_reactor = false end # Start the backend and connect it. def start @stopping = false starter = proc do connect yield if block_given? @running = true end # Allow for early run up of eventmachine. if EventMachine.reactor_running? starter.call else @started_reactor = true EventMachine.run(&starter) end end # Stop of the backend from accepting new connections. def stop @running = false @stopping = true # Do not accept anymore connection disconnect # Close idle persistent connections @connections.each_value { |connection| connection.close_connection if connection.idle? } stop! if @connections.empty? end # Force stop of the backend NOW, too bad for the current connections. def stop! @running = false @stopping = false EventMachine.stop if @started_reactor && EventMachine.reactor_running? @connections.each_value { |connection| connection.close_connection } close end # Configure the backend. This method will be called before droping superuser privileges, # so you can do crazy stuff that require godlike powers here. def config # See http://rubyeventmachine.com/pub/rdoc/files/EPOLL.html EventMachine.epoll unless @no_epoll # Set the maximum number of socket descriptors that the server may open. # The process needs to have required privilege to set it higher the 1024 on # some systems. @maximum_connections = EventMachine.set_descriptor_table_size(@maximum_connections) unless Thin.win? end # Free up resources used by the backend. def close end # Returns +true+ if the backend is connected and running. def running? @running end def started_reactor? @started_reactor end # Called by a connection when it's unbinded. def connection_finished(connection) @persistent_connection_count -= 1 if connection.can_persist? @connections.delete(connection.__id__) # Finalize gracefull stop if there's no more active connection. stop! if @stopping && @connections.empty? end # Returns +true+ if no active connection. def empty? @connections.empty? end # Number of active connections. def size @connections.size end protected # Initialize a new connection to a client. def initialize_connection(connection) connection.backend = self connection.app = @server.app connection.comm_inactivity_timeout = @timeout connection.threaded = @threaded if @ssl connection.start_tls(@ssl_options) end # We control the number of persistent connections by keeping # a count of the total one allowed yet. if @persistent_connection_count < @maximum_persistent_connections connection.can_persist! @persistent_connection_count += 1 end @connections[connection.__id__] = connection end end end end thin-1.6.3/lib/thin/backends/swiftiply_client.rb0000644000004100000410000000253412432275201021714 0ustar www-datawww-datamodule Thin module Backends # Backend to act as a Swiftiply client (http://swiftiply.swiftcore.org). class SwiftiplyClient < Base attr_accessor :key attr_accessor :host, :port def initialize(host, port, options = {}) @host = host @port = port.to_i @key = options[:swiftiply].to_s super() end # Connect the server def connect EventMachine.connect(@host, @port, SwiftiplyConnection, &method(:initialize_connection)) end # Stops the server def disconnect EventMachine.stop end def to_s "#{@host}:#{@port} swiftiply" end end end class SwiftiplyConnection < Connection def connection_completed send_data swiftiply_handshake(@backend.key) end def persistent? true end def unbind super EventMachine.add_timer(rand(2)) { reconnect(@backend.host, @backend.port) } if @backend.running? end protected def swiftiply_handshake(key) 'swiftclient' << host_ip.collect { |x| sprintf('%02x', x.to_i) }.join << sprintf('%04x', @backend.port) << sprintf('%02x', key.length) << key end # For some reason Swiftiply request the current host def host_ip Socket.gethostbyname(@backend.host)[3].unpack('CCCC') rescue [0, 0, 0, 0] end end endthin-1.6.3/lib/thin/connection.rb0000644000004100000410000001466512432275201016721 0ustar www-datawww-datarequire 'socket' module Thin # Connection between the server and client. # This class is instanciated by EventMachine on each new connection # that is opened. class Connection < EventMachine::Connection include Logging # This is a template async response. N.B. Can't use string for body on 1.9 AsyncResponse = [-1, {}, []].freeze # Rack application (adapter) served by this connection. attr_accessor :app # Backend to the server attr_accessor :backend # Current request served by the connection attr_accessor :request # Next response sent through the connection attr_accessor :response # Calling the application in a threaded allowing # concurrent processing of requests. attr_writer :threaded # Get the connection ready to process a request. def post_init @request = Request.new @response = Response.new end # Called when data is received from the client. def receive_data(data) @idle = false trace data process if @request.parse(data) rescue InvalidRequest => e log_error("Invalid request", e) post_process Response::BAD_REQUEST end # Called when all data was received and the request # is ready to be processed. def process if threaded? @request.threaded = true EventMachine.defer(method(:pre_process), method(:post_process)) else @request.threaded = false post_process(pre_process) end end def ssl_verify_peer(cert) # In order to make the cert available later we have to have made at least # a show of verifying it. true end def pre_process # Add client info to the request env @request.remote_address = remote_address # Connection may be closed unless the App#call response was a [-1, ...] # It should be noted that connection objects will linger until this # callback is no longer referenced, so be tidy! @request.async_callback = method(:post_process) if @backend.ssl? @request.env["rack.url_scheme"] = "https" if cert = get_peer_cert @request.env['rack.peer_cert'] = cert end end # When we're under a non-async framework like rails, we can still spawn # off async responses using the callback info, so there's little point # in removing this. response = AsyncResponse catch(:async) do # Process the request calling the Rack adapter response = @app.call(@request.env) end response rescue Exception => e unexpected_error(e) # Pass through error response can_persist? && @request.persistent? ? Response::PERSISTENT_ERROR : Response::ERROR end def post_process(result) return unless result result = result.to_a # Status code -1 indicates that we're going to respond later (async). return if result.first == AsyncResponse.first @response.status, @response.headers, @response.body = *result log_error("Rack application returned nil body. " \ "Probably you wanted it to be an empty string?") if @response.body.nil? # HEAD requests should not return a body. @response.skip_body! if @request.head? # Make the response persistent if requested by the client @response.persistent! if @request.persistent? # Send the response @response.each do |chunk| trace chunk send_data chunk end rescue Exception => e unexpected_error(e) # Close connection since we can't handle response gracefully close_connection ensure # If the body is being deferred, then terminate afterward. if @response.body.respond_to?(:callback) && @response.body.respond_to?(:errback) @response.body.callback { terminate_request } @response.body.errback { terminate_request } else # Don't terminate the response if we're going async. terminate_request unless result && result.first == AsyncResponse.first end end # Logs information about an unexpected exceptional condition def unexpected_error(e) log_error("Unexpected error while processing request", e) end def close_request_response @request.async_close.succeed if @request.async_close @request.close rescue nil @response.close rescue nil end # Does request and response cleanup (closes open IO streams and # deletes created temporary files). # Re-initializes response and request if client supports persistent # connection. def terminate_request unless persistent? close_connection_after_writing rescue nil close_request_response else close_request_response # Connection become idle but it's still open @idle = true # Prepare the connection for another request if the client # supports HTTP pipelining (persistent connection). post_init end end # Called when the connection is unbinded from the socket # and can no longer be used to process requests. def unbind @request.async_close.succeed if @request.async_close @response.body.fail if @response.body.respond_to?(:fail) @backend.connection_finished(self) end # Allows this connection to be persistent. def can_persist! @can_persist = true end # Return +true+ if this connection is allowed to stay open and be persistent. def can_persist? @can_persist end # Return +true+ if the connection must be left open # and ready to be reused for another request. def persistent? @can_persist && @response.persistent? end # Return +true+ if the connection is open but is not # processing any user requests def idle? @idle end # +true+ if app.call will be called inside a thread. # You can set all requests as threaded setting Connection#threaded=true # or on a per-request case returning +true+ in app.deferred?. def threaded? @threaded || (@app.respond_to?(:deferred?) && @app.deferred?(@request.env)) end # IP Address of the remote client. def remote_address socket_address rescue Exception => e log_error('Could not infer remote address', e) nil end protected # Returns IP address of peer as a string. def socket_address peer = get_peername Socket.unpack_sockaddr_in(peer)[1] if peer end end end thin-1.6.3/lib/thin/controllers/0000755000004100000410000000000012432275201016567 5ustar www-datawww-datathin-1.6.3/lib/thin/controllers/cluster.rb0000644000004100000410000001230512432275201020576 0ustar www-datawww-datarequire 'socket' module Thin # An exception class to handle the event that server didn't start on time class RestartTimeout < RuntimeError; end module Controllers # Control a set of servers. # * Generate start and stop commands and run them. # * Inject the port or socket number in the pid and log filenames. # Servers are started throught the +thin+ command-line script. class Cluster < Controller # Cluster only options that should not be passed in the command sent # to the indiviual servers. CLUSTER_OPTIONS = [:servers, :only, :onebyone, :wait] # Maximum wait time for the server to be restarted DEFAULT_WAIT_TIME = 30 # seconds # Create a new cluster of servers launched using +options+. def initialize(options) super # Cluster can only contain daemonized servers @options.merge!(:daemonize => true) end def first_port; @options[:port] end def address; @options[:address] end def socket; @options[:socket] end def pid_file; @options[:pid] end def log_file; @options[:log] end def size; @options[:servers] end def only; @options[:only] end def onebyone; @options[:onebyone] end def wait; @options[:wait] end def swiftiply? @options.has_key?(:swiftiply) end # Start the servers def start with_each_server { |n| start_server n } end # Start a single server def start_server(number) log_info "Starting server on #{server_id(number)} ... " run :start, number end # Stop the servers def stop with_each_server { |n| stop_server n } end # Stop a single server def stop_server(number) log_info "Stopping server on #{server_id(number)} ... " run :stop, number end # Stop and start the servers. def restart unless onebyone # Let's do a normal restart by defaults stop sleep 0.1 # Let's breath a bit shall we ? start else with_each_server do |n| stop_server(n) sleep 0.1 # Let's breath a bit shall we ? start_server(n) wait_until_server_started(n) end end end def test_socket(number) if socket UNIXSocket.new(socket_for(number)) else TCPSocket.new(address, number) end rescue nil end # Make sure the server is running before moving on to the next one. def wait_until_server_started(number) log_info "Waiting for server to start ..." STDOUT.flush # Need this to make sure user got the message tries = 0 loop do if test_socket = test_socket(number) test_socket.close break elsif tries < wait sleep 1 tries += 1 else raise RestartTimeout, "The server didn't start in time. Please look at server's log file " + "for more information, or set the value of 'wait' in your config " + "file to be higher (defaults: 30)." end end end def server_id(number) if socket socket_for(number) elsif swiftiply? [address, first_port, number].join(':') else [address, number].join(':') end end def log_file_for(number) include_server_number log_file, number end def pid_file_for(number) include_server_number pid_file, number end def socket_for(number) include_server_number socket, number end def pid_for(number) File.read(pid_file_for(number)).chomp.to_i end private # Send the command to the +thin+ script def run(cmd, number) cmd_options = @options.reject { |option, value| CLUSTER_OPTIONS.include?(option) } cmd_options.merge!(:pid => pid_file_for(number), :log => log_file_for(number)) if socket cmd_options.merge!(:socket => socket_for(number)) elsif swiftiply? cmd_options.merge!(:port => first_port) else cmd_options.merge!(:port => number) end Command.run(cmd, cmd_options) end def with_each_server if only if first_port && only < 80 # interpret +only+ as a sequence number yield first_port + only else # interpret +only+ as an absolute port number yield only end elsif socket || swiftiply? size.times { |n| yield n } else size.times { |n| yield first_port + n } end end # Add the server port or number in the filename # so each instance get its own file def include_server_number(path, number) ext = File.extname(path) path.gsub(/#{ext}$/, ".#{number}#{ext}") end end end end thin-1.6.3/lib/thin/controllers/controller.rb0000644000004100000410000001475012432275201021306 0ustar www-datawww-datarequire 'yaml' module Thin # Error raised that will abort the process and print not backtrace. class RunnerError < RuntimeError; end # Raised when a mandatory option is missing to run a command. class OptionRequired < RunnerError def initialize(option) super("#{option} option required") end end # Raised when an option is not valid. class InvalidOption < RunnerError; end # Build and control Thin servers. # Hey Controller pattern is not only for web apps yo! module Controllers # Controls one Thin server. # Allow to start, stop, restart and configure a single thin server. class Controller include Logging # Command line options passed to the thin script attr_accessor :options def initialize(options) @options = options if @options[:socket] @options.delete(:address) @options.delete(:port) end end def start # Constantize backend class @options[:backend] = eval(@options[:backend], TOPLEVEL_BINDING) if @options[:backend] server = Server.new(@options[:socket] || @options[:address], # Server detects kind of socket @options[:port], # Port ignored on UNIX socket @options) # Set options server.pid_file = @options[:pid] server.log_file = @options[:log] server.timeout = @options[:timeout] server.maximum_connections = @options[:max_conns] server.maximum_persistent_connections = @options[:max_persistent_conns] server.threaded = @options[:threaded] server.no_epoll = @options[:no_epoll] if server.backend.respond_to?(:no_epoll=) server.threadpool_size = @options[:threadpool_size] if server.threaded? # ssl support if @options[:ssl] server.ssl = true server.ssl_options = { :private_key_file => @options[:ssl_key_file], :cert_chain_file => @options[:ssl_cert_file], :verify_peer => !@options[:ssl_disable_verify] } end # Detach the process, after this line the current process returns server.daemonize if @options[:daemonize] # +config+ must be called before changing privileges since it might require superuser power. server.config server.change_privilege @options[:user], @options[:group] if @options[:user] && @options[:group] # If a Rack config file is specified we eval it inside a Rack::Builder block to create # a Rack adapter from it. Or else we guess which adapter to use and load it. if @options[:rackup] server.app = load_rackup_config else server.app = load_adapter end # If a prefix is required, wrap in Rack URL mapper server.app = Rack::URLMap.new(@options[:prefix] => server.app) if @options[:prefix] # If a stats URL is specified, wrap in Stats adapter server.app = Stats::Adapter.new(server.app, @options[:stats]) if @options[:stats] # Register restart procedure which just start another process with same options, # so that's why this is done here. server.on_restart { Command.run(:start, @options) } server.start end def stop raise OptionRequired, :pid unless @options[:pid] tail_log(@options[:log]) do if Server.kill(@options[:pid], @options[:force] ? 0 : (@options[:timeout] || 60)) wait_for_file :deletion, @options[:pid] end end end def restart raise OptionRequired, :pid unless @options[:pid] tail_log(@options[:log]) do if Server.restart(@options[:pid]) wait_for_file :creation, @options[:pid] end end end def config config_file = @options.delete(:config) || raise(OptionRequired, :config) # Stringify keys @options.keys.each { |o| @options[o.to_s] = @options.delete(o) } File.open(config_file, 'w') { |f| f << @options.to_yaml } log_info "Wrote configuration to #{config_file}" end protected # Wait for a pid file to either be created or deleted. def wait_for_file(state, file) Timeout.timeout(@options[:timeout] || 30) do case state when :creation then sleep 0.1 until File.exist?(file) when :deletion then sleep 0.1 while File.exist?(file) end end end # Tail the log file of server +number+ during the execution of the block. def tail_log(log_file) if log_file tail_thread = tail(log_file) yield tail_thread.kill else yield end end # Acts like GNU tail command. Taken from Rails. def tail(file) cursor = File.exist?(file) ? File.size(file) : 0 last_checked = Time.now tail_thread = Thread.new do Thread.pass until File.exist?(file) File.open(file, 'r') do |f| loop do f.seek cursor if f.mtime > last_checked last_checked = f.mtime contents = f.read cursor += contents.length print contents STDOUT.flush end sleep 0.1 end end end sleep 1 if File.exist?(file) # HACK Give the thread a little time to open the file tail_thread end private def load_adapter adapter = @options[:adapter] || Rack::Adapter.guess(@options[:chdir]) log_info "Using #{adapter} adapter" Rack::Adapter.for(adapter, @options) rescue Rack::AdapterNotFound => e raise InvalidOption, e.message end def load_rackup_config ENV['RACK_ENV'] = @options[:environment] case @options[:rackup] when /\.rb$/ Kernel.load(@options[:rackup]) Object.const_get(File.basename(@options[:rackup], '.rb').capitalize.to_sym) when /\.ru$/ Rack::Adapter.load(@options[:rackup]) else raise "Invalid rackup file. please specify either a .ru or .rb file" end end end end end thin-1.6.3/lib/thin/controllers/service.sh.erb0000644000004100000410000000133712432275201021336 0ustar www-datawww-data#!/bin/sh ### BEGIN INIT INFO # Provides: thin # Required-Start: $local_fs $remote_fs # Required-Stop: $local_fs $remote_fs # Default-Start: 2 3 4 5 # Default-Stop: S 0 1 6 # Short-Description: thin initscript # Description: thin ### END INIT INFO # Original author: Forrest Robertson # Do NOT "set -e" DAEMON=<%= Command.script %> SCRIPT_NAME=<%= INITD_PATH %> CONFIG_PATH=<%= config_files_path %> # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 case "$1" in start) $DAEMON start --all $CONFIG_PATH ;; stop) $DAEMON stop --all $CONFIG_PATH ;; restart) $DAEMON restart --all $CONFIG_PATH ;; *) echo "Usage: $SCRIPT_NAME {start|stop|restart}" >&2 exit 3 ;; esac : thin-1.6.3/lib/thin/controllers/service.rb0000644000004100000410000000425112432275201020556 0ustar www-datawww-datarequire 'erb' module Thin module Controllers # System service controller to launch all servers which # config files are in a directory. class Service < Controller INITD_PATH = File.directory?('/etc/rc.d') ? '/etc/rc.d/thin' : '/etc/init.d/thin' DEFAULT_CONFIG_PATH = '/etc/thin' TEMPLATE = File.dirname(__FILE__) + '/service.sh.erb' def initialize(options) super raise PlatformNotSupported, 'Running as a service only supported on Linux' unless Thin.linux? end def config_path @options[:all] || DEFAULT_CONFIG_PATH end def start run :start end def stop run :stop end def restart run :restart end def install(config_files_path=DEFAULT_CONFIG_PATH) if File.exist?(INITD_PATH) log_info "Thin service already installed at #{INITD_PATH}" else log_info "Installing thin service at #{INITD_PATH} ..." sh "mkdir -p #{File.dirname(INITD_PATH)}" log_info "writing #{INITD_PATH}" File.open(INITD_PATH, 'w') do |f| f << ERB.new(File.read(TEMPLATE)).result(binding) end sh "chmod +x #{INITD_PATH}" # Make executable end sh "mkdir -p #{config_files_path}" log_info '' log_info "To configure thin to start at system boot:" log_info "on RedHat like systems:" log_info " sudo /sbin/chkconfig --level 345 #{NAME} on" log_info "on Debian-like systems (Ubuntu):" log_info " sudo /usr/sbin/update-rc.d -f #{NAME} defaults" log_info "on Gentoo:" log_info " sudo rc-update add #{NAME} default" log_info '' log_info "Then put your config files in #{config_files_path}" end private def run(command) Dir[config_path + '/*'].each do |config| log_info "[#{command}] #{config} ..." Command.run(command, :config => config, :daemonize => true) end end def sh(cmd) log_info cmd system(cmd) end end end end thin-1.6.3/lib/thin/response.rb0000644000004100000410000000643312432275201016412 0ustar www-datawww-datamodule Thin # A response sent to the client. class Response CONNECTION = 'Connection'.freeze CLOSE = 'close'.freeze KEEP_ALIVE = 'keep-alive'.freeze SERVER = 'Server'.freeze CONTENT_LENGTH = 'Content-Length'.freeze PERSISTENT_STATUSES = [100, 101].freeze #Error Responses ERROR = [500, {'Content-Type' => 'text/plain'}, ['Internal server error']].freeze PERSISTENT_ERROR = [500, {'Content-Type' => 'text/plain', 'Connection' => 'keep-alive', 'Content-Length' => "21"}, ['Internal server error']].freeze BAD_REQUEST = [400, {'Content-Type' => 'text/plain'}, ['Bad Request']].freeze # Status code attr_accessor :status # Response body, must respond to +each+. attr_accessor :body # Headers key-value hash attr_reader :headers def initialize @headers = Headers.new @status = 200 @persistent = false @skip_body = false end # String representation of the headers # to be sent in the response. def headers_output # Set default headers @headers[CONNECTION] = persistent? ? KEEP_ALIVE : CLOSE unless @headers.has_key?(CONNECTION) @headers[SERVER] = Thin::NAME unless @headers.has_key?(SERVER) @headers.to_s end # Top header of the response, # containing the status code and response headers. def head "HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n" end if Thin.ruby_18? # Ruby 1.8 implementation. # Respects Rack specs. # # See http://rack.rubyforge.org/doc/files/SPEC.html def headers=(key_value_pairs) key_value_pairs.each do |k, vs| vs.each { |v| @headers[k] = v.chomp } if vs end if key_value_pairs end else # Ruby 1.9 doesn't have a String#each anymore. # Rack spec doesn't take care of that yet, for now we just use # +each+ but fallback to +each_line+ on strings. # I wish we could remove that condition. # To be reviewed when a new Rack spec comes out. def headers=(key_value_pairs) key_value_pairs.each do |k, vs| next unless vs if vs.is_a?(String) vs.each_line { |v| @headers[k] = v.chomp } else vs.each { |v| @headers[k] = v.chomp } end end if key_value_pairs end end # Close any resource used by the response def close @body.close if @body.respond_to?(:close) end # Yields each chunk of the response. # To control the size of each chunk # define your own +each+ method on +body+. def each yield head unless @skip_body if @body.is_a?(String) yield @body else @body.each { |chunk| yield chunk } end end end # Tell the client the connection should stay open def persistent! @persistent = true end # Persistent connection must be requested as keep-alive # from the server and have a Content-Length, or the response # status must require that the connection remain open. def persistent? (@persistent && @headers.has_key?(CONTENT_LENGTH)) || PERSISTENT_STATUSES.include?(@status) end def skip_body! @skip_body = true end end end thin-1.6.3/lib/thin/statuses.rb0000644000004100000410000000250212432275201016420 0ustar www-datawww-datamodule Thin # Every standard HTTP code mapped to the appropriate message. # Stolent from Mongrel. HTTP_STATUS_CODES = { 100 => 'Continue', 101 => 'Switching Protocols', 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Moved Temporarily', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 422 => 'Unprocessable Entity', 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' } endthin-1.6.3/lib/thin/daemonizing.rb0000644000004100000410000001247712432275201017065 0ustar www-datawww-datarequire 'etc' require 'daemons' unless Thin.win? module Process # Returns +true+ the process identied by +pid+ is running. def running?(pid) Process.getpgid(pid) != -1 rescue Errno::EPERM true rescue Errno::ESRCH false end module_function :running? end module Thin # Raised when the pid file already exist starting as a daemon. class PidFileExist < RuntimeError; end class PidFileNotFound < RuntimeError; end # Module included in classes that can be turned into a daemon. # Handle stuff like: # * storing the PID in a file # * redirecting output to the log file # * changing process privileges # * killing the process gracefully module Daemonizable attr_accessor :pid_file, :log_file def self.included(base) base.extend ClassMethods end def pid File.exist?(pid_file) ? open(pid_file).read.to_i : nil end # Turns the current script into a daemon process that detaches from the console. def daemonize raise PlatformNotSupported, 'Daemonizing is not supported on Windows' if Thin.win? raise ArgumentError, 'You must specify a pid_file to daemonize' unless @pid_file remove_stale_pid_file pwd = Dir.pwd # Current directory is changed during daemonization, so store it # HACK we need to create the directory before daemonization to prevent a bug under 1.9 # ignoring all signals when the directory is created after daemonization. FileUtils.mkdir_p File.dirname(@pid_file) FileUtils.mkdir_p File.dirname(@log_file) Daemonize.daemonize(File.expand_path(@log_file), name) Dir.chdir(pwd) write_pid_file at_exit do log_info "Exiting!" remove_pid_file end end # Change privileges of the process # to the specified user and group. def change_privilege(user, group=user) log_info "Changing process privilege to #{user}:#{group}" uid, gid = Process.euid, Process.egid target_uid = Etc.getpwnam(user).uid target_gid = Etc.getgrnam(group).gid if uid != target_uid || gid != target_gid # Change PID file ownership File.chown(target_uid, target_gid, @pid_file) if File.exists?(@pid_file) # Change process ownership Process.initgroups(user, target_gid) Process::GID.change_privilege(target_gid) Process::UID.change_privilege(target_uid) end rescue Errno::EPERM => e log_info "Couldn't change user and group to #{user}:#{group}: #{e}" end # Register a proc to be called to restart the server. def on_restart(&block) @on_restart = block end # Restart the server. def restart if @on_restart log_info 'Restarting ...' stop remove_pid_file @on_restart.call exit! end end module ClassMethods # Send a QUIT or INT (if timeout is +0+) signal the process which # PID is stored in +pid_file+. # If the process is still running after +timeout+, KILL signal is # sent. def kill(pid_file, timeout=60) if timeout == 0 send_signal('INT', pid_file, timeout) else send_signal('QUIT', pid_file, timeout) end end # Restart the server by sending HUP signal. def restart(pid_file) send_signal('HUP', pid_file) end # Send a +signal+ to the process which PID is stored in +pid_file+. def send_signal(signal, pid_file, timeout=60) if pid = read_pid_file(pid_file) Logging.log_info "Sending #{signal} signal to process #{pid} ... " Process.kill(signal, pid) Timeout.timeout(timeout) do sleep 0.1 while Process.running?(pid) end else raise PidFileNotFound, "Can't stop process, no PID found in #{pid_file}" end rescue Timeout::Error Logging.log_info "Timeout!" force_kill(pid, pid_file) rescue Interrupt force_kill(pid, pid_file) rescue Errno::ESRCH # No such process Logging.log_info "process not found!" force_kill(pid, pid_file) end def force_kill(pid, pid_file) Logging.log_info "Sending KILL signal to process #{pid} ... " Process.kill("KILL", pid) File.delete(pid_file) if File.exist?(pid_file) end def read_pid_file(file) if File.file?(file) && pid = File.read(file) pid.to_i else nil end end end protected def remove_pid_file File.delete(@pid_file) if @pid_file && File.exists?(@pid_file) end def write_pid_file log_info "Writing PID to #{@pid_file}" open(@pid_file,"w") { |f| f.write(Process.pid) } File.chmod(0644, @pid_file) end # If PID file is stale, remove it. def remove_stale_pid_file if File.exist?(@pid_file) if pid && Process.running?(pid) raise PidFileExist, "#{@pid_file} already exists, seems like it's already running (process ID: #{pid}). " + "Stop the process or delete #{@pid_file}." else log_info "Deleting stale PID file #{@pid_file}" remove_pid_file end end end end end thin-1.6.3/lib/thin/command.rb0000644000004100000410000000276112432275201016172 0ustar www-datawww-datarequire 'open3' module Thin # Run a command through the +thin+ command-line script. class Command include Logging class << self # Path to the +thin+ script used to control the servers. # Leave this to default to use the one in the path. attr_accessor :script end def initialize(name, options={}) @name = name @options = options end def self.run(*args) new(*args).run end # Send the command to the +thin+ script def run shell_cmd = shellify trace shell_cmd trap('INT') {} # Ignore INT signal to pass CTRL+C to subprocess Open3.popen3(shell_cmd) do |stdin, stdout, stderr| log_info stdout.gets until stdout.eof? log_info stderr.gets until stderr.eof? end end # Turn into a runnable shell command def shellify shellified_options = @options.inject([]) do |args, (name, value)| option_name = name.to_s.tr("_", "-") case value when NilClass, TrueClass then args << "--#{option_name}" when FalseClass when Array then value.each { |v| args << "--#{option_name}=#{v.inspect}" } else args << "--#{option_name}=#{value.inspect}" end args end raise ArgumentError, "Path to thin script can't be found, set Command.script" unless self.class.script "#{self.class.script} #{@name} #{shellified_options.compact.join(' ')}" end end end thin-1.6.3/lib/thin/stats.html.erb0000644000004100000410000001427612432275201017026 0ustar www-datawww-data<%# # Taken from Rack::ShowException # adapted from Django # Copyright (c) 2005, the Lawrence Journal-World # Used under the modified BSD license: # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 %> Thin Stats

Server stats

<%= Thin::SERVER %>

Uptime <%= Time.now - @start_time %> sec
PID <%=h Process.pid %>
<% if @last_request %>

Jump to:

<% end %>

Requests

Stats

Requests <%= @requests %>
Finished <%= @requests_finished %>
Errors <%= @requests - @requests_finished %>
Last request <%= @last_request_time %> sec
<% if @last_request %>

Last Request information

GET

<% unless @last_request.GET.empty? %> <% @last_request.GET.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No GET data.

<% end %>

POST

<% unless @last_request.POST.empty? %> <% @last_request.POST.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No POST data.

<% end %> <% unless @last_request.cookies.empty? %> <% @last_request.cookies.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val.inspect %>
<% else %>

No cookie data.

<% end %>

Rack ENV

<% @last_request.env.sort_by { |k, v| k.to_s }.each { |key, val| %> <% } %>
Variable Value
<%=h key %>
<%=h val %>
<% end %>

You're seeing this page because you use Thin::Stats.

thin-1.6.3/lib/thin/request.rb0000644000004100000410000001143312432275201016240 0ustar www-datawww-datarequire 'tempfile' module Thin # Raised when an incoming request is not valid # and the server can not process it. class InvalidRequest < IOError; end # A request sent by the client to the server. class Request # Maximum request body size before it is moved out of memory # and into a tempfile for reading. MAX_BODY = 1024 * (80 + 32) BODY_TMPFILE = 'thin-body'.freeze MAX_HEADER = 1024 * (80 + 32) INITIAL_BODY = '' # Force external_encoding of request's body to ASCII_8BIT INITIAL_BODY.encode!(Encoding::ASCII_8BIT) if INITIAL_BODY.respond_to?(:encode!) # Freeze some HTTP header names & values SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze SERVER_NAME = 'SERVER_NAME'.freeze REQUEST_METHOD = 'REQUEST_METHOD'.freeze LOCALHOST = 'localhost'.freeze HTTP_VERSION = 'HTTP_VERSION'.freeze HTTP_1_0 = 'HTTP/1.0'.freeze REMOTE_ADDR = 'REMOTE_ADDR'.freeze CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze CONNECTION = 'HTTP_CONNECTION'.freeze KEEP_ALIVE_REGEXP = /\bkeep-alive\b/i.freeze CLOSE_REGEXP = /\bclose\b/i.freeze HEAD = 'HEAD'.freeze # Freeze some Rack header names RACK_INPUT = 'rack.input'.freeze RACK_VERSION = 'rack.version'.freeze RACK_ERRORS = 'rack.errors'.freeze RACK_MULTITHREAD = 'rack.multithread'.freeze RACK_MULTIPROCESS = 'rack.multiprocess'.freeze RACK_RUN_ONCE = 'rack.run_once'.freeze ASYNC_CALLBACK = 'async.callback'.freeze ASYNC_CLOSE = 'async.close'.freeze # CGI-like request environment variables attr_reader :env # Unparsed data of the request attr_reader :data # Request body attr_reader :body def initialize @parser = Thin::HttpParser.new @data = '' @nparsed = 0 @body = StringIO.new(INITIAL_BODY.dup) @env = { SERVER_SOFTWARE => SERVER, SERVER_NAME => LOCALHOST, # Rack stuff RACK_INPUT => @body, RACK_VERSION => VERSION::RACK, RACK_ERRORS => STDERR, RACK_MULTITHREAD => false, RACK_MULTIPROCESS => false, RACK_RUN_ONCE => false } end # Parse a chunk of data into the request environment # Raises an +InvalidRequest+ if invalid. # Returns +true+ if the parsing is complete. def parse(data) if @parser.finished? # Header finished, can only be some more body @body << data else # Parse more header using the super parser @data << data raise InvalidRequest, 'Header longer than allowed' if @data.size > MAX_HEADER @nparsed = @parser.execute(@env, @data, @nparsed) # Transfer to a tempfile if body is very big move_body_to_tempfile if @parser.finished? && content_length > MAX_BODY end if finished? # Check if header and body are complete @data = nil @body.rewind true # Request is fully parsed else false # Not finished, need more data end end # +true+ if headers and body are finished parsing def finished? @parser.finished? && @body.size >= content_length end # Expected size of the body def content_length @env[CONTENT_LENGTH].to_i end # Returns +true+ if the client expects the connection to be persistent. def persistent? # Clients and servers SHOULD NOT assume that a persistent connection # is maintained for HTTP versions less than 1.1 unless it is explicitly # signaled. (http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html) if @env[HTTP_VERSION] == HTTP_1_0 @env[CONNECTION] =~ KEEP_ALIVE_REGEXP # HTTP/1.1 client intends to maintain a persistent connection unless # a Connection header including the connection-token "close" was sent # in the request else @env[CONNECTION].nil? || @env[CONNECTION] !~ CLOSE_REGEXP end end def remote_address=(address) @env[REMOTE_ADDR] = address end def threaded=(value) @env[RACK_MULTITHREAD] = value end def async_callback=(callback) @env[ASYNC_CALLBACK] = callback @env[ASYNC_CLOSE] = EventMachine::DefaultDeferrable.new end def async_close @async_close ||= @env[ASYNC_CLOSE] end def head? @env[REQUEST_METHOD] == HEAD end # Close any resource used by the request def close @body.close! if @body.class == Tempfile end private def move_body_to_tempfile current_body = @body current_body.rewind @body = Tempfile.new(BODY_TMPFILE) @body.binmode @body << current_body.read @env[RACK_INPUT] = @body end end end thin-1.6.3/lib/thin/logging.rb0000644000004100000410000001026612432275201016201 0ustar www-datawww-datarequire 'logger' module Thin # To be included in classes to allow some basic logging # that can be silenced (Logging.silent=) or made # more verbose. # Logging.trace=: log all raw request and response and # messages logged with +trace+. # Logging.silent=: silence all log all log messages # altogether. module Logging # Simple formatter which only displays the message. # Taken from ActiveSupport class SimpleFormatter < Logger::Formatter def call(severity, timestamp, progname, msg) "#{String === msg ? msg : msg.inspect}\n" end end class << self attr_reader :logger attr_reader :trace_logger def trace=(enabled) if enabled @trace_logger ||= Logger.new(STDOUT) else @trace_logger = nil end end def trace? !@trace_logger.nil? end def silent=(shh) if shh @logger = nil else @logger ||= Logger.new(STDOUT) end end def silent? !@logger.nil? end def level @logger ? @logger.level : nil # or 'silent' end def level=(value) # If logging has been silenced, then re-enable logging @logger = Logger.new(STDOUT) if @logger.nil? @logger.level = value end # Allow user to specify a custom logger to use. # This object must respond to: # +level+, +level=+ and +debug+, +info+, +warn+, +error+, +fatal+ def logger=(custom_logger) [ :level , :level= , :debug , :info , :warn , :error , :fatal , :unknown , ].each do |method| if not custom_logger.respond_to?(method) raise ArgumentError, "logger must respond to #{method}" end end @logger = custom_logger end def trace_logger=(custom_tracer) [ :level , :level= , :debug , :info , :warn , :error , :fatal , :unknown , ].each do |method| if not custom_tracer.respond_to?(method) raise ArgumentError, "trace logger must respond to #{method}" end end @trace_logger = custom_tracer end def log_msg(msg, level=Logger::INFO) return unless @logger @logger.add(level, msg) end def trace_msg(msg) return unless @trace_logger @trace_logger.info(msg) end # Provided for backwards compatibility. # Callers should be using the +level+ (on the +Logging+ module # or on the instance) to figure out what the log level is. def debug? self.level == Logger::DEBUG end def debug=(val) self.level = (val ? Logger::DEBUG : Logger::INFO) end end # module methods # Default logger to stdout. self.logger = Logger.new(STDOUT) self.logger.level = Logger::INFO self.logger.formatter = Logging::SimpleFormatter.new def silent Logging.silent? end def silent=(value) Logging.silent = value end # Log a message if tracing is activated def trace(msg=nil) Logging.trace_msg(msg) if msg end module_function :trace public :trace # Log a message at DEBUG level def log_debug(msg=nil) Logging.log_msg(msg || yield, Logger::DEBUG) end module_function :log_debug public :log_debug # Log a message at INFO level def log_info(msg) Logging.log_msg(msg || yield, Logger::INFO) end module_function :log_info public :log_info # Log a message at ERROR level (and maybe a backtrace) def log_error(msg, e=nil) log_msg = msg + ": #{e}\n\t" + e.backtrace.join("\n\t") + "\n" if e Logging.log_msg(log_msg, Logger::ERROR) end module_function :log_error public :log_error # For backwards compatibility def log msg STDERR.puts('#log has been deprecated, please use the ' \ 'log_level function instead (e.g. - log_info).') log_info(msg) end end end thin-1.6.3/lib/thin/version.rb0000644000004100000410000000122412432275201016232 0ustar www-datawww-datamodule Thin # Raised when a feature is not supported on the # current platform. class PlatformNotSupported < RuntimeError; end module VERSION #:nodoc: MAJOR = 1 MINOR = 6 TINY = 3 STRING = [MAJOR, MINOR, TINY].join('.') CODENAME = "Protein Powder".freeze RACK = [1, 0].freeze # Rack protocol version end NAME = 'thin'.freeze SERVER = "#{NAME} #{VERSION::STRING} codename #{VERSION::CODENAME}".freeze def self.win? RUBY_PLATFORM =~ /mswin|mingw/ end def self.linux? RUBY_PLATFORM =~ /linux/ end def self.ruby_18? RUBY_VERSION =~ /^1\.8/ end end thin-1.6.3/lib/thin/runner.rb0000644000004100000410000002554412432275201016071 0ustar www-datawww-datarequire 'logger' require 'optparse' require 'yaml' module Thin # CLI runner. # Parse options and send command to the correct Controller. class Runner COMMANDS = %w(start stop restart config) LINUX_ONLY_COMMANDS = %w(install) # Commands that wont load options from the config file CONFIGLESS_COMMANDS = %w(config install) # Parsed options attr_accessor :options # Name of the command to be runned. attr_accessor :command # Arguments to be passed to the command. attr_accessor :arguments # Return all available commands def self.commands commands = COMMANDS commands += LINUX_ONLY_COMMANDS if Thin.linux? commands end def initialize(argv) @argv = argv # Default options values @options = { :chdir => Dir.pwd, :environment => ENV['RACK_ENV'] || 'development', :address => '0.0.0.0', :port => Server::DEFAULT_PORT, :timeout => Server::DEFAULT_TIMEOUT, :log => File.join(Dir.pwd, 'log/thin.log'), :pid => 'tmp/pids/thin.pid', :max_conns => Server::DEFAULT_MAXIMUM_CONNECTIONS, :max_persistent_conns => Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS, :require => [], :wait => Controllers::Cluster::DEFAULT_WAIT_TIME, :threadpool_size => 20 } parse! end def parser # NOTE: If you add an option here make sure the key in the +options+ hash is the # same as the name of the command line option. # +option+ keys are used to build the command line to launch other processes, # see lib/thin/command.rb. @parser ||= OptionParser.new do |opts| opts.banner = "Usage: thin [options] #{self.class.commands.join('|')}" opts.separator "" opts.separator "Server options:" opts.on("-a", "--address HOST", "bind to HOST address " + "(default: #{@options[:address]})") { |host| @options[:address] = host } opts.on("-p", "--port PORT", "use PORT (default: #{@options[:port]})") { |port| @options[:port] = port.to_i } opts.on("-S", "--socket FILE", "bind to unix domain socket") { |file| @options[:socket] = file } opts.on("-y", "--swiftiply [KEY]", "Run using swiftiply") { |key| @options[:swiftiply] = key } opts.on("-A", "--adapter NAME", "Rack adapter to use (default: autodetect)", "(#{Rack::ADAPTERS.map{|(a,b)|a}.join(', ')})") { |name| @options[:adapter] = name } opts.on("-R", "--rackup FILE", "Load a Rack config file instead of " + "Rack adapter") { |file| @options[:rackup] = file } opts.on("-c", "--chdir DIR", "Change to dir before starting") { |dir| @options[:chdir] = File.expand_path(dir) } opts.on( "--stats PATH", "Mount the Stats adapter under PATH") { |path| @options[:stats] = path } opts.separator "" opts.separator "SSL options:" opts.on( "--ssl", "Enables SSL") { @options[:ssl] = true } opts.on( "--ssl-key-file PATH", "Path to private key") { |path| @options[:ssl_key_file] = path } opts.on( "--ssl-cert-file PATH", "Path to certificate") { |path| @options[:ssl_cert_file] = path } opts.on( "--ssl-disable-verify", "Disables (optional) client cert requests") { @options[:ssl_disable_verify] = true } opts.separator "" opts.separator "Adapter options:" opts.on("-e", "--environment ENV", "Framework environment " + "(default: #{@options[:environment]})") { |env| @options[:environment] = env } opts.on( "--prefix PATH", "Mount the app under PATH (start with /)") { |path| @options[:prefix] = path } unless Thin.win? # Daemonizing not supported on Windows opts.separator "" opts.separator "Daemon options:" opts.on("-d", "--daemonize", "Run daemonized in the background") { @options[:daemonize] = true } opts.on("-l", "--log FILE", "File to redirect output " + "(default: #{@options[:log]})") { |file| @options[:log] = file } opts.on("-P", "--pid FILE", "File to store PID " + "(default: #{@options[:pid]})") { |file| @options[:pid] = file } opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| @options[:user] = user } opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)") { |group| @options[:group] = group } opts.on( "--tag NAME", "Additional text to display in process listing") { |tag| @options[:tag] = tag } opts.separator "" opts.separator "Cluster options:" opts.on("-s", "--servers NUM", "Number of servers to start") { |num| @options[:servers] = num.to_i } opts.on("-o", "--only NUM", "Send command to only one server of the cluster") { |only| @options[:only] = only.to_i } opts.on("-C", "--config FILE", "Load options from config file") { |file| @options[:config] = file } opts.on( "--all [DIR]", "Send command to each config files in DIR") { |dir| @options[:all] = dir } if Thin.linux? opts.on("-O", "--onebyone", "Restart the cluster one by one (only works with restart command)") { @options[:onebyone] = true } opts.on("-w", "--wait NUM", "Maximum wait time for server to be started in seconds (use with -O)") { |time| @options[:wait] = time.to_i } end opts.separator "" opts.separator "Tuning options:" opts.on("-b", "--backend CLASS", "Backend to use, full classname") { |name| @options[:backend] = name } opts.on("-t", "--timeout SEC", "Request or command timeout in sec " + "(default: #{@options[:timeout]})") { |sec| @options[:timeout] = sec.to_i } opts.on("-f", "--force", "Force the execution of the command") { @options[:force] = true } opts.on( "--max-conns NUM", "Maximum number of open file descriptors " + "(default: #{@options[:max_conns]})", "Might require sudo to set higher than 1024") { |num| @options[:max_conns] = num.to_i } unless Thin.win? opts.on( "--max-persistent-conns NUM", "Maximum number of persistent connections", "(default: #{@options[:max_persistent_conns]})") { |num| @options[:max_persistent_conns] = num.to_i } opts.on( "--threaded", "Call the Rack application in threads " + "[experimental]") { @options[:threaded] = true } opts.on( "--threadpool-size NUM", "Sets the size of the EventMachine threadpool.", "(default: #{@options[:threadpool_size]})") { |num| @options[:threadpool_size] = num.to_i } opts.on( "--no-epoll", "Disable the use of epoll") { @options[:no_epoll] = true } if Thin.linux? opts.separator "" opts.separator "Common options:" opts.on_tail("-r", "--require FILE", "require the library") { |file| @options[:require] << file } opts.on_tail("-q", "--quiet", "Silence all logging") { @options[:quiet] = true } opts.on_tail("-D", "--debug", "Enable debug logging") { @options[:debug] = true } opts.on_tail("-V", "--trace", "Set tracing on (log raw request/response)") { @options[:trace] = true } opts.on_tail("-h", "--help", "Show this message") { puts opts; exit } opts.on_tail('-v', '--version', "Show version") { puts Thin::SERVER; exit } end end # Parse the options. def parse! parser.parse! @argv @command = @argv.shift @arguments = @argv end # Parse the current shell arguments and run the command. # Exits on error. def run! if self.class.commands.include?(@command) run_command elsif @command.nil? puts "Command required" puts @parser exit 1 else abort "Unknown command: #{@command}. Use one of #{self.class.commands.join(', ')}" end end # Send the command to the controller: single instance or cluster. def run_command load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command) # PROGRAM_NAME is relative to the current directory, so make sure # we store and expand it before changing directory. Command.script = File.expand_path($PROGRAM_NAME) # Change the current directory ASAP so that all relative paths are # relative to this one. Dir.chdir(@options[:chdir]) unless CONFIGLESS_COMMANDS.include?(@command) @options[:require].each { |r| ruby_require r } # Setup the logger if @options[:quiet] Logging.silent = true else Logging.level = Logger::DEBUG if @options[:debug] end if @options[:trace] # Trace raw requests/responses Logging.trace_logger = Logging.logger end controller = case when cluster? then Controllers::Cluster.new(@options) when service? then Controllers::Service.new(@options) else Controllers::Controller.new(@options) end if controller.respond_to?(@command) begin controller.send(@command, *@arguments) rescue RunnerError => e abort e.message end else abort "Invalid options for command: #{@command}" end end # +true+ if we're controlling a cluster. def cluster? @options[:only] || @options[:servers] || @options[:config] end # +true+ if we're acting a as system service. def service? @options.has_key?(:all) || @command == 'install' end private def load_options_from_config_file! if file = @options.delete(:config) YAML.load_file(file).each { |key, value| @options[key.to_sym] = value } end end def ruby_require(file) if File.extname(file) == '.ru' warn 'WARNING: Use the -R option to load a Rack config file' @options[:rackup] = file else require file end end end end thin-1.6.3/lib/thin/stats.rb0000644000004100000410000000212412432275201015703 0ustar www-datawww-datarequire 'erb' module Thin module Stats # Rack adapter to log stats about a Rack application. class Adapter include ERB::Util def initialize(app, path='/stats') @app = app @path = path @template = ERB.new(File.read(File.dirname(__FILE__) + '/stats.html.erb')) @requests = 0 @requests_finished = 0 @start_time = Time.now end def call(env) if env['PATH_INFO'].index(@path) == 0 serve(env) else log(env) { @app.call(env) } end end def log(env) @requests += 1 @last_request = Rack::Request.new(env) request_started_at = Time.now response = yield @requests_finished += 1 @last_request_time = Time.now - request_started_at response end def serve(env) body = @template.result(binding) [ 200, { 'Content-Type' => 'text/html' }, [body] ] end end end end thin-1.6.3/lib/thin/server.rb0000644000004100000410000002331612432275201016061 0ustar www-datawww-datamodule Thin # The uterly famous Thin HTTP server. # It listen for incoming request through a given +backend+ # and forward all request to +app+. # # == TCP server # Create a new TCP server on bound to host:port by specifiying +host+ # and +port+ as the first 2 arguments. # # Thin::Server.start('0.0.0.0', 3000, app) # # == UNIX domain server # Create a new UNIX domain socket bound to +socket+ file by specifiying a filename # as the first argument. Eg.: /tmp/thin.sock. If the first argument contains a / # it will be assumed to be a UNIX socket. # # Thin::Server.start('/tmp/thin.sock', app) # # == Using a custom backend # You can implement your own way to connect the server to its client by creating your # own Backend class and pass it as the :backend option. # # Thin::Server.start('galaxy://faraway', 1345, app, :backend => Thin::Backends::MyFancyBackend) # # == Rack application (+app+) # All requests will be processed through +app+ that must be a valid Rack adapter. # A valid Rack adapter (application) must respond to call(env#Hash) and # return an array of [status, headers, body]. # # == Building an app in place # If a block is passed, a Rack::Builder instance # will be passed to build the +app+. So you can do cool stuff like this: # # Thin::Server.start('0.0.0.0', 3000) do # use Rack::CommonLogger # use Rack::ShowExceptions # map "/lobster" do # use Rack::Lint # run Rack::Lobster.new # end # end # # == Controlling with signals # * INT and TERM: Force shutdown (see Server#stop!) # * TERM & QUIT calls +stop+ to shutdown gracefully. # * HUP calls +restart+ to ... surprise, restart! # * USR1 reopen log files. # Signals are processed at one second intervals. # Disable signals by passing :signals => false. # class Server include Logging include Daemonizable extend Forwardable # Default values DEFAULT_TIMEOUT = 30 #sec DEFAULT_HOST = '0.0.0.0' DEFAULT_PORT = 3000 DEFAULT_MAXIMUM_CONNECTIONS = 1024 DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS = 100 # Application (Rack adapter) called with the request that produces the response. attr_accessor :app # A tag that will show in the process listing attr_accessor :tag # Backend handling the connections to the clients. attr_accessor :backend # Maximum number of seconds for incoming data to arrive before the connection # is dropped. def_delegators :backend, :timeout, :timeout= # Maximum number of file or socket descriptors that the server may open. def_delegators :backend, :maximum_connections, :maximum_connections= # Maximum number of connection that can be persistent at the same time. # Most browser never close the connection so most of the time they are closed # when the timeout occur. If we don't control the number of persistent connection, # if would be very easy to overflow the server for a DoS attack. def_delegators :backend, :maximum_persistent_connections, :maximum_persistent_connections= # Allow using threads in the backend. def_delegators :backend, :threaded?, :threaded=, :threadpool_size, :threadpool_size= # Allow using SSL in the backend. def_delegators :backend, :ssl?, :ssl=, :ssl_options= # Address and port on which the server is listening for connections. def_delegators :backend, :host, :port # UNIX domain socket on which the server is listening for connections. def_delegator :backend, :socket # Disable the use of epoll under Linux def_delegators :backend, :no_epoll, :no_epoll= def initialize(*args, &block) host, port, options = DEFAULT_HOST, DEFAULT_PORT, {} # Guess each parameter by its type so they can be # received in any order. args.each do |arg| case arg when Fixnum, /^\d+$/ then port = arg.to_i when String then host = arg when Hash then options = arg else @app = arg if arg.respond_to?(:call) end end # Set tag if needed self.tag = options[:tag] # Try to intelligently select which backend to use. @backend = select_backend(host, port, options) load_cgi_multipart_eof_fix @backend.server = self # Set defaults @backend.maximum_connections = DEFAULT_MAXIMUM_CONNECTIONS @backend.maximum_persistent_connections = DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS @backend.timeout = DEFAULT_TIMEOUT # Allow using Rack builder as a block @app = Rack::Builder.new(&block).to_app if block # If in debug mode, wrap in logger adapter @app = Rack::CommonLogger.new(@app) if Logging.debug? @setup_signals = options[:signals] != false end # Lil' shortcut to turn this: # # Server.new(...).start # # into this: # # Server.start(...) # def self.start(*args, &block) new(*args, &block).start! end # Start the server and listen for connections. def start raise ArgumentError, 'app required' unless @app log_info "Thin web server (v#{VERSION::STRING} codename #{VERSION::CODENAME})" log_debug "Debugging ON" trace "Tracing ON" log_info "Maximum connections set to #{@backend.maximum_connections}" log_info "Listening on #{@backend}, CTRL+C to stop" @backend.start { setup_signals if @setup_signals } end alias :start! :start # == Gracefull shutdown # Stops the server after processing all current connections. # As soon as this method is called, the server stops accepting # new requests and wait for all current connections to finish. # Calling twice is the equivalent of calling stop!. def stop if running? @backend.stop unless @backend.empty? log_info "Waiting for #{@backend.size} connection(s) to finish, "\ "can take up to #{timeout} sec, CTRL+C to stop now" end else stop! end end # == Force shutdown # Stops the server closing all current connections right away. # This doesn't wait for connection to finish their work and send data. # All current requests will be dropped. def stop! if @backend.started_reactor? log_info "Stopping ..." else log_info "Stopping Thin ..." log_info "Thin was started inside an existing EventMachine.run block." log_info "Call `EventMachine.stop` to stop the reactor and quit the process." end @backend.stop! end # == Reopen log file. # Reopen the log file and redirect STDOUT and STDERR to it. def reopen_log return unless log_file file = File.expand_path(log_file) log_info "Reopening log file: #{file}" Daemonize.redirect_io(file) end # == Configure the server # The process might need to have superuser privilege to configure # server with optimal options. def config @backend.config end # Name of the server and type of backend used. # This is also the name of the process in which Thin is running as a daemon. def name "thin server (#{@backend})" + (tag ? " [#{tag}]" : "") end alias :to_s :name # Return +true+ if the server is running and ready to receive requests. # Note that the server might still be running and return +false+ when # shuting down and waiting for active connections to complete. def running? @backend.running? end protected def setup_signals # Queue up signals so they are processed in non-trap context # using a EM timer. @signal_queue ||= [] %w( INT TERM ).each do |signal| trap(signal) { @signal_queue.push signal } end # *nix only signals %w( QUIT HUP USR1 ).each do |signal| trap(signal) { @signal_queue.push signal } end unless Thin.win? # Signals are processed at one second intervals. @signal_timer ||= EM.add_periodic_timer(1) { handle_signals } end def handle_signals case @signal_queue.shift when 'INT' stop! when 'TERM', 'QUIT' stop when 'HUP' restart when 'USR1' reopen_log end EM.next_tick { handle_signals } unless @signal_queue.empty? end def select_backend(host, port, options) case when options.has_key?(:backend) raise ArgumentError, ":backend must be a class" unless options[:backend].is_a?(Class) options[:backend].new(host, port, options) when options.has_key?(:swiftiply) Backends::SwiftiplyClient.new(host, port, options) when host.include?('/') Backends::UnixServer.new(host) else Backends::TcpServer.new(host, port) end end # Taken from Mongrel cgi_multipart_eof_fix # Ruby 1.8.5 has a security bug in cgi.rb, we need to patch it. def load_cgi_multipart_eof_fix version = RUBY_VERSION.split('.').map { |i| i.to_i } if version[0] <= 1 && version[1] <= 8 && version[2] <= 5 && RUBY_PLATFORM !~ /java/ begin require 'cgi_multipart_eof_fix' rescue LoadError log_error "Ruby 1.8.5 is not secure please install cgi_multipart_eof_fix:" log_error "gem install cgi_multipart_eof_fix" end end end end end thin-1.6.3/lib/rack/0000755000004100000410000000000012432275201014177 5ustar www-datawww-datathin-1.6.3/lib/rack/adapter/0000755000004100000410000000000012432275201015617 5ustar www-datawww-datathin-1.6.3/lib/rack/adapter/loader.rb0000644000004100000410000000441712432275201017420 0ustar www-datawww-datamodule Rack class AdapterNotFound < RuntimeError; end # Mapping used to guess which adapter to use in Adapter.for. # Framework => in order they will # be tested. # +nil+ for value to never guess. # NOTE: If a framework has a file that is not unique, make sure to place # it at the end. ADAPTERS = [ [:rack, 'config.ru'], [:rails, 'config/environment.rb'], [:ramaze, 'start.rb'], [:merb, 'config/init.rb'], [:file, nil] ] module Adapter # Guess which adapter to use based on the directory structure # or file content. # Returns a symbol representing the name of the adapter to use # to load the application under dir/. def self.guess(dir) ADAPTERS.each do |adapter, file| return adapter if file && ::File.exist?(::File.join(dir, file)) end raise AdapterNotFound, "No adapter found for #{dir}" end # Load a Rack application from a Rack config file (.ru). def self.load(config) rackup_code = ::File.read(config) eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, config) end # Loads an adapter identified by +name+ using +options+ hash. def self.for(name, options={}) ENV['RACK_ENV'] = options[:environment] case name.to_sym when :rack return load(::File.join(options[:chdir], "config.ru")) when :rails return Rails.new(options.merge(:root => options[:chdir])) when :ramaze require "#{options[:chdir]}/start" Ramaze.trait[:essentials].delete Ramaze::Adapter Ramaze.start :force => true return Ramaze::Adapter::Base when :merb require 'merb-core' Merb::Config.setup(:merb_root => options[:chdir], :environment => options[:environment]) Merb.environment = Merb::Config[:environment] Merb.root = Merb::Config[:merb_root] Merb::BootLoader.run return Merb::Rack::Application.new when :file return Rack::File.new(options[:chdir]) else raise AdapterNotFound, "Adapter not found: #{name}" end end end endthin-1.6.3/lib/rack/adapter/rails.rb0000644000004100000410000001353412432275201017264 0ustar www-datawww-datarequire 'cgi' # Adapter to run a Rails app with any supported Rack handler. # By default it will try to load the Rails application in the # current directory in the development environment. # # Options: # root: Root directory of the Rails app # environment: Rails environment to run in (development [default], production or test) # prefix: Set the relative URL root. # # Based on http://fuzed.rubyforge.org/ Rails adapter module Rack module Adapter class Rails FILE_METHODS = %w(GET HEAD).freeze def initialize(options = {}) @root = options[:root] || Dir.pwd @env = options[:environment] || 'development' @prefix = options[:prefix] load_application @rails_app = self.class.rack_based? ? ActionController::Dispatcher.new : CgiApp.new @file_app = Rack::File.new(::File.join(RAILS_ROOT, "public")) end def load_application ENV['RAILS_ENV'] = @env require "#{@root}/config/environment" require 'dispatcher' if @prefix if ActionController::Base.respond_to?(:relative_url_root=) ActionController::Base.relative_url_root = @prefix # Rails 2.1.1 else ActionController::AbstractRequest.relative_url_root = @prefix end end end def file_exist?(path) full_path = ::File.join(@file_app.root, Utils.unescape(path)) ::File.file?(full_path) && ::File.readable_real?(full_path) end def call(env) path = env['PATH_INFO'].chomp('/') method = env['REQUEST_METHOD'] cached_path = (path.empty? ? 'index' : path) + ActionController::Base.page_cache_extension if FILE_METHODS.include?(method) if file_exist?(path) # Serve the file if it's there return @file_app.call(env) elsif file_exist?(cached_path) # Serve the page cache if it's there env['PATH_INFO'] = cached_path return @file_app.call(env) end end # No static file, let Rails handle it @rails_app.call(env) end def self.rack_based? rails_version = ::Rails::VERSION return false if rails_version::MAJOR < 2 return false if rails_version::MAJOR == 2 && rails_version::MINOR < 2 return false if rails_version::MAJOR == 2 && rails_version::MINOR == 2 && rails_version::TINY < 3 true # >= 2.2.3 end protected # For Rails pre Rack (2.3) class CgiApp def call(env) request = Request.new(env) response = Response.new session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS cgi = CGIWrapper.new(request, response) Dispatcher.dispatch(cgi, session_options, response) response.finish end end class CGIWrapper < ::CGI def initialize(request, response, *args) @request = request @response = response @args = *args @input = request.body super *args end def header(options = 'text/html') if options.is_a?(String) @response['Content-Type'] = options unless @response['Content-Type'] else @response['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length'] @response['Content-Type'] = options.delete('type') || "text/html" @response['Content-Type'] += '; charset=' + options.delete('charset') if options['charset'] @response['Content-Language'] = options.delete('language') if options['language'] @response['Expires'] = options.delete('expires') if options['expires'] @response.status = options.delete('Status') if options['Status'] # Convert 'cookie' header to 'Set-Cookie' headers. # Because Set-Cookie header can appear more the once in the response body, # we store it in a line break seperated string that will be translated to # multiple Set-Cookie header by the handler. if cookie = options.delete('cookie') cookies = [] case cookie when Array then cookie.each { |c| cookies << c.to_s } when Hash then cookie.each { |_, c| cookies << c.to_s } else cookies << cookie.to_s end @output_cookies.each { |c| cookies << c.to_s } if @output_cookies @response['Set-Cookie'] = [@response['Set-Cookie'], cookies].compact # See http://groups.google.com/group/rack-devel/browse_thread/thread/e8759b91a82c5a10/a8dbd4574fe97d69?#a8dbd4574fe97d69 if Thin.ruby_18? @response['Set-Cookie'].flatten! else @response['Set-Cookie'] = @response['Set-Cookie'].join("\n") end end options.each { |k, v| @response[k] = v } end '' end def params @params ||= @request.params end def cookies @request.cookies end def query_string @request.query_string end # Used to wrap the normal args variable used inside CGI. def args @args end # Used to wrap the normal env_table variable used inside CGI. def env_table @request.env end # Used to wrap the normal stdinput variable used inside CGI. def stdinput @input end def stdoutput STDERR.puts 'stdoutput should not be used.' @response.body end end end end end thin-1.6.3/lib/thin.rb0000644000004100000410000000242112432275201014545 0ustar www-datawww-datarequire 'fileutils' require 'timeout' require 'stringio' require 'time' require 'forwardable' require 'openssl' require 'eventmachine' require 'rack' module Thin autoload :Command, "thin/command" autoload :Connection, "thin/connection" autoload :Daemonizable, "thin/daemonizing" autoload :Logging, "thin/logging" autoload :Headers, "thin/headers" autoload :Request, "thin/request" autoload :Response, "thin/response" autoload :Runner, "thin/runner" autoload :Server, "thin/server" autoload :Stats, "thin/stats" module Backends autoload :Base, "thin/backends/base" autoload :SwiftiplyClient, "thin/backends/swiftiply_client" autoload :TcpServer, "thin/backends/tcp_server" autoload :UnixServer, "thin/backends/unix_server" end module Controllers autoload :Cluster, "thin/controllers/cluster" autoload :Controller, "thin/controllers/controller" autoload :Service, "thin/controllers/service" end end require "thin/version" require "thin/statuses" require "rack/adapter/loader" require "thin_parser" module Rack module Adapter autoload :Rails, "rack/adapter/rails" end end thin-1.6.3/metadata.yml0000644000004100000410000000646612432275201015030 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: thin version: !ruby/object:Gem::Version version: 1.6.3 platform: ruby authors: - Marc-Andre Cournoyer autorequire: bindir: bin cert_chain: [] date: 2014-10-02 00:00:00.000000000 Z dependencies: - !ruby/object:Gem::Dependency name: rack requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' - !ruby/object:Gem::Dependency name: eventmachine requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' - !ruby/object:Gem::Dependency name: daemons requirement: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' - - ">=" - !ruby/object:Gem::Version version: 1.0.9 type: :runtime prerelease: false version_requirements: !ruby/object:Gem::Requirement requirements: - - "~>" - !ruby/object:Gem::Version version: '1.0' - - ">=" - !ruby/object:Gem::Version version: 1.0.9 description: A thin and fast web server email: macournoyer@gmail.com executables: - thin extensions: - ext/thin_parser/extconf.rb extra_rdoc_files: [] files: - CHANGELOG - README.md - Rakefile - bin/thin - example/adapter.rb - example/async_app.ru - example/async_chat.ru - example/async_tailer.ru - example/config.ru - example/monit_sockets - example/monit_unixsock - example/myapp.rb - example/ramaze.ru - example/thin.god - example/thin_solaris_smf.erb - example/thin_solaris_smf.readme.txt - example/vlad.rake - ext/thin_parser/common.rl - ext/thin_parser/ext_help.h - ext/thin_parser/extconf.rb - ext/thin_parser/parser.c - ext/thin_parser/parser.h - ext/thin_parser/parser.rl - ext/thin_parser/thin.c - lib/rack/adapter/loader.rb - lib/rack/adapter/rails.rb - lib/thin.rb - lib/thin/backends/base.rb - lib/thin/backends/swiftiply_client.rb - lib/thin/backends/tcp_server.rb - lib/thin/backends/unix_server.rb - lib/thin/command.rb - lib/thin/connection.rb - lib/thin/controllers/cluster.rb - lib/thin/controllers/controller.rb - lib/thin/controllers/service.rb - lib/thin/controllers/service.sh.erb - lib/thin/daemonizing.rb - lib/thin/headers.rb - lib/thin/logging.rb - lib/thin/request.rb - lib/thin/response.rb - lib/thin/runner.rb - lib/thin/server.rb - lib/thin/stats.html.erb - lib/thin/stats.rb - lib/thin/statuses.rb - lib/thin/version.rb homepage: http://code.macournoyer.com/thin/ licenses: - GPLv2+ - Ruby 1.8 metadata: {} post_install_message: rdoc_options: [] require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: 1.8.5 required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: '0' requirements: [] rubyforge_project: thin rubygems_version: 2.2.2 signing_key: specification_version: 4 summary: A thin and fast web server test_files: [] thin-1.6.3/example/0000755000004100000410000000000012432275201014144 5ustar www-datawww-datathin-1.6.3/example/thin.god0000644000004100000410000000423612432275201015606 0ustar www-datawww-data# == God config file # http://god.rubyforge.org/ # Authors: Gump and michael@glauche.de # # Config file for god that configures watches for each instance of a thin server for # each thin configuration file found in /etc/thin. # In order to get it working on Ubuntu, I had to make a change to god as noted at # the following blog: # http://blog.alexgirard.com/ruby-one-line-to-save-god/ # require 'yaml' config_path = "/etc/thin" Dir[config_path + "/*.yml"].each do |file| config = YAML.load_file(file) num_servers = config["servers"] ||= 1 (0...num_servers).each do |i| # UNIX socket cluster use number 0 to 2 (for 3 servers) # and tcp cluster use port number 3000 to 3002. number = config['socket'] ? i : (config['port'] + i) God.watch do |w| w.group = "thin-" + File.basename(file, ".yml") w.name = w.group + "-#{number}" w.interval = 30.seconds w.uid = config["user"] w.gid = config["group"] w.start = "thin start -C #{file} -o #{number}" w.start_grace = 10.seconds w.stop = "thin stop -C #{file} -o #{number}" w.stop_grace = 10.seconds w.restart = "thin restart -C #{file} -o #{number}" pid_path = config["chdir"] + "/" + config["pid"] ext = File.extname(pid_path) w.pid_file = pid_path.gsub(/#{ext}$/, ".#{number}#{ext}") w.behavior(:clean_pid_file) w.start_if do |start| start.condition(:process_running) do |c| c.interval = 5.seconds c.running = false end end w.restart_if do |restart| restart.condition(:memory_usage) do |c| c.above = 150.megabytes c.times = [3,5] # 3 out of 5 intervals end restart.condition(:cpu_usage) do |c| c.above = 50.percent c.times = 5 end end w.lifecycle do |on| on.condition(:flapping) do |c| c.to_state = [:start, :restart] c.times = 5 c.within = 5.minutes c.transition = :unmonitored c.retry_in = 10.minutes c.retry_times = 5 c.retry_within = 2.hours end end end end endthin-1.6.3/example/monit_unixsock0000644000004100000410000000203412432275201017137 0ustar www-datawww-datacheck process blog1 with pidfile /u/apps/blog/shared/pids/thin.1.pid start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.1.sock -P tmp/pids/thin.1.pid -c /u/apps/blog/current" stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.1.pid" if totalmem > 90.0 MB for 5 cycles then restart if failed unixsocket /u/apps/blog/shared/pids/thin.1.sock then restart if cpu usage > 95% for 3 cycles then restart if 5 restarts within 5 cycles then timeout group blog check process blog2 with pidfile /u/apps/blog/shared/pids/thin.2.pid start program = "ruby thin start -d -e production -S /u/apps/blog/shared/pids/thin.2.sock -P tmp/pids/thin.2.pid -c /u/apps/blog/current" stop program = "ruby thin stop -P /u/apps/blog/shared/pids/thin.2.pid" if totalmem > 90.0 MB for 5 cycles then restart if failed unixsocket /u/apps/blog/shared/pids/thin.2.sock then restart if cpu usage > 95% for 3 cycles then restart if 5 restarts within 5 cycles then timeout group blog thin-1.6.3/example/async_chat.ru0000644000004100000410000001377612432275201016646 0ustar www-datawww-data#!/usr/bin/env rackup -s thin # # async_chat.ru # raggi/thin # # Created by James Tucker on 2008-06-19. # Copyright 2008 James Tucker . # Uncomment if appropriate for you.. EM.epoll # EM.kqueue # bug on OS X in 0.12? class DeferrableBody include EventMachine::Deferrable def initialize @queue = [] end def schedule_dequeue return unless @body_callback EventMachine::next_tick do next unless body = @queue.shift body.each do |chunk| @body_callback.call(chunk) end schedule_dequeue unless @queue.empty? end end def call(body) @queue << body schedule_dequeue end def each &blk @body_callback = blk schedule_dequeue end end class Chat module UserBody attr_accessor :username end def initialize @users = {} end def render_page [] << <<-EOPAGE Async Chat
Your first message will become your nickname! Users: #{@users.map{|k,u|u.username}.join(', ')}
EOPAGE end def register_user(user_id, renderer) body = create_user(user_id) body.call render_page body.errback { delete_user user_id } body.callback { delete_user user_id } EventMachine::next_tick do renderer.call [200, {'Content-Type' => 'text/html'}, body] end end def new_message(user_id, message) return unless @users[user_id] if @users[user_id].username == :anonymous username = unique_username(message) log "User: #{user_id} is #{username}" @users[user_id].username = message message = "-> #{username} signed on." end username ||= @users[user_id].username log "User: #{username} sent: #{message}" @users.each do |id, body| EventMachine::next_tick { body.call [js_message(username, message)] } end end private def unique_username(name) name.concat('_') while @users.any? { |id,u| name == u.username } name end def log(str) print str, "\n" end def add_user(id, body) @users[id] = body end def delete_user(id) message = "User: #{id} - #{@users[id].username if @users[id]} disconnected." log message new_message(id, message) @users.delete id end def js_message(username, message) %() end def create_user(id) message = "User: #{id} connected." log message new_message(id, message) body = DeferrableBody.new body.extend UserBody body.username = :anonymous add_user(id, body) body end end class AsyncChat AsyncResponse = [-1, {}, []].freeze AjaxResponse = [200, {}, []].freeze def initialize @chat = Chat.new end def call(env) request = Rack::Request.new(env) # TODO - cookie me, baby user_id = request.env['REMOTE_ADDR'] if request.xhr? message = request['message'] @chat.new_message(user_id, Rack::Utils.escape_html(message)) AjaxResponse else renderer = request.env['async.callback'] @chat.register_user(user_id, renderer) AsyncResponse end end end run AsyncChat.new thin-1.6.3/example/thin_solaris_smf.readme.txt0000644000004100000410000001214112432275201021503 0ustar www-datawww-dataUsing Thin with Solaris' SMF Monitoring Framework - - - - - - - - - - - - - - - - - - - - - - - - - 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. The sample XML file (thin_solaris_smf.erb) is an example SMF manifest which I use on a Joyent accelerator which runs on OpenSolaris. This setup will: - ensure the right dependencies are loaded - 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'). 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. 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. Here are some examples for usage to get you started with Capistrano, and Thin: FILE : config/deploy.rb -- require 'config/accelerator/accelerator_tasks' set :application, "yourapp" set :svcadm_bin, "/usr/sbin/svcadm" set :svccfg_bin, "/usr/sbin/svccfg" set :svcs_bin, "/usr/bin/svcs" # gets the list of remote service SMF names that we need to start # like (depending on thin_max_instances settings): # svc:/network/thin/yourapp-production:i_0 # svc:/network/thin/yourapp-production:i_1 # svc:/network/thin/yourapp-production:i_2 set :service_list, "`svcs -H -o FMRI svc:network/thin/#{application}-production`" # how many Thin instances should be setup to run? # this affects the generated thin smf file, and the nginx vhost conf # need to re-run setup for thin smf and nginx vhost conf when changed set :thin_max_instances, 3 # OVERRIDE STANDARD TASKS desc "Restart the entire application" deploy.task :restart do accelerator.thin.restart accelerator.nginx.restart end desc "Start the entire application" deploy.task :start do accelerator.thin.restart accelerator.nginx.restart end desc "Stop the entire application" deploy.task :stop do accelerator.thin.disable accelerator.nginx.disable end FILE : config/accelerator/accelerator_tasks.rb -- desc "Create and deploy Thin SMF config" task :create_thin_smf, :roles => :app do service_name = application working_directory = current_path template = File.read("config/accelerator/thin_solaris_smf.erb") buffer = ERB.new(template).result(binding) put buffer, "#{shared_path}/#{application}-thin-smf.xml" sudo "#{svccfg_bin} import #{shared_path}/#{application}-thin-smf.xml" end desc "Delete Thin SMF config" task :delete_thin_smf, :roles => :app do accelerator.thin.disable sudo "#{svccfg_bin} delete /network/thin/#{application}-production" end desc "Show all SMF services" task :svcs do run "#{svcs_bin} -a" do |ch, st, data| puts data end end desc "Shows all non-functional SMF services" task :svcs_broken do run "#{svcs_bin} -vx" do |ch, st, data| puts data end end namespace :thin do desc "Disable all Thin servers" task :disable, :roles => :app do # temporarily disable, until next reboot (-t) sudo "#{svcadm_bin} disable -t #{service_list}" end desc "Enable all Thin servers" task :enable, :roles => :app do # start the app with all recursive dependencies sudo "#{svcadm_bin} enable -r #{service_list}" end desc "Restart all Thin servers" task :restart, :roles => :app do # svcadm restart doesn't seem to work right, so we'll brute force it disable enable end end # namespace thin FILE : config/thin.yml -- --- pid: tmp/pids/thin.pid socket: /tmp/thin.sock log: log/thin.log max_conns: 1024 timeout: 30 chdir: /your/app/dir/rails/root environment: production max_persistent_conns: 512 daemonize: true servers: 3 FILE : config/accelerator/thin_solaris_smf.erb -- This is of course an example. It works for me, but YMMV You may need to change this line to match your environment and config: exec='/opt/csw/bin/thin -C config/thin.yml --only <%= instance.to_s %> start' CONTRIBUTE: 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. Cheers, Glenn Rempe 2008/03/20 thin-1.6.3/example/async_app.ru0000755000004100000410000000741512432275201016503 0ustar www-datawww-data#!/usr/bin/env rackup -s thin # # async_app.ru # raggi/thin # # A second demo app for async rack + thin app processing! # Now using http status code 100 instead. # # Created by James Tucker on 2008-06-17. # Copyright 2008 James Tucker . # #-- # Benchmark Results: # # raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/ # This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0 # Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ # Copyright 2006 The Apache Software Foundation, http://www.apache.org/ # # Benchmarking 127.0.0.1 (be patient) # Completed 100 requests # Completed 200 requests # Completed 300 requests # Completed 400 requests # Finished 500 requests # # # Server Software: thin # Server Hostname: 127.0.0.1 # Server Port: 3000 # # Document Path: / # Document Length: 12 bytes # # Concurrency Level: 100 # Time taken for tests: 5.263089 seconds # Complete requests: 500 # Failed requests: 0 # Write errors: 0 # Total transferred: 47000 bytes # HTML transferred: 6000 bytes # Requests per second: 95.00 [#/sec] (mean) # Time per request: 1052.618 [ms] (mean) # Time per request: 10.526 [ms] (mean, across all concurrent requests) # Transfer rate: 8.55 [Kbytes/sec] received # # Connection Times (ms) # min mean[+/-sd] median max # Connect: 0 3 2.2 3 8 # Processing: 1042 1046 3.1 1046 1053 # Waiting: 1037 1042 3.6 1041 1050 # Total: 1045 1049 3.1 1049 1057 # # Percentage of the requests served within a certain time (ms) # 50% 1049 # 66% 1051 # 75% 1053 # 80% 1053 # 90% 1054 # 95% 1054 # 98% 1056 # 99% 1057 # 100% 1057 (longest request) class DeferrableBody include EventMachine::Deferrable def call(body) body.each do |chunk| @body_callback.call(chunk) end end def each &blk @body_callback = blk end end class AsyncApp # This is a template async response. N.B. Can't use string for body on 1.9 AsyncResponse = [-1, {}, []].freeze def call(env) body = DeferrableBody.new # Get the headers out there asap, let the client know we're alive... EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] } # Semi-emulate a long db request, instead of a timer, in reality we'd be # waiting for the response data. Whilst this happens, other connections # can be serviced. # This could be any callback based thing though, a deferrable waiting on # IO data, a db request, an http request, an smtp send, whatever. EventMachine::add_timer(1) { body.call ["Woah, async!\n"] EventMachine::next_tick { # This could actually happen any time, you could spawn off to new # threads, pause as a good looking lady walks by, whatever. # Just shows off how we can defer chunks of data in the body, you can # even call this many times. body.call ["Cheers then!"] body.succeed } } # throw :async # Still works for supporting non-async frameworks... AsyncResponse # May end up in Rack :-) end end # The additions to env for async.connection and async.callback absolutely # destroy the speed of the request if Lint is doing it's checks on env. # It is also important to note that an async response will not pass through # any further middleware, as the async response notification has been passed # right up to the webserver, and the callback goes directly there too. # Middleware could possibly catch :async, and also provide a different # async.connection and async.callback. # use Rack::Lint run AsyncApp.new thin-1.6.3/example/async_tailer.ru0000644000004100000410000000350312432275201017172 0ustar www-datawww-data#!/usr/bin/env rackup -s thin # # async_tailer.ru # raggi/thin # # Tested with 150 spawned tails on OS X # # Created by James Tucker on 2008-06-18. # Copyright 2008 James Tucker . # Uncomment if appropriate for you.. # EM.epoll # EM.kqueue class DeferrableBody include EventMachine::Deferrable def initialize @queue = [] # make sure to flush out the queue before closing the connection callback{ until @queue.empty? @queue.shift.each{|chunk| @body_callback.call(chunk) } end } end def schedule_dequeue return unless @body_callback EventMachine::next_tick do next unless body = @queue.shift body.each do |chunk| @body_callback.call(chunk) end schedule_dequeue unless @queue.empty? end end def call(body) @queue << body schedule_dequeue end def each &blk @body_callback = blk schedule_dequeue end end module TailRenderer attr_accessor :callback def receive_data(data) @callback.call([data]) end def unbind @callback.succeed end end class AsyncTailer AsyncResponse = [-1, {}, []].freeze def call(env) body = DeferrableBody.new EventMachine::next_tick do env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body] body.call ["

Async Tailer

"]
      
    end
    
    EventMachine::popen('tail -f /var/log/system.log', TailRenderer) do |t|
      
      t.callback = body
      
      # If for some reason we 'complete' body, close the tail.
      body.callback do
        t.close_connection
      end
      
      # If for some reason the client disconnects, close the tail.
      body.errback do
        t.close_connection
      end
      
    end
    
    AsyncResponse
  end
  
end

run AsyncTailer.new
thin-1.6.3/example/myapp.rb0000644000004100000410000000006512432275201015620 0ustar  www-datawww-dataMyapp = lambda { |env| [200, {}, 'this is my app!'] }thin-1.6.3/example/ramaze.ru0000644000004100000410000000033612432275201015775 0ustar  www-datawww-data# Ramaze Rackup config file.
# by tmm1
# Use with --rackup option:
# 
#   thin start -r ramaze.ru
# 
require 'start'

Ramaze.trait[:essentials].delete Ramaze::Adapter
Ramaze.start :force => true

run Ramaze::Adapter::Base
thin-1.6.3/example/thin_solaris_smf.erb0000644000004100000410000000353312432275201020205 0ustar  www-datawww-data


  
    
    
      
    
    
      
      
      
    
    <% 0.upto(thin_max_instances - 1) do |instance| %>
    
    
      
      
        
      
      
        
          
          
            
          
        
      
      
    
    <% end %>
  


thin-1.6.3/example/vlad.rake0000644000004100000410000000413712432275201015743 0ustar  www-datawww-data# $GEM_HOME/gems/vlad-1.2.0/lib/vlad/thin.rb
# Thin tasks for Vlad the Deployer
# By cnantais
require 'vlad'

namespace :vlad do
  ##
  # Thin app server

  set :thin_address,       nil
  set :thin_command,       "thin"
  set(:thin_conf)          { "#{shared_path}/thin_cluster.conf" }
  set :thin_environment,   "production"
  set :thin_group,         nil
  set :thin_log_file,      nil
  set :thin_pid_file,      nil
  set :thin_port,          nil
  set :thin_socket,        nil
  set :thin_prefix,        nil
  set :thin_servers,       2
  set :thin_user,          nil
  
  set :thin_uses_bundler,  true

  desc "Prepares application servers for deployment. thin
configuration is set via the thin_* variables.".cleanup

  remote_task :setup_app, :roles => :app do
  
    raise(ArgumentError, "Please provide either thin_socket or thin_port") if thin_port.nil? && thin_socket.nil?
  
    cmd = [
           "config",
           (%(-s "#{thin_servers}") if thin_servers),
           (%(-S "#{thin_socket}") if thin_socket),
           (%(-e "#{thin_environment}") if thin_environment),
           (%(-a "#{thin_address}") if thin_address),
           %(-c "#{current_path}"),
           (%(-C "#{thin_conf}") if thin_conf),
           (%(-P "#{thin_pid_file}") if thin_pid_file),
           (%(-l "#{thin_log_file}") if thin_log_file),
           (%(--user "#{thin_user}") if thin_user),
           (%(--group "#{thin_group}") if thin_group),
           (%(--prefix "#{thin_prefix}") if thin_prefix),
           (%(-p "#{thin_port}") if thin_port),
          ].compact.join ' '

    thin(cmd)
  end

  def thin(cmd) # :nodoc:
    command = if thin_uses_bundler
      %(BUNDLE_GEMFILE="#{current_path}/Gemfile" bundle exec #{thin_command} #{cmd} -C "#{thin_conf}")
    else
      %(#{thin_command} #{cmd} -C "#{thin_conf}")
    end

    %(cd "#{current_path}" && #{command})
  end

  desc "Restart the app servers"

  remote_task :start_app, :roles => :app do
    run thin(%(restart -O -s "#{thin_servers}"))
  end

  desc "Stop the app servers"

  remote_task :stop_app, :roles => :app do
    run thin(%(stop -s "#{thin_servers}"))
  end
end
thin-1.6.3/example/adapter.rb0000644000004100000410000000122412432275201016110 0ustar  www-datawww-data# Run with: ruby adapter.rb
# Then browse to http://localhost:3000/test
# and http://localhost:3000/files/adapter.rb
require 'thin'

class SimpleAdapter
  def call(env)
    body = ["hello!"]
    [
      200,
      { 'Content-Type' => 'text/plain' },
      body
    ]
  end
end

Thin::Server.start('0.0.0.0', 3000) do
  use Rack::CommonLogger
  map '/test' do
    run SimpleAdapter.new
  end
  map '/files' do
    run Rack::File.new('.')
  end
end

# You could also start the server like this:
#
#   app = Rack::URLMap.new('/test'  => SimpleAdapter.new,
#                          '/files' => Rack::File.new('.'))
#   Thin::Server.start('0.0.0.0', 3000, app)
#
thin-1.6.3/example/monit_sockets0000644000004100000410000000176212432275201016756 0ustar  www-datawww-datacheck process blog1
    with pidfile /u/apps/blog/shared/pids/thin.14000.pid
    start program = "ruby thin start -d -e production -u nobody -g nobody -p 14000 -a 127.0.0.1 -P tmp/pids/thin.14000.pid -c /u/apps/blog/current"
    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14000.pid"
    if totalmem > 90.0 MB for 5 cycles then restart
    if failed port 14000 then restart
    if cpu usage > 95% for 3 cycles then restart
    if 5 restarts within 5 cycles then timeout
		group blog

check process blog2
    with pidfile /u/apps/blog/shared/pids/thin.14001.pid
    start program = "ruby thin start -d -e production -u nobody -g nobody -p 14001 -a 127.0.0.1 -P tmp/pids/thin.14001.pid -c /u/apps/blog/current"
    stop program  = "ruby thin stop -P /u/apps/blog/shared/pids/thin.14001.pid"
    if totalmem > 90.0 MB for 5 cycles then restart
    if failed port 14001 then restart
    if cpu usage > 95% for 3 cycles then restart
    if 5 restarts within 5 cycles then timeout
		group blog

thin-1.6.3/example/config.ru0000644000004100000410000000125012432275201015757 0ustar  www-datawww-data# Run with: rackup -s thin
# then browse to http://localhost:9292
# Or with: thin start -R config.ru
# then browse to http://localhost:3000
# 
# Check Rack::Builder doc for more details on this file format:
#  http://rack.rubyforge.org/doc/classes/Rack/Builder.html
require 'thin'

app = proc do |env|
  # Response body has to respond to each and yield strings
  # See Rack specs for more info: http://rack.rubyforge.org/doc/files/SPEC.html
  body = ['hi!']
  
  [
    200,                                        # Status code
    { 'Content-Type' => 'text/html' },          # Reponse headers
    body                                        # Body of the response
  ]
end

run appthin-1.6.3/ext/0000755000004100000410000000000012432275201013311 5ustar  www-datawww-datathin-1.6.3/ext/thin_parser/0000755000004100000410000000000012432275201015627 5ustar  www-datawww-datathin-1.6.3/ext/thin_parser/parser.c0000644000004100000410000006565712432275201017312 0ustar  www-datawww-data
#line 1 "parser.rl"
/**
 * Copyright (c) 2005 Zed A. Shaw
 * You can redistribute it and/or modify it under the same terms as Ruby.
 */
#include "parser.h"
#include 
#include 
#include 
#include 
#include 

#define LEN(AT, FPC) (FPC - buffer - parser->AT)
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
#define PTR_TO(F) (buffer + parser->F)

/** Machine **/


#line 81 "parser.rl"


/** Data **/

#line 27 "parser.c"
static const int http_parser_start = 1;
static const int http_parser_first_final = 70;
static const int http_parser_error = 0;

static const int http_parser_en_main = 1;


#line 85 "parser.rl"

int thin_http_parser_init(http_parser *parser)  {
  int cs = 0;
  
#line 40 "parser.c"
	{
	cs = http_parser_start;
	}

#line 89 "parser.rl"
  parser->cs = cs;
  parser->body_start = 0;
  parser->content_len = 0;
  parser->mark = 0;
  parser->nread = 0;
  parser->field_len = 0;
  parser->field_start = 0;    

  return(1);
}


/** exec **/
size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
  const char *p, *pe;
  int cs = parser->cs;

  assert(off <= len && "offset past end of buffer");

  p = buffer+off;
  pe = buffer+len;

  assert(*pe == '\0' && "pointer does not end on NUL");
  assert(pe - p == (long)(len - off) && "pointers aren't same distance");


  
#line 73 "parser.c"
	{
	if ( p == pe )
		goto _test_eof;
	switch ( cs )
	{
case 1:
	switch( (*p) ) {
		case 36: goto tr0;
		case 95: goto tr0;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto tr0;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto tr0;
	} else
		goto tr0;
	goto st0;
st0:
cs = 0;
	goto _out;
tr0:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st2;
st2:
	if ( ++p == pe )
		goto _test_eof2;
case 2:
#line 104 "parser.c"
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st51;
		case 95: goto st51;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st51;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st51;
	} else
		goto st51;
	goto st0;
tr2:
#line 36 "parser.rl"
	{ 
    if (parser->request_method != NULL) {
      parser->request_method(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st3;
st3:
	if ( ++p == pe )
		goto _test_eof3;
case 3:
#line 131 "parser.c"
	switch( (*p) ) {
		case 42: goto tr4;
		case 47: goto tr5;
		case 72: goto st34;
		case 104: goto st34;
	}
	goto st0;
tr4:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st4;
st4:
	if ( ++p == pe )
		goto _test_eof4;
case 4:
#line 147 "parser.c"
	switch( (*p) ) {
		case 32: goto tr7;
		case 35: goto tr8;
	}
	goto st0;
tr7:
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
tr30:
#line 22 "parser.rl"
	{MARK(mark, p); }
#line 46 "parser.rl"
	{ 
    if (parser->fragment != NULL) {
      parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
tr33:
#line 46 "parser.rl"
	{ 
    if (parser->fragment != NULL) {
      parser->fragment(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
tr38:
#line 65 "parser.rl"
	{
    if (parser->request_path != NULL) {
      parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
tr45:
#line 52 "parser.rl"
	{MARK(query_start, p); }
#line 53 "parser.rl"
	{ 
    if (parser->query_string != NULL) {
      parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
tr49:
#line 53 "parser.rl"
	{ 
    if (parser->query_string != NULL) {
      parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st5;
st5:
	if ( ++p == pe )
		goto _test_eof5;
case 5:
#line 227 "parser.c"
	if ( (*p) == 72 )
		goto tr9;
	goto st0;
tr9:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st6;
st6:
	if ( ++p == pe )
		goto _test_eof6;
case 6:
#line 239 "parser.c"
	if ( (*p) == 84 )
		goto st7;
	goto st0;
st7:
	if ( ++p == pe )
		goto _test_eof7;
case 7:
	if ( (*p) == 84 )
		goto st8;
	goto st0;
st8:
	if ( ++p == pe )
		goto _test_eof8;
case 8:
	if ( (*p) == 80 )
		goto st9;
	goto st0;
st9:
	if ( ++p == pe )
		goto _test_eof9;
case 9:
	if ( (*p) == 47 )
		goto st10;
	goto st0;
st10:
	if ( ++p == pe )
		goto _test_eof10;
case 10:
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st11;
	goto st0;
st11:
	if ( ++p == pe )
		goto _test_eof11;
case 11:
	if ( (*p) == 46 )
		goto st12;
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st11;
	goto st0;
st12:
	if ( ++p == pe )
		goto _test_eof12;
case 12:
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st13;
	goto st0;
st13:
	if ( ++p == pe )
		goto _test_eof13;
case 13:
	if ( (*p) == 13 )
		goto tr17;
	if ( 48 <= (*p) && (*p) <= 57 )
		goto st13;
	goto st0;
tr17:
#line 59 "parser.rl"
	{	
    if (parser->http_version != NULL) {
      parser->http_version(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st14;
tr25:
#line 30 "parser.rl"
	{ MARK(mark, p); }
#line 31 "parser.rl"
	{ 
    if (parser->http_field != NULL) {
      parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st14;
tr28:
#line 31 "parser.rl"
	{ 
    if (parser->http_field != NULL) {
      parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st14;
st14:
	if ( ++p == pe )
		goto _test_eof14;
case 14:
#line 326 "parser.c"
	if ( (*p) == 10 )
		goto st15;
	goto st0;
st15:
	if ( ++p == pe )
		goto _test_eof15;
case 15:
	switch( (*p) ) {
		case 13: goto st16;
		case 33: goto tr20;
		case 124: goto tr20;
		case 126: goto tr20;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 39 ) {
			if ( 42 <= (*p) && (*p) <= 43 )
				goto tr20;
		} else if ( (*p) >= 35 )
			goto tr20;
	} else if ( (*p) > 46 ) {
		if ( (*p) < 65 ) {
			if ( 48 <= (*p) && (*p) <= 57 )
				goto tr20;
		} else if ( (*p) > 90 ) {
			if ( 94 <= (*p) && (*p) <= 122 )
				goto tr20;
		} else
			goto tr20;
	} else
		goto tr20;
	goto st0;
st16:
	if ( ++p == pe )
		goto _test_eof16;
case 16:
	if ( (*p) == 10 )
		goto tr21;
	goto st0;
tr21:
#line 71 "parser.rl"
	{ 
    parser->body_start = p - buffer + 1; 
    if (parser->header_done != NULL) {
      parser->header_done(parser->data, p + 1, pe - p - 1);
    }
    {p++; cs = 70; goto _out;}
  }
	goto st70;
st70:
	if ( ++p == pe )
		goto _test_eof70;
case 70:
#line 379 "parser.c"
	goto st0;
tr20:
#line 25 "parser.rl"
	{ MARK(field_start, p); }
	goto st17;
st17:
	if ( ++p == pe )
		goto _test_eof17;
case 17:
#line 389 "parser.c"
	switch( (*p) ) {
		case 33: goto st17;
		case 58: goto tr23;
		case 124: goto st17;
		case 126: goto st17;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 39 ) {
			if ( 42 <= (*p) && (*p) <= 43 )
				goto st17;
		} else if ( (*p) >= 35 )
			goto st17;
	} else if ( (*p) > 46 ) {
		if ( (*p) < 65 ) {
			if ( 48 <= (*p) && (*p) <= 57 )
				goto st17;
		} else if ( (*p) > 90 ) {
			if ( 94 <= (*p) && (*p) <= 122 )
				goto st17;
		} else
			goto st17;
	} else
		goto st17;
	goto st0;
tr23:
#line 26 "parser.rl"
	{ 
    parser->field_len = LEN(field_start, p);
  }
	goto st18;
tr26:
#line 30 "parser.rl"
	{ MARK(mark, p); }
	goto st18;
st18:
	if ( ++p == pe )
		goto _test_eof18;
case 18:
#line 428 "parser.c"
	switch( (*p) ) {
		case 13: goto tr25;
		case 32: goto tr26;
	}
	goto tr24;
tr24:
#line 30 "parser.rl"
	{ MARK(mark, p); }
	goto st19;
st19:
	if ( ++p == pe )
		goto _test_eof19;
case 19:
#line 442 "parser.c"
	if ( (*p) == 13 )
		goto tr28;
	goto st19;
tr8:
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st20;
tr39:
#line 65 "parser.rl"
	{
    if (parser->request_path != NULL) {
      parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st20;
tr46:
#line 52 "parser.rl"
	{MARK(query_start, p); }
#line 53 "parser.rl"
	{ 
    if (parser->query_string != NULL) {
      parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st20;
tr50:
#line 53 "parser.rl"
	{ 
    if (parser->query_string != NULL) {
      parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, p));
    }
  }
#line 41 "parser.rl"
	{
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, p));
    }
  }
	goto st20;
st20:
	if ( ++p == pe )
		goto _test_eof20;
case 20:
#line 502 "parser.c"
	switch( (*p) ) {
		case 32: goto tr30;
		case 35: goto st0;
		case 37: goto tr31;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto tr29;
tr29:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st21;
st21:
	if ( ++p == pe )
		goto _test_eof21;
case 21:
#line 520 "parser.c"
	switch( (*p) ) {
		case 32: goto tr33;
		case 35: goto st0;
		case 37: goto st22;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st21;
tr31:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st22;
st22:
	if ( ++p == pe )
		goto _test_eof22;
case 22:
#line 538 "parser.c"
	if ( (*p) == 117 )
		goto st24;
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st23;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st23;
	} else
		goto st23;
	goto st0;
st23:
	if ( ++p == pe )
		goto _test_eof23;
case 23:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st21;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st21;
	} else
		goto st21;
	goto st0;
st24:
	if ( ++p == pe )
		goto _test_eof24;
case 24:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st23;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st23;
	} else
		goto st23;
	goto st0;
tr5:
#line 22 "parser.rl"
	{MARK(mark, p); }
	goto st25;
st25:
	if ( ++p == pe )
		goto _test_eof25;
case 25:
#line 584 "parser.c"
	switch( (*p) ) {
		case 32: goto tr38;
		case 35: goto tr39;
		case 37: goto st26;
		case 63: goto tr41;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st25;
st26:
	if ( ++p == pe )
		goto _test_eof26;
case 26:
	if ( (*p) == 117 )
		goto st28;
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st27;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st27;
	} else
		goto st27;
	goto st0;
st27:
	if ( ++p == pe )
		goto _test_eof27;
case 27:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st25;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st25;
	} else
		goto st25;
	goto st0;
st28:
	if ( ++p == pe )
		goto _test_eof28;
case 28:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st27;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st27;
	} else
		goto st27;
	goto st0;
tr41:
#line 65 "parser.rl"
	{
    if (parser->request_path != NULL) {
      parser->request_path(parser->data, PTR_TO(mark), LEN(mark,p));
    }
  }
	goto st29;
st29:
	if ( ++p == pe )
		goto _test_eof29;
case 29:
#line 648 "parser.c"
	switch( (*p) ) {
		case 32: goto tr45;
		case 35: goto tr46;
		case 37: goto tr47;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto tr44;
tr44:
#line 52 "parser.rl"
	{MARK(query_start, p); }
	goto st30;
st30:
	if ( ++p == pe )
		goto _test_eof30;
case 30:
#line 666 "parser.c"
	switch( (*p) ) {
		case 32: goto tr49;
		case 35: goto tr50;
		case 37: goto st31;
		case 127: goto st0;
	}
	if ( 0 <= (*p) && (*p) <= 31 )
		goto st0;
	goto st30;
tr47:
#line 52 "parser.rl"
	{MARK(query_start, p); }
	goto st31;
st31:
	if ( ++p == pe )
		goto _test_eof31;
case 31:
#line 684 "parser.c"
	if ( (*p) == 117 )
		goto st33;
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st32;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st32;
	} else
		goto st32;
	goto st0;
st32:
	if ( ++p == pe )
		goto _test_eof32;
case 32:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st30;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st30;
	} else
		goto st30;
	goto st0;
st33:
	if ( ++p == pe )
		goto _test_eof33;
case 33:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st32;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st32;
	} else
		goto st32;
	goto st0;
st34:
	if ( ++p == pe )
		goto _test_eof34;
case 34:
	switch( (*p) ) {
		case 84: goto st35;
		case 116: goto st35;
	}
	goto st0;
st35:
	if ( ++p == pe )
		goto _test_eof35;
case 35:
	switch( (*p) ) {
		case 84: goto st36;
		case 116: goto st36;
	}
	goto st0;
st36:
	if ( ++p == pe )
		goto _test_eof36;
case 36:
	switch( (*p) ) {
		case 80: goto st37;
		case 112: goto st37;
	}
	goto st0;
st37:
	if ( ++p == pe )
		goto _test_eof37;
case 37:
	switch( (*p) ) {
		case 58: goto st38;
		case 83: goto st50;
		case 115: goto st50;
	}
	goto st0;
st38:
	if ( ++p == pe )
		goto _test_eof38;
case 38:
	if ( (*p) == 47 )
		goto st39;
	goto st0;
st39:
	if ( ++p == pe )
		goto _test_eof39;
case 39:
	if ( (*p) == 47 )
		goto st40;
	goto st0;
st40:
	if ( ++p == pe )
		goto _test_eof40;
case 40:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto st0;
		case 60: goto st0;
		case 91: goto st47;
		case 95: goto st45;
		case 127: goto st0;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 32 ) {
			if ( 34 <= (*p) && (*p) <= 35 )
				goto st0;
		} else if ( (*p) >= 0 )
			goto st0;
	} else if ( (*p) > 57 ) {
		if ( (*p) < 65 ) {
			if ( 62 <= (*p) && (*p) <= 64 )
				goto st0;
		} else if ( (*p) > 90 ) {
			if ( 97 <= (*p) && (*p) <= 122 )
				goto st45;
		} else
			goto st45;
	} else
		goto st45;
	goto st41;
st41:
	if ( ++p == pe )
		goto _test_eof41;
case 41:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto st0;
		case 60: goto st0;
		case 64: goto st40;
		case 127: goto st0;
	}
	if ( (*p) < 34 ) {
		if ( 0 <= (*p) && (*p) <= 32 )
			goto st0;
	} else if ( (*p) > 35 ) {
		if ( 62 <= (*p) && (*p) <= 63 )
			goto st0;
	} else
		goto st0;
	goto st41;
st42:
	if ( ++p == pe )
		goto _test_eof42;
case 42:
	if ( (*p) == 117 )
		goto st44;
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st43;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st43;
	} else
		goto st43;
	goto st0;
st43:
	if ( ++p == pe )
		goto _test_eof43;
case 43:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st41;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st41;
	} else
		goto st41;
	goto st0;
st44:
	if ( ++p == pe )
		goto _test_eof44;
case 44:
	if ( (*p) < 65 ) {
		if ( 48 <= (*p) && (*p) <= 57 )
			goto st43;
	} else if ( (*p) > 70 ) {
		if ( 97 <= (*p) && (*p) <= 102 )
			goto st43;
	} else
		goto st43;
	goto st0;
st45:
	if ( ++p == pe )
		goto _test_eof45;
case 45:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto tr5;
		case 58: goto st46;
		case 60: goto st0;
		case 64: goto st40;
		case 95: goto st45;
		case 127: goto st0;
	}
	if ( (*p) < 45 ) {
		if ( (*p) > 32 ) {
			if ( 34 <= (*p) && (*p) <= 35 )
				goto st0;
		} else if ( (*p) >= 0 )
			goto st0;
	} else if ( (*p) > 57 ) {
		if ( (*p) < 65 ) {
			if ( 62 <= (*p) && (*p) <= 63 )
				goto st0;
		} else if ( (*p) > 90 ) {
			if ( 97 <= (*p) && (*p) <= 122 )
				goto st45;
		} else
			goto st45;
	} else
		goto st45;
	goto st41;
st46:
	if ( ++p == pe )
		goto _test_eof46;
case 46:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto tr5;
		case 60: goto st0;
		case 64: goto st40;
		case 127: goto st0;
	}
	if ( (*p) < 34 ) {
		if ( 0 <= (*p) && (*p) <= 32 )
			goto st0;
	} else if ( (*p) > 35 ) {
		if ( (*p) > 57 ) {
			if ( 62 <= (*p) && (*p) <= 63 )
				goto st0;
		} else if ( (*p) >= 48 )
			goto st46;
	} else
		goto st0;
	goto st41;
st47:
	if ( ++p == pe )
		goto _test_eof47;
case 47:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto st0;
		case 60: goto st0;
		case 64: goto st40;
		case 127: goto st0;
	}
	if ( (*p) < 48 ) {
		if ( (*p) > 32 ) {
			if ( 34 <= (*p) && (*p) <= 35 )
				goto st0;
		} else if ( (*p) >= 0 )
			goto st0;
	} else if ( (*p) > 58 ) {
		if ( (*p) < 65 ) {
			if ( 62 <= (*p) && (*p) <= 63 )
				goto st0;
		} else if ( (*p) > 70 ) {
			if ( 97 <= (*p) && (*p) <= 102 )
				goto st48;
		} else
			goto st48;
	} else
		goto st48;
	goto st41;
st48:
	if ( ++p == pe )
		goto _test_eof48;
case 48:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto st0;
		case 60: goto st0;
		case 64: goto st40;
		case 93: goto st49;
		case 127: goto st0;
	}
	if ( (*p) < 48 ) {
		if ( (*p) > 32 ) {
			if ( 34 <= (*p) && (*p) <= 35 )
				goto st0;
		} else if ( (*p) >= 0 )
			goto st0;
	} else if ( (*p) > 58 ) {
		if ( (*p) < 65 ) {
			if ( 62 <= (*p) && (*p) <= 63 )
				goto st0;
		} else if ( (*p) > 70 ) {
			if ( 97 <= (*p) && (*p) <= 102 )
				goto st48;
		} else
			goto st48;
	} else
		goto st48;
	goto st41;
st49:
	if ( ++p == pe )
		goto _test_eof49;
case 49:
	switch( (*p) ) {
		case 37: goto st42;
		case 47: goto tr5;
		case 58: goto st46;
		case 60: goto st0;
		case 64: goto st40;
		case 127: goto st0;
	}
	if ( (*p) < 34 ) {
		if ( 0 <= (*p) && (*p) <= 32 )
			goto st0;
	} else if ( (*p) > 35 ) {
		if ( 62 <= (*p) && (*p) <= 63 )
			goto st0;
	} else
		goto st0;
	goto st41;
st50:
	if ( ++p == pe )
		goto _test_eof50;
case 50:
	if ( (*p) == 58 )
		goto st38;
	goto st0;
st51:
	if ( ++p == pe )
		goto _test_eof51;
case 51:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st52;
		case 95: goto st52;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st52;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st52;
	} else
		goto st52;
	goto st0;
st52:
	if ( ++p == pe )
		goto _test_eof52;
case 52:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st53;
		case 95: goto st53;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st53;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st53;
	} else
		goto st53;
	goto st0;
st53:
	if ( ++p == pe )
		goto _test_eof53;
case 53:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st54;
		case 95: goto st54;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st54;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st54;
	} else
		goto st54;
	goto st0;
st54:
	if ( ++p == pe )
		goto _test_eof54;
case 54:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st55;
		case 95: goto st55;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st55;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st55;
	} else
		goto st55;
	goto st0;
st55:
	if ( ++p == pe )
		goto _test_eof55;
case 55:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st56;
		case 95: goto st56;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st56;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st56;
	} else
		goto st56;
	goto st0;
st56:
	if ( ++p == pe )
		goto _test_eof56;
case 56:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st57;
		case 95: goto st57;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st57;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st57;
	} else
		goto st57;
	goto st0;
st57:
	if ( ++p == pe )
		goto _test_eof57;
case 57:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st58;
		case 95: goto st58;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st58;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st58;
	} else
		goto st58;
	goto st0;
st58:
	if ( ++p == pe )
		goto _test_eof58;
case 58:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st59;
		case 95: goto st59;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st59;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st59;
	} else
		goto st59;
	goto st0;
st59:
	if ( ++p == pe )
		goto _test_eof59;
case 59:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st60;
		case 95: goto st60;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st60;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st60;
	} else
		goto st60;
	goto st0;
st60:
	if ( ++p == pe )
		goto _test_eof60;
case 60:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st61;
		case 95: goto st61;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st61;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st61;
	} else
		goto st61;
	goto st0;
st61:
	if ( ++p == pe )
		goto _test_eof61;
case 61:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st62;
		case 95: goto st62;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st62;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st62;
	} else
		goto st62;
	goto st0;
st62:
	if ( ++p == pe )
		goto _test_eof62;
case 62:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st63;
		case 95: goto st63;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st63;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st63;
	} else
		goto st63;
	goto st0;
st63:
	if ( ++p == pe )
		goto _test_eof63;
case 63:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st64;
		case 95: goto st64;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st64;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st64;
	} else
		goto st64;
	goto st0;
st64:
	if ( ++p == pe )
		goto _test_eof64;
case 64:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st65;
		case 95: goto st65;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st65;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st65;
	} else
		goto st65;
	goto st0;
st65:
	if ( ++p == pe )
		goto _test_eof65;
case 65:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st66;
		case 95: goto st66;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st66;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st66;
	} else
		goto st66;
	goto st0;
st66:
	if ( ++p == pe )
		goto _test_eof66;
case 66:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st67;
		case 95: goto st67;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st67;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st67;
	} else
		goto st67;
	goto st0;
st67:
	if ( ++p == pe )
		goto _test_eof67;
case 67:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st68;
		case 95: goto st68;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st68;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st68;
	} else
		goto st68;
	goto st0;
st68:
	if ( ++p == pe )
		goto _test_eof68;
case 68:
	switch( (*p) ) {
		case 32: goto tr2;
		case 36: goto st69;
		case 95: goto st69;
	}
	if ( (*p) < 48 ) {
		if ( 45 <= (*p) && (*p) <= 46 )
			goto st69;
	} else if ( (*p) > 57 ) {
		if ( 65 <= (*p) && (*p) <= 90 )
			goto st69;
	} else
		goto st69;
	goto st0;
st69:
	if ( ++p == pe )
		goto _test_eof69;
case 69:
	if ( (*p) == 32 )
		goto tr2;
	goto st0;
	}
	_test_eof2: cs = 2; goto _test_eof; 
	_test_eof3: cs = 3; goto _test_eof; 
	_test_eof4: cs = 4; goto _test_eof; 
	_test_eof5: cs = 5; goto _test_eof; 
	_test_eof6: cs = 6; goto _test_eof; 
	_test_eof7: cs = 7; goto _test_eof; 
	_test_eof8: cs = 8; goto _test_eof; 
	_test_eof9: cs = 9; goto _test_eof; 
	_test_eof10: cs = 10; goto _test_eof; 
	_test_eof11: cs = 11; goto _test_eof; 
	_test_eof12: cs = 12; goto _test_eof; 
	_test_eof13: cs = 13; goto _test_eof; 
	_test_eof14: cs = 14; goto _test_eof; 
	_test_eof15: cs = 15; goto _test_eof; 
	_test_eof16: cs = 16; goto _test_eof; 
	_test_eof70: cs = 70; goto _test_eof; 
	_test_eof17: cs = 17; goto _test_eof; 
	_test_eof18: cs = 18; goto _test_eof; 
	_test_eof19: cs = 19; goto _test_eof; 
	_test_eof20: cs = 20; goto _test_eof; 
	_test_eof21: cs = 21; goto _test_eof; 
	_test_eof22: cs = 22; goto _test_eof; 
	_test_eof23: cs = 23; goto _test_eof; 
	_test_eof24: cs = 24; goto _test_eof; 
	_test_eof25: cs = 25; goto _test_eof; 
	_test_eof26: cs = 26; goto _test_eof; 
	_test_eof27: cs = 27; goto _test_eof; 
	_test_eof28: cs = 28; goto _test_eof; 
	_test_eof29: cs = 29; goto _test_eof; 
	_test_eof30: cs = 30; goto _test_eof; 
	_test_eof31: cs = 31; goto _test_eof; 
	_test_eof32: cs = 32; goto _test_eof; 
	_test_eof33: cs = 33; goto _test_eof; 
	_test_eof34: cs = 34; goto _test_eof; 
	_test_eof35: cs = 35; goto _test_eof; 
	_test_eof36: cs = 36; goto _test_eof; 
	_test_eof37: cs = 37; goto _test_eof; 
	_test_eof38: cs = 38; goto _test_eof; 
	_test_eof39: cs = 39; goto _test_eof; 
	_test_eof40: cs = 40; goto _test_eof; 
	_test_eof41: cs = 41; goto _test_eof; 
	_test_eof42: cs = 42; goto _test_eof; 
	_test_eof43: cs = 43; goto _test_eof; 
	_test_eof44: cs = 44; goto _test_eof; 
	_test_eof45: cs = 45; goto _test_eof; 
	_test_eof46: cs = 46; goto _test_eof; 
	_test_eof47: cs = 47; goto _test_eof; 
	_test_eof48: cs = 48; goto _test_eof; 
	_test_eof49: cs = 49; goto _test_eof; 
	_test_eof50: cs = 50; goto _test_eof; 
	_test_eof51: cs = 51; goto _test_eof; 
	_test_eof52: cs = 52; goto _test_eof; 
	_test_eof53: cs = 53; goto _test_eof; 
	_test_eof54: cs = 54; goto _test_eof; 
	_test_eof55: cs = 55; goto _test_eof; 
	_test_eof56: cs = 56; goto _test_eof; 
	_test_eof57: cs = 57; goto _test_eof; 
	_test_eof58: cs = 58; goto _test_eof; 
	_test_eof59: cs = 59; goto _test_eof; 
	_test_eof60: cs = 60; goto _test_eof; 
	_test_eof61: cs = 61; goto _test_eof; 
	_test_eof62: cs = 62; goto _test_eof; 
	_test_eof63: cs = 63; goto _test_eof; 
	_test_eof64: cs = 64; goto _test_eof; 
	_test_eof65: cs = 65; goto _test_eof; 
	_test_eof66: cs = 66; goto _test_eof; 
	_test_eof67: cs = 67; goto _test_eof; 
	_test_eof68: cs = 68; goto _test_eof; 
	_test_eof69: cs = 69; goto _test_eof; 

	_test_eof: {}
	_out: {}
	}

#line 116 "parser.rl"

  parser->cs = cs;
  parser->nread += p - (buffer + off);

  assert(p <= pe && "buffer overflow after parsing execute");
  assert(parser->nread <= len && "nread longer than length");
  assert(parser->body_start <= len && "body starts after buffer end");
  assert(parser->mark < len && "mark is after buffer end");
  assert(parser->field_len <= len && "field has length longer than whole buffer");
  assert(parser->field_start < len && "field starts after buffer end");

  if(parser->body_start) {
    /* final \r\n combo encountered so stop right here */
    parser->nread++;
  }

  return(parser->nread);
}

int thin_http_parser_has_error(http_parser *parser) {
  return parser->cs == http_parser_error;
}

int thin_http_parser_is_finished(http_parser *parser) {
  return parser->cs == http_parser_first_final;
}

int thin_http_parser_finish(http_parser *parser)
{
  int cs = parser->cs;


  parser->cs = cs;

  if (thin_http_parser_has_error(parser) ) {
    return -1;
  } else if (thin_http_parser_is_finished(parser) ) {
    return 1;
  } else {
    return 0;
  }
}
thin-1.6.3/ext/thin_parser/extconf.rb0000644000004100000410000000014412432275201017621 0ustar  www-datawww-datarequire 'mkmf'

dir_config("thin_parser")
have_library("c", "main")

create_makefile("thin_parser")
thin-1.6.3/ext/thin_parser/thin.c0000644000004100000410000003054212432275201016741 0ustar  www-datawww-data/**
 * Mongrel Parser adpated to Thin and to play more nicely with Rack specs.
 * 
 * Orignal version Copyright (c) 2005 Zed A. Shaw
 * You can redistribute it and/or modify it under the same terms as Ruby.
 */
#include "ruby.h"
#include "ext_help.h"
#include 
#include 
#include "parser.h"
#include 

static VALUE mThin;
static VALUE cHttpParser;
static VALUE eHttpParserError;

static VALUE global_empty;
static VALUE global_http_prefix;
static VALUE global_request_method;
static VALUE global_request_uri;
static VALUE global_fragment;
static VALUE global_query_string;
static VALUE global_http_version;
static VALUE global_content_length;
static VALUE global_http_content_length;
static VALUE global_request_path;
static VALUE global_content_type;
static VALUE global_http_content_type;
static VALUE global_gateway_interface;
static VALUE global_gateway_interface_value;
static VALUE global_server_name;
static VALUE global_server_port;
static VALUE global_server_protocol;
static VALUE global_server_protocol_value;
static VALUE global_http_host;
static VALUE global_port_80;
static VALUE global_http_body;
static VALUE global_url_scheme;
static VALUE global_url_scheme_value;
static VALUE global_script_name;
static VALUE global_path_info;

#define TRIE_INCREASE 30

/** Defines common length and error messages for input length validation. */
#define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N  " is longer than the " # length " allowed length."

/** Validates the max length of given input and throws an HttpParserError exception if over. */
#define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, "%s", MAX_##N##_LENGTH_ERR); }

/** Defines global strings in the init method. */
#define DEF_GLOBAL(N, val)   global_##N = rb_obj_freeze(rb_str_new2(val)); rb_global_variable(&global_##N)

/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_PTR */
#ifndef RSTRING_PTR
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
#endif

/* for compatibility with Ruby 1.8.5, which doesn't declare RSTRING_LEN */
#ifndef RSTRING_LEN
#define RSTRING_LEN(s) (RSTRING(s)->len)
#endif

/* Defines the maximum allowed lengths for various input elements.*/
DEF_MAX_LENGTH(FIELD_NAME, 256);
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
DEF_MAX_LENGTH(REQUEST_PATH, 1024);
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));

static void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen)
{
  char *ch, *end;
  VALUE req = (VALUE)data;
  VALUE v = Qnil;
  VALUE f = Qnil;

  VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
  VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);

  v = rb_str_new(value, vlen);
  f = rb_str_dup(global_http_prefix);
  f = rb_str_buf_cat(f, field, flen); 

  for(ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix), end = RSTRING_PTR(f) + RSTRING_LEN(f); ch < end; ch++) {
    if (*ch >= 'a' && *ch <= 'z') {
      *ch &= ~0x20; // upcase
    } else if (*ch == '-') {
      *ch = '_';
    }
  }

  rb_hash_aset(req, f, v);
}

static void request_method(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = Qnil;

  val = rb_str_new(at, length);
  rb_hash_aset(req, global_request_method, val);
}

static void request_uri(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = Qnil;

  VALIDATE_MAX_LENGTH(length, REQUEST_URI);

  val = rb_str_new(at, length);
  rb_hash_aset(req, global_request_uri, val);
}

static void fragment(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = Qnil;

  VALIDATE_MAX_LENGTH(length, FRAGMENT);

  val = rb_str_new(at, length);
  rb_hash_aset(req, global_fragment, val);
}

static void request_path(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = Qnil;

  VALIDATE_MAX_LENGTH(length, REQUEST_PATH);

  val = rb_str_new(at, length);
  rb_hash_aset(req, global_request_path, val);
  rb_hash_aset(req, global_path_info, val);
}

static void query_string(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = Qnil;

  VALIDATE_MAX_LENGTH(length, QUERY_STRING);

  val = rb_str_new(at, length);
  rb_hash_aset(req, global_query_string, val);
}

static void http_version(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE val = rb_str_new(at, length);
  rb_hash_aset(req, global_http_version, val);
}

/** Finalizes the request header to have a bunch of stuff that's
  needed. */

static void header_done(void *data, const char *at, size_t length)
{
  VALUE req = (VALUE)data;
  VALUE temp = Qnil;
  VALUE ctype = Qnil;
  VALUE clen = Qnil;
  VALUE body = Qnil;
  char *colon = NULL;

  clen = rb_hash_aref(req, global_http_content_length);
  if(clen != Qnil) {
    rb_hash_aset(req, global_content_length, clen);
    rb_hash_delete(req, global_http_content_length);
  }

  ctype = rb_hash_aref(req, global_http_content_type);
  if(ctype != Qnil) {
    rb_hash_aset(req, global_content_type, ctype);
    rb_hash_delete(req, global_http_content_type);
  }

  rb_hash_aset(req, global_gateway_interface, global_gateway_interface_value);
  if((temp = rb_hash_aref(req, global_http_host)) != Qnil) {
    /* ruby better close strings off with a '\0' dammit */
    colon = strchr(RSTRING_PTR(temp), ':');
    if(colon != NULL) {
      rb_hash_aset(req, global_server_name, rb_str_substr(temp, 0, colon - RSTRING_PTR(temp)));
      rb_hash_aset(req, global_server_port, 
          rb_str_substr(temp, colon - RSTRING_PTR(temp)+1, 
            RSTRING_LEN(temp)));
    } else {
      rb_hash_aset(req, global_server_name, temp);
      rb_hash_aset(req, global_server_port, global_port_80);
    }
  }

  /* grab the initial body and stuff it into the hash */
  if(length > 0) {
    body = rb_hash_aref(req, global_http_body);
    rb_io_write(body, rb_str_new(at, length));
  }
  
  /* according to Rack specs, query string must be empty string if none */
  if (rb_hash_aref(req, global_query_string) == Qnil) {
    rb_hash_aset(req, global_query_string, global_empty);
  }
  if (rb_hash_aref(req, global_path_info) == Qnil) {
    rb_hash_aset(req, global_path_info, global_empty);
  }
  
  /* set some constants */
  rb_hash_aset(req, global_server_protocol, global_server_protocol_value);
  rb_hash_aset(req, global_url_scheme, global_url_scheme_value);
  rb_hash_aset(req, global_script_name, global_empty);
}


void Thin_HttpParser_free(void *data) {
  TRACE();

  if(data) {
    free(data);
  }
}


VALUE Thin_HttpParser_alloc(VALUE klass)
{
  VALUE obj;
  http_parser *hp = ALLOC_N(http_parser, 1);
  TRACE();
  hp->http_field = http_field;
  hp->request_method = request_method;
  hp->request_uri = request_uri;
  hp->fragment = fragment;
  hp->request_path = request_path;
  hp->query_string = query_string;
  hp->http_version = http_version;
  hp->header_done = header_done;
  thin_http_parser_init(hp);

  obj = Data_Wrap_Struct(klass, NULL, Thin_HttpParser_free, hp);

  return obj;
}


/**
 * call-seq:
 *    parser.new -> parser
 *
 * Creates a new parser.
 */
VALUE Thin_HttpParser_init(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);
  thin_http_parser_init(http);

  return self;
}


/**
 * call-seq:
 *    parser.reset -> nil
 *
 * Resets the parser to it's initial state so that you can reuse it
 * rather than making new ones.
 */
VALUE Thin_HttpParser_reset(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);
  thin_http_parser_init(http);

  return Qnil;
}


/**
 * call-seq:
 *    parser.finish -> true/false
 *
 * Finishes a parser early which could put in a "good" or bad state.
 * You should call reset after finish it or bad things will happen.
 */
VALUE Thin_HttpParser_finish(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);
  thin_http_parser_finish(http);

  return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
}


/**
 * call-seq:
 *    parser.execute(req_hash, data, start) -> Integer
 *
 * Takes a Hash and a String of data, parses the String of data filling in the Hash
 * returning an Integer to indicate how much of the data has been read.  No matter
 * what the return value, you should call HttpParser#finished? and HttpParser#error?
 * to figure out if it's done parsing or there was an error.
 * 
 * This function now throws an exception when there is a parsing error.  This makes 
 * the logic for working with the parser much easier.  You can still test for an 
 * error, but now you need to wrap the parser with an exception handling block.
 *
 * The third argument allows for parsing a partial request and then continuing
 * the parsing from that position.  It needs all of the original data as well 
 * so you have to append to the data buffer as you read.
 */
VALUE Thin_HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
{
  http_parser *http = NULL;
  int from = 0;
  char *dptr = NULL;
  long dlen = 0;

  DATA_GET(self, http_parser, http);

  from = FIX2INT(start);
  dptr = RSTRING_PTR(data);
  dlen = RSTRING_LEN(data);

  if(from >= dlen) {
    rb_raise(eHttpParserError, "%s", "Requested start is after data buffer end.");
  } else {
    http->data = (void *)req_hash;
    thin_http_parser_execute(http, dptr, dlen, from);

    VALIDATE_MAX_LENGTH(http_parser_nread(http), HEADER);

    if(thin_http_parser_has_error(http)) {
      rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
    } else {
      return INT2FIX(http_parser_nread(http));
    }
  }
}



/**
 * call-seq:
 *    parser.error? -> true/false
 *
 * Tells you whether the parser is in an error state.
 */
VALUE Thin_HttpParser_has_error(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);

  return thin_http_parser_has_error(http) ? Qtrue : Qfalse;
}


/**
 * call-seq:
 *    parser.finished? -> true/false
 *
 * Tells you whether the parser is finished or not and in a good state.
 */
VALUE Thin_HttpParser_is_finished(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);

  return thin_http_parser_is_finished(http) ? Qtrue : Qfalse;
}


/**
 * call-seq:
 *    parser.nread -> Integer
 *
 * Returns the amount of data processed so far during this processing cycle.  It is
 * set to 0 on initialize or reset calls and is incremented each time execute is called.
 */
VALUE Thin_HttpParser_nread(VALUE self)
{
  http_parser *http = NULL;
  DATA_GET(self, http_parser, http);

  return INT2FIX(http->nread);
}

void Init_thin_parser()
{

  mThin = rb_define_module("Thin");

  DEF_GLOBAL(empty, "");
  DEF_GLOBAL(http_prefix, "HTTP_");
  DEF_GLOBAL(request_method, "REQUEST_METHOD");
  DEF_GLOBAL(request_uri, "REQUEST_URI");
  DEF_GLOBAL(fragment, "FRAGMENT");
  DEF_GLOBAL(query_string, "QUERY_STRING");
  DEF_GLOBAL(http_version, "HTTP_VERSION");
  DEF_GLOBAL(request_path, "REQUEST_PATH");
  DEF_GLOBAL(content_length, "CONTENT_LENGTH");
  DEF_GLOBAL(http_content_length, "HTTP_CONTENT_LENGTH");
  DEF_GLOBAL(content_type, "CONTENT_TYPE");
  DEF_GLOBAL(http_content_type, "HTTP_CONTENT_TYPE");
  DEF_GLOBAL(gateway_interface, "GATEWAY_INTERFACE");
  DEF_GLOBAL(gateway_interface_value, "CGI/1.2");
  DEF_GLOBAL(server_name, "SERVER_NAME");
  DEF_GLOBAL(server_port, "SERVER_PORT");
  DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
  DEF_GLOBAL(server_protocol_value, "HTTP/1.1");
  DEF_GLOBAL(http_host, "HTTP_HOST");
  DEF_GLOBAL(port_80, "80");
  DEF_GLOBAL(http_body, "rack.input");
  DEF_GLOBAL(url_scheme, "rack.url_scheme");
  DEF_GLOBAL(url_scheme_value, "http");
  DEF_GLOBAL(script_name, "SCRIPT_NAME");
  DEF_GLOBAL(path_info, "PATH_INFO");

  eHttpParserError = rb_define_class_under(mThin, "InvalidRequest", rb_eIOError);

  cHttpParser = rb_define_class_under(mThin, "HttpParser", rb_cObject);
  rb_define_alloc_func(cHttpParser, Thin_HttpParser_alloc);
  rb_define_method(cHttpParser, "initialize", Thin_HttpParser_init,0);
  rb_define_method(cHttpParser, "reset", Thin_HttpParser_reset,0);
  rb_define_method(cHttpParser, "finish", Thin_HttpParser_finish,0);
  rb_define_method(cHttpParser, "execute", Thin_HttpParser_execute,3);
  rb_define_method(cHttpParser, "error?", Thin_HttpParser_has_error,0);
  rb_define_method(cHttpParser, "finished?", Thin_HttpParser_is_finished,0);
  rb_define_method(cHttpParser, "nread", Thin_HttpParser_nread,0);
}
thin-1.6.3/ext/thin_parser/ext_help.h0000644000004100000410000000075112432275201017613 0ustar  www-datawww-data#ifndef ext_help_h
#define ext_help_h

#define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be.");
#define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name);
#define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T);

#ifdef DEBUG
#define TRACE()  fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__)
#else
#define TRACE() 
#endif

#endif
thin-1.6.3/ext/thin_parser/parser.h0000644000004100000410000000221712432275201017276 0ustar  www-datawww-data/**
 * Copyright (c) 2005 Zed A. Shaw
 * You can redistribute it and/or modify it under the same terms as Ruby.
 */

#ifndef http11_parser_h
#define http11_parser_h

#include 

#if defined(_WIN32)
#include 
#endif

typedef void (*element_cb)(void *data, const char *at, size_t length);
typedef void (*field_cb)(void *data, const char *field, size_t flen, const char *value, size_t vlen);

typedef struct http_parser { 
  int cs;
  size_t body_start;
  int content_len;
  size_t nread;
  size_t mark;
  size_t field_start;
  size_t field_len;
  size_t query_start;

  void *data;

  field_cb http_field;
  element_cb request_method;
  element_cb request_uri;
  element_cb fragment;
  element_cb request_path;
  element_cb query_string;
  element_cb http_version;
  element_cb header_done;
  
} http_parser;

int http_parser_init(http_parser *parser);
int http_parser_finish(http_parser *parser);
size_t http_parser_execute(http_parser *parser, const char *data, size_t len, size_t off);
int http_parser_has_error(http_parser *parser);
int http_parser_is_finished(http_parser *parser);

#define http_parser_nread(parser) (parser)->nread 

#endif
thin-1.6.3/ext/thin_parser/common.rl0000644000004100000410000000405212432275201017457 0ustar  www-datawww-data%%{
  
  machine http_parser_common;

#### HTTP PROTOCOL GRAMMAR
# line endings
  CRLF = "\r\n";

# character types
  CTL = (cntrl | 127);
  safe = ("$" | "-" | "_" | ".");
  extra = ("!" | "*" | "'" | "(" | ")" | ",");
  reserved = (";" | "/" | "?" | ":" | "@" | "&" | "=" | "+");
  sorta_safe = ("\"" | "<" | ">");
  unsafe = (CTL | " " | "#" | "%" | sorta_safe);
  national = any -- (alpha | digit | reserved | extra | safe | unsafe);
  unreserved = (alpha | digit | safe | extra | national);
  escape = ("%" "u"? xdigit xdigit);
  uchar = (unreserved | escape | sorta_safe);
  pchar = (uchar | ":" | "@" | "&" | "=" | "+");
  tspecials = ("(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\\" | "\"" | "/" | "[" | "]" | "?" | "=" | "{" | "}" | " " | "\t");

# elements
  token = (ascii -- (CTL | tspecials));

# URI schemes and absolute paths
  scheme = ( "http"i ("s"i)? );
  hostname = ((alnum | "-" | "." | "_")+ | ("[" (":" | xdigit)+ "]"));
  host_with_port = (hostname (":" digit*)?);
  userinfo = ((unreserved | escape | ";" | ":" | "&" | "=" | "+")+ "@")*;

  path = ( pchar+ ( "/" pchar* )* ) ;
  query = ( uchar | reserved )* %query_string ;
  param = ( pchar | "/" )* ;
  params = ( param ( ";" param )* ) ;
  rel_path = (path? (";" params)? %request_path) ("?" %start_query query)?;
  absolute_path = ( "/"+ rel_path );
  path_uri = absolute_path > mark %request_uri;
  Absolute_URI = (scheme "://" userinfo host_with_port path_uri);

  Request_URI = ((absolute_path | "*") >mark %request_uri) | Absolute_URI;
  Fragment = ( uchar | reserved )* >mark %fragment;
  Method = ( upper | digit | safe ){1,20} >mark %request_method;

  http_number = ( digit+ "." digit+ ) ;
  HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
  Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;

  field_name = ( token -- ":" )+ >start_field %write_field;

  field_value = any* >start_value %write_value;

  message_header = field_name ":" " "* field_value :> CRLF;

  Request = Request_Line ( message_header )* ( CRLF @done );

main := Request;

}%%
thin-1.6.3/ext/thin_parser/parser.rl0000644000004100000410000000724112432275201017466 0ustar  www-datawww-data/**
 * Copyright (c) 2005 Zed A. Shaw
 * You can redistribute it and/or modify it under the same terms as Ruby.
 */
#include "parser.h"
#include 
#include 
#include 
#include 
#include 

#define LEN(AT, FPC) (FPC - buffer - parser->AT)
#define MARK(M,FPC) (parser->M = (FPC) - buffer)
#define PTR_TO(F) (buffer + parser->F)

/** Machine **/

%%{
  
  machine http_parser;

  action mark {MARK(mark, fpc); }


  action start_field { MARK(field_start, fpc); }
  action write_field { 
    parser->field_len = LEN(field_start, fpc);
  }

  action start_value { MARK(mark, fpc); }
  action write_value { 
    if (parser->http_field != NULL) {
      parser->http_field(parser->data, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
    }
  }
  action request_method { 
    if (parser->request_method != NULL) {
      parser->request_method(parser->data, PTR_TO(mark), LEN(mark, fpc));
    }
  }
  action request_uri {
    if (parser->request_uri != NULL) {
      parser->request_uri(parser->data, PTR_TO(mark), LEN(mark, fpc));
    }
  }
  action fragment { 
    if (parser->fragment != NULL) {
      parser->fragment(parser->data, PTR_TO(mark), LEN(mark, fpc));
    }
  }

  action start_query {MARK(query_start, fpc); }
  action query_string { 
    if (parser->query_string != NULL) {
      parser->query_string(parser->data, PTR_TO(query_start), LEN(query_start, fpc));
    }
  }

  action http_version {	
    if (parser->http_version != NULL) {
      parser->http_version(parser->data, PTR_TO(mark), LEN(mark, fpc));
    }
  }

  action request_path {
    if (parser->request_path != NULL) {
      parser->request_path(parser->data, PTR_TO(mark), LEN(mark,fpc));
    }
  }

  action done { 
    parser->body_start = fpc - buffer + 1; 
    if (parser->header_done != NULL) {
      parser->header_done(parser->data, fpc + 1, pe - fpc - 1);
    }
    fbreak;
  }

  include http_parser_common "common.rl";

}%%

/** Data **/
%% write data;

int thin_http_parser_init(http_parser *parser)  {
  int cs = 0;
  %% write init;
  parser->cs = cs;
  parser->body_start = 0;
  parser->content_len = 0;
  parser->mark = 0;
  parser->nread = 0;
  parser->field_len = 0;
  parser->field_start = 0;    

  return(1);
}


/** exec **/
size_t thin_http_parser_execute(http_parser *parser, const char *buffer, size_t len, size_t off)  {
  const char *p, *pe;
  int cs = parser->cs;

  assert(off <= len && "offset past end of buffer");

  p = buffer+off;
  pe = buffer+len;

  assert(*pe == '\0' && "pointer does not end on NUL");
  assert(pe - p == (long)(len - off) && "pointers aren't same distance");


  %% write exec;

  parser->cs = cs;
  parser->nread += p - (buffer + off);

  assert(p <= pe && "buffer overflow after parsing execute");
  assert(parser->nread <= len && "nread longer than length");
  assert(parser->body_start <= len && "body starts after buffer end");
  assert(parser->mark < len && "mark is after buffer end");
  assert(parser->field_len <= len && "field has length longer than whole buffer");
  assert(parser->field_start < len && "field starts after buffer end");

  if(parser->body_start) {
    /* final \r\n combo encountered so stop right here */
    parser->nread++;
  }

  return(parser->nread);
}

int thin_http_parser_has_error(http_parser *parser) {
  return parser->cs == http_parser_error;
}

int thin_http_parser_is_finished(http_parser *parser) {
  return parser->cs == http_parser_first_final;
}

int thin_http_parser_finish(http_parser *parser)
{
  int cs = parser->cs;


  parser->cs = cs;

  if (thin_http_parser_has_error(parser) ) {
    return -1;
  } else if (thin_http_parser_is_finished(parser) ) {
    return 1;
  } else {
    return 0;
  }
}
thin-1.6.3/README.md0000644000004100000410000001360212432275201013772 0ustar  www-datawww-dataThin
====

Tiny, fast & funny HTTP server

Thin is a Ruby web server that glues together 3 of the best Ruby libraries in web history:

  * The Mongrel parser: the root of Mongrel speed and security
  * Event Machine: a network I/O library with extremely high scalability, performance and stability
  * Rack: a minimal interface between webservers and Ruby frameworks

Which makes it, with all humility, the most secure, stable, fast and extensible Ruby web server
bundled in an easy to use gem for your own pleasure.

    Site:  http://code.macournoyer.com/thin/
    Group: http://groups.google.com/group/thin-ruby/topics
    Bugs:  http://github.com/macournoyer/thin/issues
    Code:  http://github.com/macournoyer/thin
    IRC:   #thin on freenode

## Installation

For the latest stable version:

`gem install thin`

Or from source:

```
git clone git://github.com/macournoyer/thin.git
cd thin
rake install
```

## Usage

A +thin+ script offers an easy way to start your Rack application:

```
cd to/your/app
thin start
```

When using with Rails and Bundler, make sure to add `gem 'thin'`
to your Gemfile.

See example directory for samples.

### Command Line Examples

Use a rackup file and bind to localhost port 8080:

```
thin -R config.ru -a 127.0.0.1 -p 8080 start
```

Store the server process ID, log to a file and daemonize:

```
thin -p 9292 -P tmp/pids/thin.pid -l logs/thin.log -d start
```

Thin is quite flexible in that many options can be specified at the command line (see below for usage).

### Configuration files

You can create configuration files in yaml format and feed them to thin using `thin -C config.yml`.  Here is an example config file:

```yaml
--- 
user: www-data
group: www-data
pid: tmp/pids/thin.pid
timeout: 30
wait: 30
log: log/thin.log
max_conns: 1024
require: []
environment: production
max_persistent_conns: 512
servers: 1
threaded: true
no-epoll: true
daemonize: true
socket: tmp/sockets/thin.sock
chdir: /path/to/your/apps/root
tag: a-name-to-show-up-in-ps aux
```

### Command Line Options

This is the usage for the thin command which can be obtained by running `thin -h` at the command line.

```sh
Usage: thin [options] start|stop|restart|config|install

Server options:
    -a, --address HOST               bind to HOST address (default: 0.0.0.0)
    -p, --port PORT                  use PORT (default: 3000)
    -S, --socket FILE                bind to unix domain socket
    -y, --swiftiply [KEY]            Run using swiftiply
    -A, --adapter NAME               Rack adapter to use (default: autodetect)
                                     (rack, rails, ramaze, merb, file)
    -R, --rackup FILE                Load a Rack config file instead of Rack adapter
    -c, --chdir DIR                  Change to dir before starting
        --stats PATH                 Mount the Stats adapter under PATH

SSL options:
        --ssl                        Enables SSL
        --ssl-key-file PATH          Path to private key
        --ssl-cert-file PATH         Path to certificate
        --ssl-disable-verify         Disables (optional) client cert requests

Adapter options:
    -e, --environment ENV            Framework environment (default: development)
        --prefix PATH                Mount the app under PATH (start with /)

Daemon options:
    -d, --daemonize                  Run daemonized in the background
    -l, --log FILE                   File to redirect output (default: /home/robert/log/thin.log)
    -P, --pid FILE                   File to store PID (default: tmp/pids/thin.pid)
    -u, --user NAME                  User to run daemon as (use with -g)
    -g, --group NAME                 Group to run daemon as (use with -u)
        --tag NAME                   Additional text to display in process listing

Cluster options:
    -s, --servers NUM                Number of servers to start
    -o, --only NUM                   Send command to only one server of the cluster
    -C, --config FILE                Load options from config file
        --all [DIR]                  Send command to each config files in DIR
    -O, --onebyone                   Restart the cluster one by one (only works with restart command)
    -w, --wait NUM                   Maximum wait time for server to be started in seconds (use with -O)

Tuning options:
    -b, --backend CLASS              Backend to use, full classname
    -t, --timeout SEC                Request or command timeout in sec (default: 30)
    -f, --force                      Force the execution of the command
        --max-conns NUM              Maximum number of open file descriptors (default: 1024)
                                     Might require sudo to set higher than 1024
        --max-persistent-conns NUM   Maximum number of persistent connections
                                     (default: 100)
        --threaded                   Call the Rack application in threads [experimental]
        --threadpool-size NUM        Sets the size of the EventMachine threadpool.
                                     (default: 20)
        --no-epoll                   Disable the use of epoll

Common options:
    -r, --require FILE               require the library
    -q, --quiet                      Silence all logging
    -D, --debug                      Enable debug logging
    -V, --trace                      Set tracing on (log raw request/response)
    -h, --help                       Show this message
    -v, --version                    Show version
```

## License

Ruby License, http://www.ruby-lang.org/en/LICENSE.txt.

## Credits

The parser was stolen from Mongrel http://mongrel.rubyforge.org by Zed Shaw.
Mongrel is copyright 2007 Zed A. Shaw and contributors. It is licensed under
the Ruby license and the GPL2.

Thin is copyright Marc-Andre Cournoyer 

Get help at http://groups.google.com/group/thin-ruby/
Report bugs at https://github.com/macournoyer/thin/issues
and major security issues directly to me macournoyer@gmail.com.