miniserver 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/Rakefile +1 -0
- data/bin/miniserver +5 -0
- data/lib/miniserver.rb +26 -0
- data/lib/miniserver/command.rb +106 -0
- data/lib/miniserver/connection.rb +78 -0
- data/lib/miniserver/const.rb +96 -0
- data/lib/miniserver/logger.rb +12 -0
- data/lib/miniserver/request.rb +30 -0
- data/lib/miniserver/response.rb +11 -0
- data/lib/miniserver/runner.rb +64 -0
- data/lib/miniserver/server.rb +65 -0
- data/lib/miniserver/version.rb +3 -0
- data/lib/rack/handler/miniserver.rb +93 -0
- data/miniserver.gemspec +25 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9ad70987c27eba35d73e421e04b626e8cd0bd6a5
|
4
|
+
data.tar.gz: f974f8e519e04bf356e9f24497600e561fa2d0a8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b5fefa23eb21d64ee6473e25712b6153fde6bc674ac0081c12d4f4b3e7c5b9e34aa73c54bab6062aa077ea6aaa4e916650cb67598f13e3bb718a6d4c6082168f
|
7
|
+
data.tar.gz: 57e62a7cf011f9e4d78aedaec76248dd8d76af9e528c4799eed8edc0af28ec6d2ac2749296e756c8167cf70ca72ce0e2d5d35c61d1caa889f58bc5b4acdd79ab
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 zhouguangming
|
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,25 @@
|
|
1
|
+
# Miniserver
|
2
|
+
|
3
|
+
Miniserver is a simple fast HTTP 1.1 server for Ruby/Rack applications.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'miniserver'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install miniserver
|
18
|
+
|
19
|
+
## Contributing
|
20
|
+
|
21
|
+
1. Fork it
|
22
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
23
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
24
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
25
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/miniserver
ADDED
data/lib/miniserver.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'miniserver/version'
|
2
|
+
require 'miniserver/server'
|
3
|
+
require 'miniserver/const'
|
4
|
+
require 'miniserver/command'
|
5
|
+
require 'rack/handler/miniserver'
|
6
|
+
|
7
|
+
module Process
|
8
|
+
def running?(pid)
|
9
|
+
Process.getpgid(pid) != -1
|
10
|
+
rescue Errno::EPERM
|
11
|
+
true
|
12
|
+
rescue Errno::ESRCH
|
13
|
+
false
|
14
|
+
end
|
15
|
+
module_function :running?
|
16
|
+
end
|
17
|
+
|
18
|
+
module Miniserver
|
19
|
+
def self.logger=(logger)
|
20
|
+
@logger=logger
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.logger
|
24
|
+
@logger
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'miniserver/runner'
|
2
|
+
require 'open3'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Miniserver
|
6
|
+
class Command
|
7
|
+
COMMANDS = %w(start stop restart status)
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@options = {
|
11
|
+
daemonize: false,
|
12
|
+
host: '0.0.0.0',
|
13
|
+
port: 8080,
|
14
|
+
rackup: ::File.expand_path('config.ru'),
|
15
|
+
log: ::File.expand_path('log/miniserver.log'),
|
16
|
+
pid: ::File.expand_path('tmp/pids/miniserver.pid'),
|
17
|
+
environment: 'development'
|
18
|
+
}
|
19
|
+
|
20
|
+
parser.parse!(argv)
|
21
|
+
|
22
|
+
@command = argv.pop || 'start'
|
23
|
+
end
|
24
|
+
|
25
|
+
def run!
|
26
|
+
if COMMANDS.include?(@command)
|
27
|
+
Runner.new(@options).send(@command)
|
28
|
+
else
|
29
|
+
puts @parser
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def parser
|
34
|
+
@parser ||= OptionParser.new do |o|
|
35
|
+
o.banner = "Miniserver #{Miniserver::VERSION}\nUsage: miniserver <options> #{COMMANDS.join('|')}"
|
36
|
+
o.separator ""
|
37
|
+
|
38
|
+
o.on "-p", "--port PORT", "Define the TCP port to bind (default: #{@options[:port]})" do |arg|
|
39
|
+
@options[:port] = arg
|
40
|
+
end
|
41
|
+
|
42
|
+
o.on "-o", "--host HOST", "Bind to HOST address (default: #{@options[:host]})" do |arg|
|
43
|
+
@options[:host] = arg
|
44
|
+
end
|
45
|
+
|
46
|
+
o.on "-l", "--log FILE", "File to redirect output (default: log/miniserver.log)" do |arg|
|
47
|
+
@options[:log] = arg
|
48
|
+
end
|
49
|
+
|
50
|
+
o.on "-P", "--pid FILE", "File to store PID (default: tmp/pids/miniserver.pid)" do |arg|
|
51
|
+
@options[:pid] = arg
|
52
|
+
end
|
53
|
+
|
54
|
+
o.on "-d", "--daemonize", "Run daemonized in the background(default: false)" do |arg|
|
55
|
+
@options[:daemonize] = arg
|
56
|
+
end
|
57
|
+
|
58
|
+
o.on "-e", "--environment ENVIRONMENT", "The environment to run the Rack app on (default development)" do |arg|
|
59
|
+
@options[:environment] = arg
|
60
|
+
end
|
61
|
+
|
62
|
+
o.on "-R", "--rackup FILE", "Load a Rack config file (default: #{@options[:rackup]})" do |arg|
|
63
|
+
@options[:rackup] = arg
|
64
|
+
end
|
65
|
+
|
66
|
+
o.on_tail "-h", "--help", "Show help" do
|
67
|
+
puts @parser
|
68
|
+
exit
|
69
|
+
end
|
70
|
+
|
71
|
+
o.on_tail("-v", "--version", "Show version") do
|
72
|
+
puts "Miniserver #{Miniserver::VERSION}"
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.restart(script, options)
|
79
|
+
File.delete(options[:pid]) if File.exist?(options[:pid])
|
80
|
+
|
81
|
+
shellified_options = options.inject([]) do |args, (name, value)|
|
82
|
+
option_name = name.to_s.tr("_", "-")
|
83
|
+
case value
|
84
|
+
when NilClass, TrueClass
|
85
|
+
args << "--#{option_name}"
|
86
|
+
when FalseClass
|
87
|
+
when Array
|
88
|
+
value.each { |v| args << "--#{option_name}=#{v}" }
|
89
|
+
else
|
90
|
+
args << "--#{option_name}=#{value}"
|
91
|
+
end
|
92
|
+
|
93
|
+
args
|
94
|
+
end
|
95
|
+
|
96
|
+
cmd = "#{script} #{shellified_options.compact.join(' ')} start"
|
97
|
+
|
98
|
+
Open3.popen3(cmd) do |stdin, stdout, stderr|
|
99
|
+
puts stdout.gets until stdout.eof?
|
100
|
+
puts stderr.gets until stderr.eof?
|
101
|
+
end
|
102
|
+
|
103
|
+
exit
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'miniserver/request'
|
2
|
+
require 'miniserver/response'
|
3
|
+
require 'http/parser'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Miniserver
|
7
|
+
class Connection < EM::Connection
|
8
|
+
attr_accessor :app
|
9
|
+
|
10
|
+
def post_init
|
11
|
+
@request = Request.new
|
12
|
+
@response = Response.new
|
13
|
+
@parser = Http::Parser.new(self)
|
14
|
+
end
|
15
|
+
|
16
|
+
def on_headers_complete(headers)
|
17
|
+
@request.headers = headers
|
18
|
+
@request.parse_header(@parser, self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def on_body(chunk)
|
22
|
+
@request.body << chunk
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_message_complete
|
26
|
+
begin
|
27
|
+
Rack::Handler::Miniserver.new(app).process(@request, @response)
|
28
|
+
send_response!
|
29
|
+
rescue
|
30
|
+
Miniserver.logger.error("!! Unexpected error while processing request: #{$!.message}")
|
31
|
+
ensure
|
32
|
+
close_connection_after_writing
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def receive_data(data)
|
37
|
+
@parser << data
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_response!
|
41
|
+
send_status_line
|
42
|
+
send_headers
|
43
|
+
send_data(BLANK_LINE)
|
44
|
+
send_body
|
45
|
+
end
|
46
|
+
|
47
|
+
def send_status_line
|
48
|
+
send_data("#{HTTP_VERSION} #{@response.status} #{HTTP_CODE[@response.status]}#{BLANK_LINE}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_headers
|
52
|
+
headers = String.new
|
53
|
+
|
54
|
+
@response.headers[SERVER] = MINISERVER
|
55
|
+
@response.headers[DATE] = Time.now.httpdate
|
56
|
+
|
57
|
+
@response.headers.each do |name, value|
|
58
|
+
if value.is_a?(String)
|
59
|
+
value.split(NEWLINE).each do |v|
|
60
|
+
headers.concat("#{name}#{COLON} #{v}#{BLANK_LINE}")
|
61
|
+
end
|
62
|
+
else
|
63
|
+
headers.concat("#{name}#{COLON} #{value}#{BLANK_LINE}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
send_data(headers)
|
68
|
+
end
|
69
|
+
|
70
|
+
def send_body
|
71
|
+
begin
|
72
|
+
@response.body.each { |chunk| send_data(chunk) }
|
73
|
+
ensure
|
74
|
+
@response.body.close if @response.body.respond_to?(:close)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Miniserver
|
2
|
+
HTTP_VERSION = 'HTTP/1.1'
|
3
|
+
NEWLINE = "\n"
|
4
|
+
BLANK_LINE = "\r\n"
|
5
|
+
SERVER = 'Server'
|
6
|
+
MINISERVER = "Miniserver #{Miniserver::VERSION}"
|
7
|
+
CONTENT_LENGTH = 'Content-Length'
|
8
|
+
CONTENT_TYPE = 'Content-Type'
|
9
|
+
COLON = ':'
|
10
|
+
HTTP_PREFIX = 'HTTP_'
|
11
|
+
HTTP = 'http'
|
12
|
+
HTTPS = 'https'
|
13
|
+
EMPTY_STRING = ''
|
14
|
+
MINUS = '-'
|
15
|
+
UNDERLINE = '_'
|
16
|
+
DATE = 'Date'
|
17
|
+
CONNECTION = 'Connection'
|
18
|
+
KEEP_ALIVE = 'keep-alive'
|
19
|
+
CLOSE = 'close'
|
20
|
+
HOST = 'Host'
|
21
|
+
PORT_80 = '80'
|
22
|
+
PATH_INFO = 'PATH_INFO'
|
23
|
+
REQUEST_METHOD = 'REQUEST_METHOD'
|
24
|
+
REQUEST_PATH = 'REQUEST_PATH'
|
25
|
+
SCRIPT_NAME = 'SCRIPT_NAME'
|
26
|
+
FRAGMENT = 'FRAGMENT'
|
27
|
+
QUERY_STRING = 'QUERY_STRING'
|
28
|
+
REMOTE_ADDR = 'REMOTE_ADDR'
|
29
|
+
SERVER_NAME = 'SERVER_NAME'
|
30
|
+
SERVER_PORT = 'SERVER_PORT'
|
31
|
+
|
32
|
+
HTTP_CODE = {
|
33
|
+
100 => 'Continue',
|
34
|
+
101 => 'Switching Protocols',
|
35
|
+
102 => 'Processing',
|
36
|
+
200 => 'OK',
|
37
|
+
201 => 'Created',
|
38
|
+
202 => 'Accepted',
|
39
|
+
203 => 'Non-Authoritative Information',
|
40
|
+
204 => 'No Content',
|
41
|
+
205 => 'Reset Content',
|
42
|
+
206 => 'Partial Content',
|
43
|
+
207 => 'Multi-Status',
|
44
|
+
208 => 'Already Reported',
|
45
|
+
226 => 'IM Used',
|
46
|
+
300 => 'Multiple Choices',
|
47
|
+
301 => 'Moved Permanently',
|
48
|
+
302 => 'Found',
|
49
|
+
303 => 'See Other',
|
50
|
+
304 => 'Not Modified',
|
51
|
+
305 => 'Use Proxy',
|
52
|
+
306 => 'Reserved',
|
53
|
+
307 => 'Temporary Redirect',
|
54
|
+
308 => 'Permanent Redirect',
|
55
|
+
400 => 'Bad Request',
|
56
|
+
401 => 'Unauthorized',
|
57
|
+
402 => 'Payment Required',
|
58
|
+
403 => 'Forbidden',
|
59
|
+
404 => 'Not Found',
|
60
|
+
405 => 'Method Not Allowed',
|
61
|
+
406 => 'Not Acceptable',
|
62
|
+
407 => 'Proxy Authentication Required',
|
63
|
+
408 => 'Request Timeout',
|
64
|
+
409 => 'Conflict',
|
65
|
+
410 => 'Gone',
|
66
|
+
411 => 'Length Required',
|
67
|
+
412 => 'Precondition Failed',
|
68
|
+
413 => 'Request Entity Too Large',
|
69
|
+
414 => 'Request-URI Too Long',
|
70
|
+
415 => 'Unsupported Media Type',
|
71
|
+
416 => 'Requested Range Not Satisfiable',
|
72
|
+
417 => 'Expectation Failed',
|
73
|
+
422 => 'Unprocessable Entity',
|
74
|
+
423 => 'Locked',
|
75
|
+
424 => 'Failed Dependency',
|
76
|
+
425 => 'Reserved for WebDAV advanced collections expired proposal',
|
77
|
+
426 => 'Upgrade Required',
|
78
|
+
427 => 'Unassigned',
|
79
|
+
428 => 'Precondition Required',
|
80
|
+
429 => 'Too Many Requests',
|
81
|
+
430 => 'Unassigned',
|
82
|
+
431 => 'Request Header Fields Too Large',
|
83
|
+
500 => 'Internal Server Error',
|
84
|
+
501 => 'Not Implemented',
|
85
|
+
502 => 'Bad Gateway',
|
86
|
+
503 => 'Service Unavailable',
|
87
|
+
504 => 'Gateway Timeout',
|
88
|
+
505 => 'HTTP Version Not Supported',
|
89
|
+
506 => 'Variant Also Negotiates (Experimental)',
|
90
|
+
507 => 'Insufficient Storage',
|
91
|
+
508 => 'Loop Detected',
|
92
|
+
509 => 'Unassigned',
|
93
|
+
510 => 'Not Extended',
|
94
|
+
511 => 'Network Authentication Required'
|
95
|
+
}
|
96
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Miniserver
|
2
|
+
class Request
|
3
|
+
attr_accessor :headers
|
4
|
+
attr_accessor :body
|
5
|
+
attr_reader :request_path
|
6
|
+
attr_reader :http_method
|
7
|
+
attr_reader :query_string
|
8
|
+
attr_reader :keep_alive
|
9
|
+
attr_reader :remote_addr
|
10
|
+
attr_reader :server_name
|
11
|
+
attr_reader :server_port
|
12
|
+
attr_reader :request_url
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@body = String.new
|
16
|
+
@headers = Hash.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_header(parser, socket)
|
20
|
+
@request_url = parser.request_url
|
21
|
+
@query_string = parser.query_string
|
22
|
+
@request_path = parser.request_path
|
23
|
+
@http_method = parser.http_method
|
24
|
+
@keep_alive = parser.keep_alive?
|
25
|
+
@remote_addr = Socket.unpack_sockaddr_in(socket.get_peername)[1]
|
26
|
+
@server_name, @server_port = @headers[HOST].split(COLON)
|
27
|
+
@server_port ||= PORT_80
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Miniserver
|
2
|
+
class Runner
|
3
|
+
attr_reader :app, :options
|
4
|
+
|
5
|
+
def initialize(options)
|
6
|
+
@options = options
|
7
|
+
end
|
8
|
+
|
9
|
+
def status
|
10
|
+
status = if File.exist?(options[:pid])
|
11
|
+
pid = File.open(options[:pid], 'r').read.to_i
|
12
|
+
|
13
|
+
if Process.running?(pid)
|
14
|
+
:Started
|
15
|
+
else
|
16
|
+
:Stopped
|
17
|
+
end
|
18
|
+
else
|
19
|
+
:Stopped
|
20
|
+
end
|
21
|
+
|
22
|
+
puts status
|
23
|
+
end
|
24
|
+
|
25
|
+
def start
|
26
|
+
if File.exist?(options[:pid])
|
27
|
+
pid = File.open(options[:pid], 'r').read.to_i
|
28
|
+
|
29
|
+
if Process.running?(pid)
|
30
|
+
raise "Miniserver has started in pid #{pid}."
|
31
|
+
else
|
32
|
+
File.delete(options[:pid])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rack/builder'
|
37
|
+
|
38
|
+
ENV["RACK_ENV"] = options[:environment]
|
39
|
+
|
40
|
+
@app = Rack::Builder.new_from_string(File.read(options[:rackup]))
|
41
|
+
|
42
|
+
Server.new(app, options).start
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop
|
46
|
+
if File.exist?(options[:pid])
|
47
|
+
pid = File.open(options[:pid], 'r').read.to_i
|
48
|
+
Process.kill(:INT, pid)
|
49
|
+
File.delete(options[:pid])
|
50
|
+
else
|
51
|
+
raise 'Miniserver is not running.'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def restart
|
56
|
+
if File.exist?(options[:pid])
|
57
|
+
pid = File.open(options[:pid], 'r').read.to_i
|
58
|
+
Process.kill(:HUP, pid)
|
59
|
+
else
|
60
|
+
raise 'Miniserver is not running.'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
require 'miniserver/connection'
|
3
|
+
require 'miniserver/logger'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module Miniserver
|
7
|
+
class Server
|
8
|
+
attr_reader :app, :options, :script
|
9
|
+
|
10
|
+
def initialize(app, options={})
|
11
|
+
@app = app
|
12
|
+
@options = options
|
13
|
+
@script = $PROGRAM_NAME
|
14
|
+
|
15
|
+
FileUtils.mkdir_p(File.dirname(options[:log]))
|
16
|
+
Miniserver.logger = Logger.new(options[:log])
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
Miniserver.logger.info "=> #{Time.now.asctime}"
|
21
|
+
Miniserver.logger.info "=> Starting Miniserver #{Miniserver::VERSION}"
|
22
|
+
Miniserver.logger.info "=> Listening on #{options[:host]}:#{options[:port]}"
|
23
|
+
|
24
|
+
if options[:daemonize]
|
25
|
+
daemonize_app
|
26
|
+
else
|
27
|
+
puts "=> Miniserver #{Miniserver::VERSION}"
|
28
|
+
puts "=> Listening on #{options[:host]}:#{options[:port]}"
|
29
|
+
puts "=> CTRL+C to stop"
|
30
|
+
end
|
31
|
+
|
32
|
+
trap(:INT) { stop }
|
33
|
+
trap(:TERM) { stop }
|
34
|
+
trap(:HUP) { restart }
|
35
|
+
|
36
|
+
EM.run do
|
37
|
+
EM.start_server(options[:host], options[:port], Connection) do |connection|
|
38
|
+
connection.app = app
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def daemonize_app
|
44
|
+
Process.daemon(true)
|
45
|
+
|
46
|
+
$0 = "Miniserver #{Miniserver::VERSION} (#{options[:host]}:#{options[:port]})"
|
47
|
+
|
48
|
+
FileUtils.mkdir_p(File.dirname(options[:pid]))
|
49
|
+
|
50
|
+
File.open(options[:pid], "w") do |f|
|
51
|
+
f.puts(Process.pid)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def stop
|
56
|
+
EM.stop
|
57
|
+
end
|
58
|
+
|
59
|
+
def restart
|
60
|
+
stop
|
61
|
+
|
62
|
+
Command.restart(script, options)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'rack/handler'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module Handler
|
6
|
+
class Miniserver
|
7
|
+
RACK_ERRORS = "rack.errors"
|
8
|
+
RACK_INPUT = "rack.input"
|
9
|
+
RACK_VERSION = 'rack.version'
|
10
|
+
RACK_MULTITHREAD = 'rack.multithread'
|
11
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'
|
12
|
+
RACK_RUN_ONCE = 'rack.run_once'
|
13
|
+
RACK_URL_SCHEME = 'rack.url_scheme'
|
14
|
+
|
15
|
+
DEFAULT_OPTIONS = {
|
16
|
+
daemonize: false,
|
17
|
+
Host: '0.0.0.0',
|
18
|
+
Port: 8080,
|
19
|
+
rackup: ::File.expand_path('config.ru'),
|
20
|
+
log: ::File.expand_path('log/miniserver.log'),
|
21
|
+
environment: 'development'
|
22
|
+
}
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def run(app, options={})
|
26
|
+
options = DEFAULT_OPTIONS.merge(options)
|
27
|
+
|
28
|
+
options[:host] = options.delete(:Host)
|
29
|
+
options[:port] = options.delete(:Port)
|
30
|
+
options[:daemonize] = false
|
31
|
+
|
32
|
+
server = ::Miniserver::Server.new(app, options)
|
33
|
+
|
34
|
+
yield(server) if block_given?
|
35
|
+
|
36
|
+
server.start
|
37
|
+
end
|
38
|
+
|
39
|
+
def valid_options
|
40
|
+
{
|
41
|
+
"Host=HOST" => "Hostname to listen on (default: 0.0.0.0)",
|
42
|
+
"Port=PORT" => "Port to listen on (default: 8080)",
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize(app)
|
48
|
+
@app = app
|
49
|
+
end
|
50
|
+
|
51
|
+
def process(request, response)
|
52
|
+
env = Hash.new
|
53
|
+
|
54
|
+
request.headers.each_pair do |name, value|
|
55
|
+
name = "#{::Miniserver::HTTP_PREFIX}#{name.upcase.tr(::Miniserver::MINUS, ::Miniserver::UNDERLINE)}"
|
56
|
+
env[name] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
%w(CONTENT_TYPE CONTENT_LENGTH).each do |name|
|
60
|
+
env[name] = env.delete("HTTP_#{name}") if env["HTTP_#{name}"]
|
61
|
+
end
|
62
|
+
|
63
|
+
uri = URI(request.request_url)
|
64
|
+
|
65
|
+
env[::Miniserver::REQUEST_METHOD] = request.http_method
|
66
|
+
env[::Miniserver::REMOTE_ADDR] = request.remote_addr
|
67
|
+
env[::Miniserver::SERVER_NAME] = request.server_name
|
68
|
+
env[::Miniserver::SERVER_PORT] = request.server_port
|
69
|
+
env[::Miniserver::QUERY_STRING] = request.query_string
|
70
|
+
env[::Miniserver::REQUEST_PATH] = uri.path
|
71
|
+
env[::Miniserver::PATH_INFO] = uri.path
|
72
|
+
env[::Miniserver::SCRIPT_NAME] = ::Miniserver::EMPTY_STRING
|
73
|
+
env[::Miniserver::FRAGMENT] = uri.fragment || ::Miniserver::EMPTY_STRING
|
74
|
+
|
75
|
+
env[RACK_ERRORS] = STDERR
|
76
|
+
env[RACK_INPUT] = StringIO.new(request.body)
|
77
|
+
env[RACK_VERSION] = Rack::VERSION
|
78
|
+
env[RACK_MULTITHREAD] = false
|
79
|
+
env[RACK_MULTIPROCESS] = false
|
80
|
+
env[RACK_RUN_ONCE] = false
|
81
|
+
env[RACK_URL_SCHEME] = ::Miniserver::HTTP
|
82
|
+
|
83
|
+
begin
|
84
|
+
response.parse(*@app.call(env))
|
85
|
+
ensure
|
86
|
+
request.body.clear
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
register :miniserver, Miniserver
|
92
|
+
end
|
93
|
+
end
|
data/miniserver.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'miniserver/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "miniserver"
|
8
|
+
spec.version = Miniserver::VERSION
|
9
|
+
spec.authors = ["zhouguangming"]
|
10
|
+
spec.email = ["zhouguangming1989@gmail.com"]
|
11
|
+
spec.description = %q{Miniserver is a simple fast HTTP 1.1 server for Ruby/Rack applications.}
|
12
|
+
spec.summary = %q{Miniserver is a simple fast HTTP 1.1 server for Ruby/Rack applications.}
|
13
|
+
spec.homepage = "https://github.com/zhouguangming/miniserver"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_dependency 'http_parser.rb'
|
24
|
+
spec.add_dependency 'eventmachine'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: miniserver
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- zhouguangming
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: http_parser.rb
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: eventmachine
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Miniserver is a simple fast HTTP 1.1 server for Ruby/Rack applications.
|
70
|
+
email:
|
71
|
+
- zhouguangming1989@gmail.com
|
72
|
+
executables:
|
73
|
+
- miniserver
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bin/miniserver
|
83
|
+
- lib/miniserver.rb
|
84
|
+
- lib/miniserver/command.rb
|
85
|
+
- lib/miniserver/connection.rb
|
86
|
+
- lib/miniserver/const.rb
|
87
|
+
- lib/miniserver/logger.rb
|
88
|
+
- lib/miniserver/request.rb
|
89
|
+
- lib/miniserver/response.rb
|
90
|
+
- lib/miniserver/runner.rb
|
91
|
+
- lib/miniserver/server.rb
|
92
|
+
- lib/miniserver/version.rb
|
93
|
+
- lib/rack/handler/miniserver.rb
|
94
|
+
- miniserver.gemspec
|
95
|
+
homepage: https://github.com/zhouguangming/miniserver
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.0.3
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: Miniserver is a simple fast HTTP 1.1 server for Ruby/Rack applications.
|
119
|
+
test_files: []
|