pronghorn 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Guillermo Iguaran
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # Pronghorn
2
+
3
+ _Minimal HTTP server for Rack applications_
4
+
5
+ ## Description
6
+
7
+ Pronghorn is a minimal and fast HTTP server for Rack applications.
8
+
9
+ It's powered by [EventMachine](https://github.com/eventmachine/eventmachine), Ryan Dahl's [http-parser](https://github.com/joyent/http-parser) and [Rack](https://github.com/rack/rack). It's highly inspired by [Thin](https://github.com/macournoyer/thin)
10
+
11
+ ## Installation
12
+
13
+ $ gem install pronghorn
14
+
15
+
16
+ ## Usage
17
+
18
+ ### Rails
19
+
20
+ Add Pronghorn to your Gemfile:
21
+
22
+ ```ruby
23
+ gem "pronghorn"
24
+ ```
25
+
26
+ Run your application with the Pronghorn server:
27
+
28
+ rails s pronghorn
29
+
30
+ ### Sinatra
31
+
32
+ Configure Sinatra to use Pronghorn server:
33
+
34
+ ```ruby
35
+ configure { set :server, :pronghorn }
36
+ ```
37
+
38
+ ### Rack
39
+
40
+ Run rackup using pronghorn server:
41
+
42
+ $ rackup -s pronghorn
43
+
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
52
+
53
+
54
+ ## Copyright
55
+
56
+ Copyright (c) 2012 Guillermo Iguaran. See LICENSE for further details.
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+
4
+ task :test do
5
+ Dir.chdir('test')
6
+ end
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << '../lib'
10
+ t.libs << '../test'
11
+ t.test_files = FileList['*_test.rb']
12
+ t.verbose = false
13
+ end
14
+
15
+ task :default => :test
data/lib/pronghorn.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'eventmachine'
2
+ require 'http/parser'
3
+ require 'rack'
4
+
5
+ require 'stringio'
6
+ require 'forwardable'
7
+ require 'socket'
8
+
9
+ require 'pronghorn/version'
10
+ require 'pronghorn/server'
11
+ require 'pronghorn/connection'
12
+ require 'pronghorn/request'
13
+ require 'pronghorn/response'
@@ -0,0 +1,55 @@
1
+ module Pronghorn
2
+ class Connection < EventMachine::Connection
3
+
4
+ attr_accessor :request
5
+
6
+ attr_accessor :response
7
+
8
+ attr_accessor :app
9
+
10
+ attr_accessor :server
11
+
12
+ def post_init
13
+ @request = Request.new
14
+ @response = Response.new
15
+ @request.parser.on_message_complete = method(:process)
16
+ end
17
+
18
+ def receive_data(data)
19
+ @request.parser << data
20
+ end
21
+
22
+ def process
23
+ send_response(process_request)
24
+ end
25
+
26
+ def process_request
27
+ @request.set_environment({
28
+ "REMOTE_ADDR" => remote_address,
29
+ "SERVER_PORT" => @server.port.to_s
30
+ })
31
+ response = @app.call(@request.env)
32
+ end
33
+
34
+ def send_response(result)
35
+ return unless result
36
+ result = result.to_a
37
+ @response.status, @response.headers, @response.body = *result
38
+ @response.each do |chunk|
39
+ send_data chunk
40
+ end
41
+ @response.close
42
+ post_init
43
+ end
44
+
45
+ def unbind
46
+ @server.finish_connection(self)
47
+ end
48
+
49
+ def remote_address
50
+ Socket.unpack_sockaddr_in(get_peername)[1]
51
+ rescue
52
+ nil
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,60 @@
1
+ module Pronghorn
2
+ class Request
3
+
4
+ attr_reader :parser
5
+
6
+ attr_reader :env
7
+
8
+ PROTO_ENV = {
9
+ "SERVER_SOFTWARE".freeze => "PragHorn #{::Pronghorn::VERSION}".freeze,
10
+ "SERVER_NAME".freeze => "localhost",
11
+ "SERVER_PROTOCOL".freeze => "HTTP/1.1",
12
+ "HTTP_VERSION".freeze => "HTTP/1.1",
13
+ "SCRIPT_NAME".freeze => "",
14
+ "QUERY_STRING".freeze => "",
15
+ "GATEWAY_INTERFACE" => "CGI/1.2",
16
+
17
+ "rack.url_scheme".freeze => "http",
18
+ "rack.errors".freeze => STDERR,
19
+ "rack.multithread".freeze => false,
20
+ "rack.multiprocess".freeze => false,
21
+ "rack.run_once".freeze => false,
22
+ "rack.version".freeze => Rack::VERSION
23
+ }
24
+
25
+ def initialize
26
+ @parser = Http::Parser.new
27
+ @parser.on_body = proc do |chunk|
28
+ @body << chunk
29
+ end
30
+ @parser.on_headers_complete = proc do |headers|
31
+ @headers = headers
32
+ end
33
+
34
+ initial_body = ''
35
+ initial_body.encode!(Encoding::ASCII_8BIT) if initial_body.respond_to?(:encode!)
36
+ @body = StringIO.new(initial_body.dup)
37
+ end
38
+
39
+ def set_environment(env_vars = {})
40
+ @env = PROTO_ENV.dup
41
+ @env.merge!({
42
+ "REQUEST_METHOD".freeze => @parser.http_method,
43
+ "REQUEST_URI".freeze => @parser.request_url,
44
+ "REQUEST_PATH".freeze => @parser.request_path,
45
+ "QUERY_STRING".freeze => @parser.query_string,
46
+ "PATH_INFO".freeze => @parser.request_path,
47
+ "rack.input".freeze => @body
48
+ })
49
+ @env["FRAGMENT"] = @parser.fragment if @parser.fragment
50
+ if @headers.key?("Content-Length")
51
+ @env["CONTENT_LENGTH"] = @headers.delete("Content-Length").to_i
52
+ end
53
+ if @headers.key?("Content-Type")
54
+ @env["CONTENT_TYPE"] = @headers.delete("Content-Type")
55
+ end
56
+ @headers.each{|k, v| @env["HTTP_#{k.upcase.gsub('-','_')}"] = v}
57
+ @env.merge!(env_vars)
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,44 @@
1
+ module Pronghorn
2
+ class Response
3
+
4
+ attr_accessor :headers
5
+
6
+ attr_accessor :body
7
+
8
+ attr_accessor :status
9
+
10
+ def initialize(status = 200, headers = {"Content-Type" => "text/plain"}, body = [""])
11
+ @status = status
12
+ @headers = headers
13
+ @body = body
14
+ end
15
+
16
+ def head
17
+ lines = []
18
+ lines << "HTTP/1.1 #{@status}\r\n"
19
+ lines += generate_header_lines(@headers)
20
+ lines << "\r\n"
21
+ lines.join
22
+ end
23
+
24
+ def close
25
+ @body.close if @body.respond_to?(:close)
26
+ end
27
+
28
+ def each
29
+ yield head
30
+ @body.each { |chunk| yield chunk }
31
+ end
32
+
33
+ private
34
+
35
+ def generate_header_lines(headers)
36
+ output = []
37
+ headers.each do |header, value|
38
+ output << "#{header}: #{value}\r\n"
39
+ end
40
+ output
41
+ end
42
+
43
+ end
44
+ end
@@ -0,0 +1,60 @@
1
+ module Pronghorn
2
+ class Server
3
+
4
+ attr_accessor :app
5
+
6
+ attr_reader :host
7
+
8
+ attr_reader :port
9
+
10
+ def initialize(app, &block)
11
+ @app = app
12
+ @app = Rack::Builder.new(&block).to_app if block
13
+ @connections = []
14
+ end
15
+
16
+ def start(host, port)
17
+ @host = host
18
+ @port = port
19
+ starter = proc do
20
+ @signature = EventMachine.start_server @host, @port, Connection, &method(:initialize_connection)
21
+ @running = true
22
+ end
23
+
24
+ Signal.trap("INT") { stop }
25
+ Signal.trap("TERM") { stop }
26
+
27
+ puts "Proghorn #{::Pronghorn::VERSION} starting..."
28
+ puts "* Listening on http://#{@host}:#{@port}"
29
+
30
+ EventMachine.error_handler{ |error|
31
+ puts error.message
32
+ }
33
+
34
+ if EventMachine.reactor_running?
35
+ starter.call
36
+ else
37
+ EventMachine.run(&starter)
38
+ end
39
+ end
40
+
41
+ def stop
42
+ puts "* Stopping server"
43
+ return unless @running
44
+ @running = false
45
+ EventMachine.stop_server(@signature)
46
+ EventMachine.stop if EventMachine.reactor_running?
47
+ end
48
+
49
+ def finish_connection(connection)
50
+ @connections.delete(connection)
51
+ end
52
+
53
+ private
54
+ def initialize_connection(connection)
55
+ connection.app = @app
56
+ connection.server = self
57
+ @connections << connection
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,3 @@
1
+ module Pronghorn
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,34 @@
1
+ require 'rack/handler'
2
+ require 'pronghorn'
3
+
4
+ module Rack
5
+ module Handler
6
+ module Pronghorn
7
+ DEFAULT_OPTIONS = {
8
+ :Host => '0.0.0.0',
9
+ :Port => 8080,
10
+ :Verbose => false
11
+ }
12
+
13
+ def self.run(app, options = {})
14
+ options = DEFAULT_OPTIONS.merge(options)
15
+
16
+ if options[:Verbose]
17
+ app = Rack::CommonLogger.new(app, STDOUT)
18
+ end
19
+ server = ::Pronghorn::Server.new(app)
20
+ yield server if block_given?
21
+ server.start(options[:Host], options[:Port])
22
+ end
23
+
24
+ def self.valid_options
25
+ {
26
+ "Host=HOST" => "Hostname to listen on (default: localhost)",
27
+ "Port=PORT" => "Port to listen on (default: 8080)"
28
+ }
29
+ end
30
+ end
31
+
32
+ register :pronghorn, Pronghorn
33
+ end
34
+ end
data/pronghorn.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/pronghorn/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "pronghorn"
6
+ gem.version = Pronghorn::VERSION
7
+
8
+ gem.authors = ["Guillermo Iguaran"]
9
+ gem.email = ["guilleiguaran@gmail.com"]
10
+ gem.description = %q{Minimal server for Rack apps based in EventMachine and http-parser}
11
+ gem.summary = %q{Minimal HTTP server for Rack apps}
12
+ gem.homepage = "http://guilleiguaran.github.com/pronghorn"
13
+
14
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_dependency 'eventmachine'
20
+ gem.add_dependency 'http_parser.rb'
21
+ gem.add_dependency 'rack'
22
+ gem.add_development_dependency 'minitest'
23
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pronghorn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Guillermo Iguaran
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: eventmachine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: http_parser.rb
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rack
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: minitest
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Minimal server for Rack apps based in EventMachine and http-parser
79
+ email:
80
+ - guilleiguaran@gmail.com
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - LICENSE
87
+ - README.md
88
+ - Rakefile
89
+ - lib/pronghorn.rb
90
+ - lib/pronghorn/connection.rb
91
+ - lib/pronghorn/request.rb
92
+ - lib/pronghorn/response.rb
93
+ - lib/pronghorn/server.rb
94
+ - lib/pronghorn/version.rb
95
+ - lib/rack/handler/pronghorn.rb
96
+ - pronghorn.gemspec
97
+ homepage: http://guilleiguaran.github.com/pronghorn
98
+ licenses: []
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.22
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: Minimal HTTP server for Rack apps
121
+ test_files: []
122
+ has_rdoc: