emmy-http-server 0.1.0

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.
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: []