emmy-http-server 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 640f4d91ac5b7afc7d9c187c66a632698954576a
4
+ data.tar.gz: c96485fca03f0ad17ce33023b8a3d0e950aa7a8d
5
+ SHA512:
6
+ metadata.gz: a4ed46332b0258af15ef204ad1ed57ea872763734944e1414aee2da43e04943d91f49530383bdb784d9a5f84e1589f8e7100f92023d1f37e5c36ab537508985c
7
+ data.tar.gz: 61f7f595899b0a7f53b5660e67889316453885cbef5a9638bd149fecb78fea09a42e720584070614dd284f45a5f3b8332a92924ad6b86cb841825724c30350d4
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ /examples/Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in emmy-http-server.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # EmmyHttp::Server
2
+
3
+
4
+ ## Usage
5
+
6
+ ```ruby
7
+ server do
8
+ connect do |req, res|
9
+ response.success()
10
+ end
11
+ end
12
+
13
+ bind *server
14
+ ```
15
+
16
+ ### Routing
17
+
18
+ ```ruby
19
+ server do
20
+ get '/' do
21
+ #...
22
+ end
23
+ end
24
+
25
+ bind *server
26
+ ```
27
+
28
+ ### Rack support
29
+
30
+ ```ruby
31
+ app do
32
+ use Rack::Logger
33
+
34
+ #run Sinatra::Application
35
+ get '/' do
36
+ #...
37
+ end
38
+ end
39
+
40
+ bind *app
41
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'emmy_http/server/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "emmy-http-server"
8
+ spec.version = EmmyHttp::Server::VERSION
9
+ spec.authors = ["inre"]
10
+ spec.email = ["inre.storm@gmail.com"]
11
+
12
+ spec.summary = %q{Emmy HTTP Server}
13
+ spec.description = %q{Emmy HTTP Server}
14
+ #spec.homepage = "TODO: Put your gem's website or public repo URL here."
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "http_parser.rb", "~> 0.6.0"
22
+ spec.add_dependency "addressable", "~> 2.3.8"
23
+ spec.add_dependency "event_object", "~> 0.9"
24
+ spec.add_dependency "fibre", "~> 0.9"
25
+ spec.add_dependency "emmy-machine", "~> 0.1"
26
+ spec.add_dependency "emmy-http", "~> 0.2"
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.9"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3"
31
+ end
data/examples/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+
2
+ gem 'sinatra'
3
+ gem 'emmy'
4
+ gem 'emmy-http-server', path: '../'
5
+ gem 'emmy-http'
data/examples/app.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'sinatra'
2
+
3
+ get '/' do
4
+ 'Hello world!'
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'bundler/setup'
2
+ require 'emmy'
3
+ require 'emmy_http/server'
4
+ require './app'
5
+ using EventObject
6
+
7
+ Emmy.run do
8
+ app = EmmyHttp::Application.new do
9
+ run Sinatra::Application
10
+ end
11
+
12
+ server = EmmyHttp::Server::Server.new(Emmy::Runner.instance.config, app)
13
+
14
+ Emmy.bind(*server)
15
+ puts "Bind server on #{server.config.url}"
16
+
17
+ server.on :error do |err|
18
+ puts err.message
19
+ puts err.backtrace.join("\n")
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ require 'http/parser'
2
+
3
+ module EmmyHttp
4
+ class Server::Parser
5
+ using EventObject
6
+
7
+ events :head, :body, :complete
8
+
9
+ attr_accessor :http_parser
10
+ attr_accessor :stop
11
+ attr_accessor :no_body
12
+
13
+ def initialize
14
+ @http_parser = HTTP::Parser.new
15
+ @http_parser.header_value_type = :mixed
16
+ @http_parser.on_headers_complete = proc do
17
+ head!(http_parser.headers, http_parser)
18
+ stop ? :stop : (no_body ? :reset : nil)
19
+ end
20
+ @http_parser.on_body = proc do |chunk|
21
+ body!(chunk)
22
+ end
23
+ @http_parser.on_message_complete = proc do
24
+ complete!
25
+ end
26
+ end
27
+
28
+ def <<(data)
29
+ @http_parser << data
30
+ rescue HTTP::Parser::Error => e
31
+ raise EmmyHttp::ParserError, e.to_s
32
+ end
33
+
34
+ def status_code
35
+ @http_parser.status_code
36
+ end
37
+
38
+ def http_method
39
+ @http_parser.http_method
40
+ end
41
+
42
+ def headers
43
+ @http_parser.headers
44
+ end
45
+
46
+ def http_version
47
+ @http_parser.http_version
48
+ end
49
+
50
+ def request_url
51
+ @http_parser.request_url
52
+ end
53
+
54
+ def reset!
55
+ @http_parser.reset!
56
+ end
57
+
58
+ #<<<
59
+ end
60
+ end
@@ -0,0 +1,153 @@
1
+ module EmmyHttp
2
+ class Server::Request
3
+ include ModelPack::Document
4
+ using EventObject
5
+
6
+ attribute :http_version
7
+ attribute :http_method
8
+ attribute :url,
9
+ writer: -> (v) { Addressable::URI.parse(v) },
10
+ serialize: -> (v) { v.to_s }
11
+ dictionary :headers
12
+ attribute :body
13
+ attribute :decoder
14
+ attribute :env
15
+ #attribute :upgrade_data
16
+
17
+ attr_reader :connection
18
+ attr_reader :parser
19
+ attr_reader :request
20
+ attr_reader :remote_address
21
+
22
+ events :data, :complete, :terminate
23
+
24
+ def initialize
25
+ @parser = Server::Parser.new
26
+ @body = StringIO.new(Server::INITIAL_BODY.dup)
27
+ request = self
28
+
29
+ on :data do |chunk|
30
+ @body << chunk
31
+ end
32
+
33
+ parser.on :head do |headers|
34
+ request.http_version = parser.http_version
35
+ request.http_method = parser.http_method
36
+ request.url = parser.request_url
37
+ request.headers = parser.headers
38
+ end
39
+
40
+ parser.on :body do |chunk|
41
+ request.data!(request.decoder ? (request.decoder.decompress(chunk) || '') : chunk)
42
+ end
43
+
44
+ parser.on :complete do
45
+ conn = request.connection
46
+ dettach
47
+ complete!(request, conn)
48
+ end
49
+ end
50
+
51
+ def post_data(chunk)
52
+ #p chunk
53
+ parser << chunk
54
+
55
+ #if parser.upgrade?
56
+ #end
57
+ rescue EmmyHttp::ParserError => e
58
+ close(e.message)
59
+ end
60
+
61
+ def keep_alive?
62
+ #return false if terminate_connection?
63
+ case http_version
64
+ when [1,0] then
65
+ (headers['Connection'].downcase == 'keep-alive') rescue false
66
+ when [1,1] then
67
+ (headers['Connection'].downcase != 'close') rescue true
68
+ when [2,0] then
69
+ false
70
+ else
71
+ false
72
+ end
73
+ end
74
+
75
+ def prepare_env
76
+ @env = {}
77
+
78
+ # Server info
79
+
80
+ @env['rack.version'] = Server::RACK_VERSION_NUM
81
+ @env['rack.errors'] = STDERR
82
+ @env['rack.multithread'] = false
83
+ @env['rack.multiprocess'] = false
84
+ @env['rack.run_once'] = false
85
+ #env['rack.logger'] = logger
86
+
87
+ # HTTP headers
88
+
89
+ headers.each do |n, v|
90
+ @env['HTTP_' + n.gsub('-','_').upcase] = v
91
+ end
92
+
93
+ %w(CONTENT_TYPE CONTENT_LENGTH).each do |n|
94
+ @env[n] = @env.delete("HTTP_#{n}") if @env["HTTP_#{n}"]
95
+ end
96
+
97
+ @env['SERVER_NAME'] = 'localhost'
98
+ @env['SERVER_SOFTWARE'] = Server::SERVER_NAME
99
+
100
+ if @env['HTTP_HOST'] # FIXME
101
+ name, port = @env['HTTP_HOST'].split(':')
102
+ @env['SERVER_NAME'] = name if name
103
+ @env['SERVER_PORT'] = port if port
104
+ end
105
+
106
+ uri = URI(parser.request_url)
107
+
108
+ @env['HTTP_VERSION'] = "HTTP/#{http_version.join('.')}"
109
+ @env['REMOTE_ADDR'] = remote_address
110
+ @env['REQUEST_METHOD'] = http_method
111
+ @env['REQUEST_URI'] = url.to_s
112
+ @env['QUERY_STRING'] = url.query# || ""
113
+ @env['SCRIPT_NAME'] = ""
114
+ @env['REQUEST_PATH'] = url.path
115
+ @env['PATH_INFO'] = url.path
116
+ @env['FRAGMENT'] = url.fragment
117
+
118
+ # HTTP body
119
+
120
+ @env['rack.input'] = body
121
+ end
122
+
123
+ def close(reason=nil)
124
+ terminate!(reason, self, connection) if reason
125
+ dettach
126
+ end
127
+
128
+ def attach(conn)
129
+ @connection = conn
130
+ @remote_address = socket_address
131
+ listen conn, :data, :post_data
132
+ listen conn, :close, :close
133
+ end
134
+
135
+ def dettach
136
+ if connection
137
+ stop_listen connection, :data
138
+ stop_listen connection, :close
139
+ @connection = nil
140
+ end
141
+ end
142
+
143
+ protected
144
+
145
+ def socket_address
146
+ Socket.unpack_sockaddr_in(connection.get_peername)[1]
147
+ rescue Exception
148
+ nil
149
+ end
150
+
151
+ #<<<
152
+ end
153
+ end
@@ -0,0 +1,84 @@
1
+ module EmmyHttp
2
+ class Server::Response
3
+ include ModelPack::Document
4
+ using EventObject
5
+
6
+ attribute :status, writer: -> (v) { v ? v.to_i : v }
7
+ dictionary :headers
8
+ attribute :body
9
+
10
+ events :terminate
11
+
12
+ attr_accessor :keep_alive
13
+ attr_accessor :connection
14
+ attr_writer :headers_sent
15
+ attr_writer :finished
16
+
17
+ def write_all
18
+ write_head unless headers_sent?
19
+ write_body unless finished?
20
+ end
21
+
22
+ def write_head
23
+ raise ResponseError, "Invalid HTTP status" unless Server::HTTP_STATUS_CODES.key?(status)
24
+
25
+ prepare_headers
26
+ head = "HTTP/1.1 #{status} #{Server::HTTP_STATUS_CODES[status.to_i]}\r\n"
27
+
28
+ headers.each do |n, v|
29
+ head += "#{n}: #{v}\r\n" if v
30
+ end
31
+
32
+ head += "\r\n"
33
+
34
+ connection.send_data(head)
35
+ @headers_sent = true
36
+ end
37
+
38
+ def write_body
39
+ if body.respond_to?(:each)
40
+ body.each { |chunk| connection.send_data(chunk) }
41
+ else
42
+ connection.send_data(body) if body
43
+ end
44
+ @finished = true
45
+ end
46
+
47
+ def close(reason=nil)
48
+ body.close if body.respond_to?(:close)
49
+ terminate!(reason, self, connection) if reason
50
+ dettach
51
+ end
52
+
53
+ def attach(conn)
54
+ @connection = conn
55
+ listen conn, :close, :close
56
+ end
57
+
58
+ def dettach
59
+ if connection
60
+ stop_listen connection, :close
61
+ @connection = nil
62
+ end
63
+ end
64
+
65
+ def headers_sent?
66
+ @headers_sent
67
+ end
68
+
69
+ def finished?
70
+ @finished
71
+ end
72
+
73
+ protected
74
+
75
+ def prepare_headers
76
+ headers['Server'] ||= Server::SERVER_NAME
77
+ headers['Date'] ||= Time.now.httpdate
78
+ headers['Connection'] = keep_alive ? 'keep-alive' : 'close'
79
+ #headers['Content-Length'] ||= "#{body.bytesize}" if body
80
+ end
81
+
82
+ #<<<
83
+ end
84
+ end
@@ -0,0 +1,112 @@
1
+ require "emmy_http/server/version"
2
+
3
+ module EmmyHttp
4
+ class Server::Server
5
+ using EventObject
6
+
7
+ attr_accessor :config
8
+ attr_accessor :app
9
+ attr_accessor :connections
10
+ attr_accessor :parser
11
+
12
+ events :connect, :request, :error
13
+
14
+ def initialize(config, app=nil)
15
+ @config = config
16
+ @app = app
17
+ @connections = []
18
+
19
+ if app
20
+ on :request do |req, res|
21
+ req.prepare_env
22
+ res.status, res.headers, res.body = app.call(req.env)
23
+ end
24
+ else
25
+ on :request do |req, res|
26
+ error!('No app found', req, res)
27
+ end
28
+ end
29
+
30
+ on :error do |reason, req, res|
31
+ if res.headers_sent?
32
+ # big problems
33
+ else
34
+ body = reason ? reason.to_s : 'An error happened'
35
+ res.update_attributes(
36
+ status: 500,
37
+ headers: {
38
+ 'Content-Type' => 'text/plain',
39
+ 'Content-Length' => body.bytesize
40
+ },
41
+ body: body
42
+ )
43
+ end
44
+ end
45
+ end
46
+
47
+ # initialize a new connection
48
+ def initialize_connection(conn)
49
+ conn.comm_inactivity_timeout = config.timeout.to_i
50
+ conn.start_tls(config.ssl ? config.ssl.serializable_hash : {}) if ssl?
51
+ # Create a new request
52
+ req = new_request(conn)
53
+ # Connected event
54
+ connect!(req, conn)
55
+ end
56
+
57
+ def new_request(conn)
58
+ req = Server::Request.new(
59
+ #decoder: config.decoding ? new_decoder_by_encoding(headers['Content-Encoding']) : nil
60
+ )
61
+
62
+ req.on :complete do |req, conn|
63
+ res = Server::Response.new
64
+ res.keep_alive = req.keep_alive?
65
+ res.attach(conn)
66
+
67
+ EmmyMachine.fiber_block do
68
+ # Process request event
69
+ begin
70
+ request!(req, res)
71
+ rescue StandardError => err
72
+ error!(err, req, res)
73
+ end
74
+
75
+ # Send response
76
+ res.write_all
77
+ # Close/Dettach response
78
+ res.close
79
+ # Disconnect after response
80
+ if res.keep_alive
81
+ new_request(conn)
82
+ else
83
+ conn.close_connection_after_writing
84
+ end
85
+ end
86
+ end
87
+
88
+ req.on :error do |reason, req, conn|
89
+ error!(reason)
90
+ conn.close_connection_after_writing rescue nil
91
+ end
92
+
93
+ req.attach(conn)
94
+ req
95
+ end
96
+
97
+ def ssl?
98
+ config.ssl || config.url.scheme == 'https' || config.url.port == 443
99
+ end
100
+
101
+ #def stop(reason=nil)
102
+ # @stop_reason ||= reason
103
+ # connection.close_connection if connection
104
+ #end
105
+
106
+ def to_a
107
+ [config.url, EmmyMachine::Connection, method(:initialize_connection)]
108
+ end
109
+
110
+ #<<<
111
+ end
112
+ end
@@ -0,0 +1,5 @@
1
+ module EmmyHttp
2
+ module Server
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,55 @@
1
+ require "emmy_http/server/version"
2
+ require "emmy_http/server/parser"
3
+ require "emmy_http/server/request"
4
+ require "emmy_http/server/response"
5
+ require "emmy_http/server/server"
6
+
7
+ module EmmyHttp
8
+ module Server
9
+ INITIAL_BODY = ''
10
+ INITIAL_BODY.encode!(Encoding::ASCII_8BIT) if INITIAL_BODY.respond_to?(:encode)
11
+
12
+ SERVER_NAME = 'Emmy'.freeze
13
+ RACK_VERSION_NUM = [1, 0]
14
+
15
+ HTTP_STATUS_CODES = {
16
+ 100 => 'Continue',
17
+ 101 => 'Switching Protocols',
18
+ 200 => 'OK',
19
+ 201 => 'Created',
20
+ 202 => 'Accepted',
21
+ 203 => 'Non-Authoritative Information',
22
+ 204 => 'No Content',
23
+ 205 => 'Reset Content',
24
+ 206 => 'Partial Content',
25
+ 300 => 'Multiple Choices',
26
+ 301 => 'Moved Permanently',
27
+ 302 => 'Moved Temporarily',
28
+ 303 => 'See Other',
29
+ 304 => 'Not Modified',
30
+ 305 => 'Use Proxy',
31
+ 400 => 'Bad Request',
32
+ 401 => 'Unauthorized',
33
+ 402 => 'Payment Required',
34
+ 403 => 'Forbidden',
35
+ 404 => 'Not Found',
36
+ 405 => 'Method Not Allowed',
37
+ 406 => 'Not Acceptable',
38
+ 407 => 'Proxy Authentication Required',
39
+ 408 => 'Request Time-out',
40
+ 409 => 'Conflict',
41
+ 410 => 'Gone',
42
+ 411 => 'Length Required',
43
+ 412 => 'Precondition Failed',
44
+ 413 => 'Request Entity Too Large',
45
+ 414 => 'Request-URI Too Large',
46
+ 415 => 'Unsupported Media Type',
47
+ 500 => 'Internal Server Error',
48
+ 501 => 'Not Implemented',
49
+ 502 => 'Bad Gateway',
50
+ 503 => 'Service Unavailable',
51
+ 504 => 'Gateway Time-out',
52
+ 505 => 'HTTP Version not supported'
53
+ }
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: emmy-http-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - inre
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http_parser.rb
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.8
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.8
41
+ - !ruby/object:Gem::Dependency
42
+ name: event_object
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: fibre
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: emmy-machine
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.1'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: emmy-http
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.2'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: bundler
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.9'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rake
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '10.0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '10.0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3'
139
+ description: Emmy HTTP Server
140
+ email:
141
+ - inre.storm@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rspec"
148
+ - ".travis.yml"
149
+ - Gemfile
150
+ - README.md
151
+ - Rakefile
152
+ - emmy-http-server.gemspec
153
+ - examples/Gemfile
154
+ - examples/app.rb
155
+ - examples/simple.rb
156
+ - lib/emmy_http/server.rb
157
+ - lib/emmy_http/server/parser.rb
158
+ - lib/emmy_http/server/request.rb
159
+ - lib/emmy_http/server/response.rb
160
+ - lib/emmy_http/server/server.rb
161
+ - lib/emmy_http/server/version.rb
162
+ homepage:
163
+ licenses: []
164
+ metadata: {}
165
+ post_install_message:
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubyforge_project:
181
+ rubygems_version: 2.4.6
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: Emmy HTTP Server
185
+ test_files: []