net-http-server 0.1.0 → 0.2.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.
- data/.gitignore +3 -0
- data/ChangeLog.md +10 -0
- data/README.md +4 -2
- data/Rakefile +4 -4
- data/benchmarks/parser.rb +31 -0
- data/benchmarks/server.rb +13 -0
- data/gemspec.yml +2 -2
- data/lib/net/http/server/chunked_stream.rb +116 -0
- data/lib/net/http/server/daemon.rb +14 -6
- data/lib/net/http/server/requests.rb +23 -19
- data/lib/net/http/server/responses.rb +32 -1
- data/lib/net/http/server/stream.rb +123 -0
- data/lib/net/http/server/version.rb +1 -1
- data/lib/rack/handler/http.rb +12 -11
- data/net-http-server.gemspec +128 -7
- data/spec/net/http/server/chunked_stream_spec.rb +87 -0
- data/spec/{server → net/http/server}/daemon_spec.rb +0 -0
- data/spec/{server → net/http/server}/parser_spec.rb +0 -0
- data/spec/{server → net/http/server}/requests_spec.rb +0 -0
- data/spec/{server → net/http/server}/responses_spec.rb +0 -0
- data/spec/{server → net/http/server}/server_spec.rb +0 -0
- data/spec/net/http/server/stream_spec.rb +66 -0
- data/spec/rack/handler/http_spec.rb +3 -4
- data/spec/spec_helper.rb +1 -1
- metadata +70 -94
data/.gitignore
ADDED
data/ChangeLog.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 0.2.0 / 2011-08-23
|
2
|
+
|
3
|
+
* Added support for handling Streams and Chunked Transfer-Encoding:
|
4
|
+
* Added {Net::HTTP::Server::Stream}.
|
5
|
+
* Added {Net::HTTP::Server::ChunkedStream}.
|
6
|
+
* Added {Net::HTTP::Server::Responses#write_body_streamed}.
|
7
|
+
* Use `Rack::RewindableInput` with {Net::HTTP::Server::Stream}.
|
8
|
+
* Fixed a bug where Parslet Strings were being returned in the Headers.
|
9
|
+
* Catch all IOErrors in {Net::HTTP::Server::Requests#read_request}.
|
10
|
+
|
1
11
|
### 0.1.0 / 2011-01-26
|
2
12
|
|
3
13
|
* Initial release:
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
* [Homepage](http://github.com/postmodern/net-http-server)
|
4
4
|
* [Issues](http://github.com/postmodern/net-http-server/issues)
|
5
5
|
* [Documentation](http://rubydoc.info/gems/net-http-server)
|
6
|
-
*
|
6
|
+
* [Email](mailto:postmodern.mod3 at gmail.com)
|
7
7
|
|
8
8
|
## Description
|
9
9
|
|
@@ -12,6 +12,8 @@
|
|
12
12
|
## Features
|
13
13
|
|
14
14
|
* Pure Ruby.
|
15
|
+
* Supports Streamed Request/Response Bodies.
|
16
|
+
* Supports Chunked Transfer-Encoding.
|
15
17
|
* Provides a [Rack](http://rack.rubyforge.org/) Handler.
|
16
18
|
|
17
19
|
## Examples
|
@@ -21,7 +23,7 @@ Simple HTTP Server:
|
|
21
23
|
require 'net/http/server'
|
22
24
|
require 'pp'
|
23
25
|
|
24
|
-
Net::HTTP::Server.run(:port => 8080) do |request,
|
26
|
+
Net::HTTP::Server.run(:port => 8080) do |request,stream|
|
25
27
|
pp request
|
26
28
|
|
27
29
|
[200, {'Content-Type' => 'text/html'}, ['Hello World']]
|
data/Rakefile
CHANGED
@@ -2,17 +2,17 @@ require 'rubygems'
|
|
2
2
|
require 'rake'
|
3
3
|
|
4
4
|
begin
|
5
|
-
gem 'ore-tasks', '~> 0.
|
5
|
+
gem 'ore-tasks', '~> 0.4'
|
6
6
|
require 'ore/tasks'
|
7
7
|
|
8
8
|
Ore::Tasks.new
|
9
9
|
rescue LoadError => e
|
10
|
-
|
11
|
-
|
10
|
+
warn e.message
|
11
|
+
warn "Run `gem install ore-tasks` to install 'ore/tasks'."
|
12
12
|
end
|
13
13
|
|
14
14
|
begin
|
15
|
-
gem 'rspec', '~> 2.4
|
15
|
+
gem 'rspec', '~> 2.4'
|
16
16
|
require 'rspec/core/rake_task'
|
17
17
|
|
18
18
|
RSpec::Core::RakeTask.new
|
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
|
6
|
+
require 'net/http/server/parser'
|
7
|
+
|
8
|
+
HTTP_REQUEST = [
|
9
|
+
'GET /search?q=test&hl=en&fp=1&cad=b&tch=1&ech=1&psi=DBQ4Te_qCI2Y_QaIuPSTCA12955207804903 HTTP/1.1',
|
10
|
+
'Host: www.google.com',
|
11
|
+
'Referer: http://www.google.com/',
|
12
|
+
'Accept: */*',
|
13
|
+
'User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10',
|
14
|
+
'Accept-Encoding: gzip,deflate,sdch',
|
15
|
+
'Avail-Dictionary: GeNLY2f-',
|
16
|
+
'Accept-Language: en-US,en;q=0.8',
|
17
|
+
'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3',
|
18
|
+
'Cookie: NID=43=bgvZmm1C00aC41wQA0Yl5lVPEJZerwnK9LYDFo4Ph9_qBZFfbwT-auI64LZzdquh8StFriEuQfhrIgf_GlVd9erjOGppXZISHpoFgdiUUfpTqUbKC8gbfNh09eZXmcK7; PREF=ID=c28d27fb5ff1280b:U=fedcd44ca2fdef4f:FF=0:LD=en:CR=2:TM=1295517030:LM=1295517030:S=D36Ccqf-FQ78ZWE7',
|
19
|
+
'',
|
20
|
+
''
|
21
|
+
].join("\r\n")
|
22
|
+
|
23
|
+
require 'benchmark'
|
24
|
+
|
25
|
+
Benchmark.bm do |bench|
|
26
|
+
parser = Net::HTTP::Server::Parser.new
|
27
|
+
|
28
|
+
(1..10).each do |i|
|
29
|
+
bench.report("parse ##{i}: ") { parser.parse(HTTP_REQUEST) }
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__),'..','lib'))
|
6
|
+
require 'net/http/server'
|
7
|
+
|
8
|
+
puts ">>> Starting the HTTP Server on port 8080 ..."
|
9
|
+
puts ">>> Prepare to run: ab -n 1000 http://localhost:8080/"
|
10
|
+
|
11
|
+
Net::HTTP::Server.run(:port => 8080) do |request,socket|
|
12
|
+
[200, {'Content-Type' => 'text/html'}, ['Hello World']]
|
13
|
+
end
|
data/gemspec.yml
CHANGED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'net/http/server/stream'
|
2
|
+
|
3
|
+
require 'net/protocol'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module Net
|
7
|
+
class HTTP < Protocol
|
8
|
+
module Server
|
9
|
+
#
|
10
|
+
# Handles reading and writing to Chunked Transfer-Encoded streams.
|
11
|
+
#
|
12
|
+
# @since 0.2.0
|
13
|
+
#
|
14
|
+
class ChunkedStream < Stream
|
15
|
+
|
16
|
+
#
|
17
|
+
# Initializes the chuked stream.
|
18
|
+
#
|
19
|
+
# @param [#read, #write, #flush] socket
|
20
|
+
# The socket to read from and write to.
|
21
|
+
#
|
22
|
+
def initialize(socket)
|
23
|
+
super(socket)
|
24
|
+
|
25
|
+
@buffer = ''
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Reads a chunk from the stream.
|
30
|
+
#
|
31
|
+
# @param [Integer] length
|
32
|
+
#
|
33
|
+
# @param [#<<] buffer
|
34
|
+
# The optional buffer to append the data to.
|
35
|
+
#
|
36
|
+
# @return [String, nil]
|
37
|
+
# A chunk from the stream.
|
38
|
+
#
|
39
|
+
# @raise [ArgumentError]
|
40
|
+
# The buffer did not respond to `#<<`.
|
41
|
+
#
|
42
|
+
# @since 0.2.0
|
43
|
+
#
|
44
|
+
def read(length=4096,buffer='')
|
45
|
+
unless buffer.respond_to?(:<<)
|
46
|
+
raise(ArgumentError,"buffer must respond to #<<")
|
47
|
+
end
|
48
|
+
|
49
|
+
until @buffer.length >= length
|
50
|
+
length_line = @socket.readline("\r\n").chomp
|
51
|
+
chunk_length, chunk_extension = length_line.split(';',2)
|
52
|
+
chunk_length = chunk_length.to_i(16)
|
53
|
+
|
54
|
+
# read the chunk
|
55
|
+
@buffer << @socket.read(chunk_length)
|
56
|
+
|
57
|
+
# chomp the terminating CRLF
|
58
|
+
@socket.read(2)
|
59
|
+
|
60
|
+
# end-of-stream
|
61
|
+
break if chunk_length == 0
|
62
|
+
end
|
63
|
+
|
64
|
+
# clear the buffer before appending
|
65
|
+
buffer.clear
|
66
|
+
|
67
|
+
unless @buffer.empty?
|
68
|
+
# empty a slice of the buffer
|
69
|
+
buffer << @buffer.slice!(0,length)
|
70
|
+
return buffer
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Writes data to the chunked stream.
|
76
|
+
#
|
77
|
+
# @param [String] data
|
78
|
+
# The data to write to the stream.
|
79
|
+
#
|
80
|
+
# @return [Integer]
|
81
|
+
# The length of the data written.
|
82
|
+
#
|
83
|
+
# @since 0.2.0
|
84
|
+
#
|
85
|
+
def write(data)
|
86
|
+
length = data.length
|
87
|
+
|
88
|
+
# do not write empty chunks
|
89
|
+
unless length == 0
|
90
|
+
# write the chunk length
|
91
|
+
@socket.write("%X\r\n" % length)
|
92
|
+
|
93
|
+
# write the data
|
94
|
+
@socket.write(data)
|
95
|
+
@socket.write("\r\n")
|
96
|
+
@socket.flush
|
97
|
+
end
|
98
|
+
|
99
|
+
return length
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Closes the chunked stream.
|
104
|
+
#
|
105
|
+
# @since 0.2.0
|
106
|
+
#
|
107
|
+
def close
|
108
|
+
# last chunk
|
109
|
+
@socket.write("0\r\n\r\n")
|
110
|
+
@socket.flush
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require 'net/http/server/parser'
|
2
2
|
require 'net/http/server/requests'
|
3
3
|
require 'net/http/server/responses'
|
4
|
+
require 'net/http/server/stream'
|
5
|
+
require 'net/http/server/chunked_stream'
|
4
6
|
|
5
7
|
require 'net/protocol'
|
6
8
|
require 'gserver'
|
@@ -36,7 +38,7 @@ module Net
|
|
36
38
|
# @option options [Integer] :max_connections (MAX_CONNECTIONS)
|
37
39
|
# The maximum number of simultaneous connections.
|
38
40
|
#
|
39
|
-
# @option options [IO] :log (
|
41
|
+
# @option options [IO] :log ($stderr)
|
40
42
|
# The log to write errors to.
|
41
43
|
#
|
42
44
|
# @option options [#call] :handler
|
@@ -55,7 +57,7 @@ module Net
|
|
55
57
|
host = options.fetch(:host,DEFAULT_HOST)
|
56
58
|
port = options.fetch(:port,DEFAULT_PORT).to_i
|
57
59
|
max_connections = options.fetch(:max_connections,MAX_CONNECTIONS)
|
58
|
-
log = options.fetch(:log
|
60
|
+
log = options.fetch(:log,$stderr)
|
59
61
|
|
60
62
|
super(port,host,max_connections,log,false,true)
|
61
63
|
|
@@ -68,14 +70,14 @@ module Net
|
|
68
70
|
# @param [#call, nil] object
|
69
71
|
# The HTTP Request Handler object.
|
70
72
|
#
|
71
|
-
# @yield [request,
|
73
|
+
# @yield [request, stream]
|
72
74
|
# If a block is given, it will be used to process HTTP Requests.
|
73
75
|
#
|
74
76
|
# @yieldparam [Hash{Symbol => String,Array,Hash}] request
|
75
77
|
# The HTTP Request.
|
76
78
|
#
|
77
|
-
# @yieldparam [
|
78
|
-
# The
|
79
|
+
# @yieldparam [Stream, ChunkedStream] stream
|
80
|
+
# The stream of the HTTP Request body.
|
79
81
|
#
|
80
82
|
# @raise [ArgumentError]
|
81
83
|
# The HTTP Request Handler must respond to `#call`.
|
@@ -110,8 +112,14 @@ module Net
|
|
110
112
|
|
111
113
|
normalize_request(request)
|
112
114
|
|
115
|
+
stream = if request[:headers]['Transfer-Encoding'] == 'chunked'
|
116
|
+
ChunkedStream.new(socket)
|
117
|
+
else
|
118
|
+
Stream.new(socket)
|
119
|
+
end
|
120
|
+
|
113
121
|
# rack compliant
|
114
|
-
status, headers, body = @handler.call(request,
|
122
|
+
status, headers, body = @handler.call(request,stream)
|
115
123
|
|
116
124
|
write_response(socket,status,headers,body)
|
117
125
|
end
|
@@ -24,29 +24,33 @@ module Net
|
|
24
24
|
def read_request(stream)
|
25
25
|
buffer = ''
|
26
26
|
|
27
|
-
|
27
|
+
begin
|
28
|
+
request_line = stream.readline("\r\n")
|
28
29
|
|
29
|
-
|
30
|
-
|
30
|
+
# the request line must contain 'HTTP/'
|
31
|
+
return unless request_line.include?('HTTP/')
|
31
32
|
|
32
|
-
|
33
|
+
buffer << request_line
|
33
34
|
|
34
|
-
|
35
|
-
|
35
|
+
stream.each_line("\r\n") do |header|
|
36
|
+
buffer << header
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
38
|
+
# a header line must contain a ':' character followed by
|
39
|
+
# linear-white-space (either ' ' or "\t").
|
40
|
+
unless (header.include?(': ') || header.include?(":\t"))
|
41
|
+
# if this is not a header line, check if it is the end
|
42
|
+
# of the request
|
43
|
+
if header == "\r\n"
|
44
|
+
# end of the request
|
45
|
+
break
|
46
|
+
else
|
47
|
+
# invalid header line
|
48
|
+
return
|
49
|
+
end
|
48
50
|
end
|
49
51
|
end
|
52
|
+
rescue IOError, SystemCallError
|
53
|
+
return
|
50
54
|
end
|
51
55
|
|
52
56
|
return buffer
|
@@ -92,8 +96,8 @@ module Net
|
|
92
96
|
|
93
97
|
unless headers.empty?
|
94
98
|
headers.each do |header|
|
95
|
-
name = header[:name]
|
96
|
-
value = header[:value]
|
99
|
+
name = header[:name].to_s
|
100
|
+
value = header[:value].to_s
|
97
101
|
|
98
102
|
if normalized_headers.has_key?(name)
|
99
103
|
previous_value = normalized_headers[name]
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'net/http/server/chunked_stream'
|
2
|
+
|
1
3
|
require 'net/protocol'
|
2
4
|
require 'time'
|
3
5
|
|
@@ -124,6 +126,26 @@ module Net
|
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
129
|
+
#
|
130
|
+
# Writes the body of a HTTP Response to a stream, using Chunked
|
131
|
+
# Transfer-Encoding.
|
132
|
+
#
|
133
|
+
# @param [IO] stream
|
134
|
+
# The stream to write the headers back to.
|
135
|
+
#
|
136
|
+
# @param [#each] body
|
137
|
+
# The body of the HTTP Response.
|
138
|
+
#
|
139
|
+
# @since 0.2.0
|
140
|
+
#
|
141
|
+
def write_body_streamed(stream,body)
|
142
|
+
chunked_stream = ChunkedStream.new(stream)
|
143
|
+
|
144
|
+
body.each { |chunk| chunked_stream.write(chunk) }
|
145
|
+
|
146
|
+
chunked_stream.close
|
147
|
+
end
|
148
|
+
|
127
149
|
#
|
128
150
|
# Writes a HTTP Response to a stream.
|
129
151
|
#
|
@@ -142,7 +164,16 @@ module Net
|
|
142
164
|
def write_response(stream,status,headers,body)
|
143
165
|
write_status stream, status
|
144
166
|
write_headers stream, headers
|
145
|
-
|
167
|
+
|
168
|
+
if headers['Transfer-Encoding'] == 'chunked'
|
169
|
+
write_body_streamed stream, body
|
170
|
+
else
|
171
|
+
write_body stream, body
|
172
|
+
|
173
|
+
# if neither `Content-Length` or `Transfer-Encoding`
|
174
|
+
# were specified, close the stream after writing the response.
|
175
|
+
stream.close unless headers['Content-Length']
|
176
|
+
end
|
146
177
|
end
|
147
178
|
|
148
179
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'net/protocol'
|
2
|
+
|
3
|
+
module Net
|
4
|
+
class HTTP < Protocol
|
5
|
+
module Server
|
6
|
+
#
|
7
|
+
# Handles reading and writing to raw HTTP streams.
|
8
|
+
#
|
9
|
+
# @since 0.2.0
|
10
|
+
#
|
11
|
+
class Stream
|
12
|
+
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
# The raw socket of the stream.
|
16
|
+
attr_reader :socket
|
17
|
+
|
18
|
+
#
|
19
|
+
# Creates a new stream.
|
20
|
+
#
|
21
|
+
# @param [TCPSocket] socket
|
22
|
+
# The raw socket that will be read/write to.
|
23
|
+
#
|
24
|
+
# @since 0.2.0
|
25
|
+
#
|
26
|
+
def initialize(socket)
|
27
|
+
@socket = socket
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# Reads data from the stream.
|
32
|
+
#
|
33
|
+
# @param [Integer] length
|
34
|
+
# The number of bytes to read.
|
35
|
+
#
|
36
|
+
# @param [#<<] buffer
|
37
|
+
# The optional buffer to append the data to.
|
38
|
+
#
|
39
|
+
# @return [String, nil]
|
40
|
+
# A chunk from the stream.
|
41
|
+
#
|
42
|
+
# @since 0.2.0
|
43
|
+
#
|
44
|
+
def read(length=4096,buffer='')
|
45
|
+
@socket.read(length,buffer)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Reads each chunk from the stream.
|
50
|
+
#
|
51
|
+
# @yield [chunk]
|
52
|
+
# The given block will be passed each chunk.
|
53
|
+
#
|
54
|
+
# @yieldparam [String] chunk
|
55
|
+
# A chunk from the stream.
|
56
|
+
#
|
57
|
+
# @return [Enumerator]
|
58
|
+
# If no block is given, an Enumerator will be returned.
|
59
|
+
#
|
60
|
+
# @since 0.2.0
|
61
|
+
#
|
62
|
+
def each
|
63
|
+
return enum_for unless block_given?
|
64
|
+
|
65
|
+
while (chunk = read)
|
66
|
+
yield chunk
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Reads the entire body.
|
72
|
+
#
|
73
|
+
# @return [String]
|
74
|
+
# The complete body.
|
75
|
+
#
|
76
|
+
# @since 0.2.0
|
77
|
+
#
|
78
|
+
def body
|
79
|
+
buffer = ''
|
80
|
+
|
81
|
+
each { |chunk| buffer << chunk }
|
82
|
+
return buffer
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Writes data to the stream.
|
87
|
+
#
|
88
|
+
# @param [String] data
|
89
|
+
# The data to write to the stream.
|
90
|
+
#
|
91
|
+
# @return [Integer]
|
92
|
+
# The length of the data written.
|
93
|
+
#
|
94
|
+
# @since 0.2.0
|
95
|
+
#
|
96
|
+
def write(data)
|
97
|
+
result = @socket.write(data)
|
98
|
+
|
99
|
+
@socket.flush
|
100
|
+
return result
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# @see #write
|
105
|
+
#
|
106
|
+
# @since 0.2.0
|
107
|
+
#
|
108
|
+
def <<(data)
|
109
|
+
write(data)
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Closes the stream.
|
114
|
+
#
|
115
|
+
# @since 0.2.0
|
116
|
+
#
|
117
|
+
def close
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/rack/handler/http.rb
CHANGED
@@ -2,6 +2,7 @@ require 'net/http/server/daemon'
|
|
2
2
|
require 'net/http/server/version'
|
3
3
|
|
4
4
|
require 'rack'
|
5
|
+
require 'rack/rewindable_input'
|
5
6
|
require 'set'
|
6
7
|
|
7
8
|
module Rack
|
@@ -14,7 +15,7 @@ module Rack
|
|
14
15
|
# The default environment settings.
|
15
16
|
DEFAULT_ENV = {
|
16
17
|
'rack.version' => Rack::VERSION,
|
17
|
-
'rack.errors' =>
|
18
|
+
'rack.errors' => $stderr,
|
18
19
|
'rack.multithread' => true,
|
19
20
|
'rack.multiprocess' => false,
|
20
21
|
'rack.run_once' => false,
|
@@ -27,7 +28,7 @@ module Rack
|
|
27
28
|
# Special HTTP Headers used by Rack::Request
|
28
29
|
SPECIAL_HEADERS = Set[
|
29
30
|
'Content-Type',
|
30
|
-
'Content-Length'
|
31
|
+
'Content-Length'
|
31
32
|
]
|
32
33
|
|
33
34
|
#
|
@@ -81,16 +82,16 @@ module Rack
|
|
81
82
|
# @param [Hash] request
|
82
83
|
# An HTTP Request received from {Net::HTTP::Server}.
|
83
84
|
#
|
84
|
-
# @param [
|
85
|
-
# The
|
85
|
+
# @param [Net::HTTP::Server::Stream, Net::HTTP::Server::ChunkedStream] stream
|
86
|
+
# The stream that represents the body of the request.
|
86
87
|
#
|
87
88
|
# @return [Array<Integer, Hash, Array>]
|
88
89
|
# The response status, headers and body.
|
89
90
|
#
|
90
|
-
def call(request,
|
91
|
+
def call(request,stream)
|
91
92
|
request_uri = request[:uri]
|
92
|
-
remote_address = socket.remote_address
|
93
|
-
local_address = socket.local_address
|
93
|
+
remote_address = stream.socket.remote_address
|
94
|
+
local_address = stream.socket.local_address
|
94
95
|
|
95
96
|
env = {}
|
96
97
|
|
@@ -98,10 +99,10 @@ module Rack
|
|
98
99
|
env.merge!(DEFAULT_ENV)
|
99
100
|
|
100
101
|
# populate
|
101
|
-
env['rack.input'] =
|
102
|
+
env['rack.input'] = Rack::RewindableInput.new(stream)
|
102
103
|
|
103
104
|
if request_uri[:scheme]
|
104
|
-
env['rack.url_scheme'] = request_uri[:scheme]
|
105
|
+
env['rack.url_scheme'] = request_uri[:scheme].to_s
|
105
106
|
end
|
106
107
|
|
107
108
|
env['SERVER_NAME'] = local_address.getnameinfo[0]
|
@@ -111,8 +112,8 @@ module Rack
|
|
111
112
|
env['REMOTE_ADDR'] = remote_address.ip_address
|
112
113
|
env['REMOTE_PORT'] = remote_address.ip_port.to_s
|
113
114
|
|
114
|
-
env['REQUEST_METHOD'] = request[:method]
|
115
|
-
env['PATH_INFO'] = request_uri.fetch(:path,'*')
|
115
|
+
env['REQUEST_METHOD'] = request[:method].to_s
|
116
|
+
env['PATH_INFO'] = request_uri.fetch(:path,'*').to_s
|
116
117
|
env['QUERY_STRING'] = request_uri[:query_string].to_s
|
117
118
|
|
118
119
|
# add the headers
|
data/net-http-server.gemspec
CHANGED
@@ -1,10 +1,131 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gemspec|
|
6
|
+
root = File.dirname(__FILE__)
|
7
|
+
lib_dir = File.join(root,'lib')
|
8
|
+
files = if File.directory?('.git')
|
9
|
+
`git ls-files`.split($/)
|
10
|
+
elsif File.directory?('.hg')
|
11
|
+
`hg manifest`.split($/)
|
12
|
+
elsif File.directory?('.svn')
|
13
|
+
`svn ls -R`.split($/).select { |path| File.file?(path) }
|
14
|
+
else
|
15
|
+
Dir['{**/}{.*,*}'].select { |path| File.file?(path) }
|
16
|
+
end
|
17
|
+
|
18
|
+
filter_files = lambda { |paths|
|
19
|
+
case paths
|
20
|
+
when Array
|
21
|
+
(files & paths)
|
22
|
+
when String
|
23
|
+
(files & Dir[paths])
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
version = {
|
28
|
+
:file => 'net/http/server/version.rb',
|
29
|
+
:constant => 'Net::HTTP::Server::VERSION'
|
30
|
+
}
|
31
|
+
|
32
|
+
defaults = {
|
33
|
+
'name' => File.basename(File.dirname(__FILE__)),
|
34
|
+
'files' => files,
|
35
|
+
'executables' => filter_files['bin/*'].map { |path| File.basename(path) },
|
36
|
+
'test_files' => filter_files['{test/{**/}*_test.rb,spec/{**/}*_spec.rb}'],
|
37
|
+
'extra_doc_files' => filter_files['*.{txt,rdoc,md,markdown,tt,textile}'],
|
38
|
+
}
|
39
|
+
|
40
|
+
metadata = defaults.merge(YAML.load_file('gemspec.yml'))
|
41
|
+
|
42
|
+
gemspec.name = metadata.fetch('name',defaults[:name])
|
43
|
+
gemspec.version = if metadata['version']
|
44
|
+
metadata['version']
|
45
|
+
else
|
46
|
+
$LOAD_PATH << lib_dir unless $LOAD_PATH.include?(lib_dir)
|
47
|
+
|
48
|
+
require version[:file]
|
49
|
+
eval(version[:constant])
|
50
|
+
end
|
51
|
+
|
52
|
+
gemspec.summary = metadata.fetch('summary',metadata['description'])
|
53
|
+
gemspec.description = metadata.fetch('description',metadata['summary'])
|
54
|
+
|
55
|
+
case metadata['license']
|
56
|
+
when Array
|
57
|
+
gemspec.licenses = metadata['license']
|
58
|
+
when String
|
59
|
+
gemspec.license = metadata['license']
|
60
|
+
end
|
61
|
+
|
62
|
+
case metadata['authors']
|
63
|
+
when Array
|
64
|
+
gemspec.authors = metadata['authors']
|
65
|
+
when String
|
66
|
+
gemspec.author = metadata['authors']
|
67
|
+
end
|
68
|
+
|
69
|
+
gemspec.email = metadata['email']
|
70
|
+
gemspec.homepage = metadata['homepage']
|
71
|
+
|
72
|
+
case metadata['require_paths']
|
73
|
+
when Array
|
74
|
+
gemspec.require_paths = metadata['require_paths']
|
75
|
+
when String
|
76
|
+
gemspec.require_path = metadata['require_paths']
|
77
|
+
end
|
78
|
+
|
79
|
+
gemspec.files = filter_files[metadata['files']]
|
80
|
+
|
81
|
+
gemspec.executables = metadata['executables']
|
82
|
+
gemspec.extensions = metadata['extensions']
|
83
|
+
|
84
|
+
if Gem::VERSION < '1.7.'
|
85
|
+
gemspec.default_executable = gemspec.executables.first
|
86
|
+
end
|
87
|
+
|
88
|
+
gemspec.test_files = filter_files[metadata['test_files']]
|
89
|
+
|
90
|
+
unless gemspec.files.include?('.document')
|
91
|
+
gemspec.extra_rdoc_files = metadata['extra_doc_files']
|
92
|
+
end
|
93
|
+
|
94
|
+
gemspec.post_install_message = metadata['post_install_message']
|
95
|
+
gemspec.requirements = metadata['requirements']
|
96
|
+
|
97
|
+
if gemspec.respond_to?(:required_ruby_version=)
|
98
|
+
gemspec.required_ruby_version = metadata['required_ruby_version']
|
99
|
+
end
|
100
|
+
|
101
|
+
if gemspec.respond_to?(:required_rubygems_version=)
|
102
|
+
gemspec.required_rubygems_version = metadata['required_ruby_version']
|
103
|
+
end
|
104
|
+
|
105
|
+
parse_versions = lambda { |versions|
|
106
|
+
case versions
|
107
|
+
when Array
|
108
|
+
versions.map { |v| v.to_s }
|
109
|
+
when String
|
110
|
+
versions.split(/,\s*/)
|
111
|
+
end
|
112
|
+
}
|
113
|
+
|
114
|
+
if metadata['dependencies']
|
115
|
+
metadata['dependencies'].each do |name,versions|
|
116
|
+
gemspec.add_dependency(name,parse_versions[versions])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
if metadata['runtime_dependencies']
|
121
|
+
metadata['runtime_dependencies'].each do |name,versions|
|
122
|
+
gemspec.add_runtime_dependency(name,parse_versions[versions])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
if metadata['development_dependencies']
|
127
|
+
metadata['development_dependencies'].each do |name,versions|
|
128
|
+
gemspec.add_development_dependency(name,parse_versions[versions])
|
129
|
+
end
|
6
130
|
end
|
7
|
-
rescue NameError
|
8
|
-
STDERR.puts "The 'net-http-server.gemspec' file requires Ore."
|
9
|
-
STDERR.puts "Run `gem install ore-core` to install Ore."
|
10
131
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
require 'net/http/server/chunked_stream'
|
5
|
+
|
6
|
+
describe Net::HTTP::Server::ChunkedStream do
|
7
|
+
describe "#read" do
|
8
|
+
let(:data) { "foo\0bar" }
|
9
|
+
|
10
|
+
it "should read the length-line and then the following chunk" do
|
11
|
+
socket = StringIO.new("%x\r\n%s\r\n0\r\n\r\n" % [data.length, data])
|
12
|
+
stream = described_class.new(socket)
|
13
|
+
|
14
|
+
stream.read.should == data
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should ignore any extension data, after the length field" do
|
18
|
+
socket = StringIO.new("%x;lol\r\n%s\r\n0\r\n\r\n" % [data.length, data])
|
19
|
+
stream = described_class.new(socket)
|
20
|
+
|
21
|
+
stream.read.should == data
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should read an amount of data from a socket, directly into a buffer" do
|
25
|
+
length = 3
|
26
|
+
buffer = ''
|
27
|
+
|
28
|
+
socket = StringIO.new("%x\r\n%s\r\n0\r\n\r\n" % [data.length, data])
|
29
|
+
stream = described_class.new(socket)
|
30
|
+
|
31
|
+
stream.read(length,buffer).should == data[0,length]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should buffer unread data from the previously read chunk" do
|
35
|
+
socket = StringIO.new("%x\r\n%s\r\n0\r\n\r\n" % [data.length, data])
|
36
|
+
stream = described_class.new(socket)
|
37
|
+
|
38
|
+
stream.read(4).should == data[0,4]
|
39
|
+
stream.read.should == data[4..-1]
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should return nil after it reads the last chunk" do
|
43
|
+
socket = StringIO.new("0\r\n\r\n")
|
44
|
+
stream = described_class.new(socket)
|
45
|
+
|
46
|
+
stream.read.should be_nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "#write" do
|
51
|
+
it "should return the length of the data written" do
|
52
|
+
socket = StringIO.new
|
53
|
+
stream = described_class.new(socket)
|
54
|
+
|
55
|
+
stream.write('foo').should == 3
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should write a length-line along with the data" do
|
59
|
+
socket = StringIO.new
|
60
|
+
stream = described_class.new(socket)
|
61
|
+
|
62
|
+
stream.write('foo')
|
63
|
+
|
64
|
+
socket.string.should == "3\r\nfoo\r\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should not write empty Strings" do
|
68
|
+
socket = StringIO.new
|
69
|
+
stream = described_class.new(socket)
|
70
|
+
|
71
|
+
stream.write('')
|
72
|
+
|
73
|
+
socket.string.should be_empty
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#close" do
|
78
|
+
it "should write the 0 length-line" do
|
79
|
+
socket = StringIO.new
|
80
|
+
stream = described_class.new(socket)
|
81
|
+
|
82
|
+
stream.close
|
83
|
+
|
84
|
+
socket.string.should == "0\r\n\r\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'net/http/server/stream'
|
3
|
+
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
describe Net::HTTP::Server::Stream do
|
7
|
+
describe "#read" do
|
8
|
+
let(:data) { "foo\0bar" }
|
9
|
+
|
10
|
+
it "should read data from a socket" do
|
11
|
+
stream = described_class.new(StringIO.new(data))
|
12
|
+
stream.read.should == data
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should read an amount of data from a socket, directly into a buffer" do
|
16
|
+
length = 3
|
17
|
+
buffer = ''
|
18
|
+
|
19
|
+
stream = described_class.new(StringIO.new(data))
|
20
|
+
stream.read(length,buffer)
|
21
|
+
|
22
|
+
buffer.should == data[0,length]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#each" do
|
27
|
+
it "should stop yielding data on 'nil'" do
|
28
|
+
results = []
|
29
|
+
|
30
|
+
stream = described_class.new(StringIO.new())
|
31
|
+
stream.each { |chunk| results << chunk }
|
32
|
+
|
33
|
+
results.should be_empty
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should yield each chunk in the stream" do
|
37
|
+
chunks = ['A' * 4096, 'B' * 4096]
|
38
|
+
data = chunks.join('')
|
39
|
+
results = []
|
40
|
+
|
41
|
+
stream = described_class.new(StringIO.new(data))
|
42
|
+
stream.each { |chunk| results << chunk }
|
43
|
+
|
44
|
+
results.should == chunks
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#body" do
|
49
|
+
it "should append each chunk to a buffer" do
|
50
|
+
chunks = ['A' * 4096, 'B' * 4096]
|
51
|
+
data = chunks.join('')
|
52
|
+
|
53
|
+
stream = described_class.new(StringIO.new(data))
|
54
|
+
stream.body.should == data
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#write" do
|
59
|
+
it "should write to the socket and flush" do
|
60
|
+
data = "foo\n\rbar"
|
61
|
+
|
62
|
+
stream = described_class.new(StringIO.new)
|
63
|
+
stream.write(data).should == data.length
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -9,9 +9,6 @@ require 'rack/static'
|
|
9
9
|
describe Rack::Handler::HTTP do
|
10
10
|
include TestRequest::Helpers
|
11
11
|
|
12
|
-
let(:host) { '0.0.0.0' }
|
13
|
-
let(:port) { 9204 }
|
14
|
-
|
15
12
|
before(:all) do
|
16
13
|
app = Rack::Builder.app do
|
17
14
|
use Rack::Lint
|
@@ -19,7 +16,9 @@ describe Rack::Handler::HTTP do
|
|
19
16
|
run TestRequest.new
|
20
17
|
end
|
21
18
|
|
22
|
-
@
|
19
|
+
@host = '127.0.0.1'
|
20
|
+
@port = 9204
|
21
|
+
@server = Rack::Handler::HTTP.new(app, :Host => @host, :Port => @port)
|
23
22
|
|
24
23
|
Thread.new { @server.run }
|
25
24
|
Thread.pass until @server.running?
|
data/spec/spec_helper.rb
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
gem 'rspec', '~> 2.4
|
1
|
+
gem 'rspec', '~> 2.4'
|
2
2
|
require 'rspec'
|
metadata
CHANGED
@@ -1,155 +1,131 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-http-server
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
version: 0.1.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- Postmodern
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
dependencies:
|
20
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2011-08-24 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
21
15
|
name: parslet
|
22
|
-
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
16
|
+
requirement: &16921520 !ruby/object:Gem::Requirement
|
24
17
|
none: false
|
25
|
-
requirements:
|
18
|
+
requirements:
|
26
19
|
- - ~>
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
|
29
|
-
- 1
|
30
|
-
- 0
|
31
|
-
version: "1.0"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.0'
|
32
22
|
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: ore-tasks
|
36
23
|
prerelease: false
|
37
|
-
|
24
|
+
version_requirements: *16921520
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ore-tasks
|
27
|
+
requirement: &16920560 !ruby/object:Gem::Requirement
|
38
28
|
none: false
|
39
|
-
requirements:
|
29
|
+
requirements:
|
40
30
|
- - ~>
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
|
43
|
-
- 0
|
44
|
-
- 3
|
45
|
-
- 0
|
46
|
-
version: 0.3.0
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0.4'
|
47
33
|
type: :development
|
48
|
-
version_requirements: *id002
|
49
|
-
- !ruby/object:Gem::Dependency
|
50
|
-
name: rspec
|
51
34
|
prerelease: false
|
52
|
-
|
35
|
+
version_requirements: *16920560
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &16920080 !ruby/object:Gem::Requirement
|
53
39
|
none: false
|
54
|
-
requirements:
|
40
|
+
requirements:
|
55
41
|
- - ~>
|
56
|
-
- !ruby/object:Gem::Version
|
57
|
-
|
58
|
-
- 2
|
59
|
-
- 4
|
60
|
-
- 0
|
61
|
-
version: 2.4.0
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '2.4'
|
62
44
|
type: :development
|
63
|
-
version_requirements: *id003
|
64
|
-
- !ruby/object:Gem::Dependency
|
65
|
-
name: yard
|
66
45
|
prerelease: false
|
67
|
-
|
46
|
+
version_requirements: *16920080
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: yard
|
49
|
+
requirement: &16919540 !ruby/object:Gem::Requirement
|
68
50
|
none: false
|
69
|
-
requirements:
|
51
|
+
requirements:
|
70
52
|
- - ~>
|
71
|
-
- !ruby/object:Gem::Version
|
72
|
-
segments:
|
73
|
-
- 0
|
74
|
-
- 6
|
75
|
-
- 0
|
53
|
+
- !ruby/object:Gem::Version
|
76
54
|
version: 0.6.0
|
77
55
|
type: :development
|
78
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *16919540
|
79
58
|
description: A Rack compatible pure Ruby HTTP Server.
|
80
59
|
email: postmodern.mod3@gmail.com
|
81
60
|
executables: []
|
82
|
-
|
83
61
|
extensions: []
|
84
|
-
|
85
|
-
|
86
|
-
- README.md
|
87
|
-
- ChangeLog.md
|
88
|
-
- LICENSE.txt
|
89
|
-
files:
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
90
64
|
- .document
|
91
65
|
- .gemtest
|
66
|
+
- .gitignore
|
92
67
|
- .rspec
|
93
68
|
- .yardopts
|
94
69
|
- ChangeLog.md
|
95
70
|
- LICENSE.txt
|
96
71
|
- README.md
|
97
72
|
- Rakefile
|
73
|
+
- benchmarks/parser.rb
|
74
|
+
- benchmarks/server.rb
|
98
75
|
- gemspec.yml
|
99
76
|
- lib/net/http/server.rb
|
77
|
+
- lib/net/http/server/chunked_stream.rb
|
100
78
|
- lib/net/http/server/daemon.rb
|
101
79
|
- lib/net/http/server/parser.rb
|
102
80
|
- lib/net/http/server/requests.rb
|
103
81
|
- lib/net/http/server/responses.rb
|
104
82
|
- lib/net/http/server/server.rb
|
83
|
+
- lib/net/http/server/stream.rb
|
105
84
|
- lib/net/http/server/version.rb
|
106
85
|
- lib/rack/handler/http.rb
|
107
86
|
- net-http-server.gemspec
|
87
|
+
- spec/net/http/server/chunked_stream_spec.rb
|
88
|
+
- spec/net/http/server/daemon_spec.rb
|
89
|
+
- spec/net/http/server/parser_spec.rb
|
90
|
+
- spec/net/http/server/requests_spec.rb
|
91
|
+
- spec/net/http/server/responses_spec.rb
|
92
|
+
- spec/net/http/server/server_spec.rb
|
93
|
+
- spec/net/http/server/stream_spec.rb
|
108
94
|
- spec/rack/handler/helpers/test_request.rb
|
109
95
|
- spec/rack/handler/http_spec.rb
|
110
96
|
- spec/rack/handler/images/image.jpg
|
111
|
-
- spec/server/daemon_spec.rb
|
112
|
-
- spec/server/parser_spec.rb
|
113
|
-
- spec/server/requests_spec.rb
|
114
|
-
- spec/server/responses_spec.rb
|
115
|
-
- spec/server/server_spec.rb
|
116
97
|
- spec/spec_helper.rb
|
117
|
-
has_rdoc: yard
|
118
98
|
homepage: http://github.com/postmodern/net-http-server
|
119
|
-
licenses:
|
99
|
+
licenses:
|
120
100
|
- MIT
|
121
101
|
post_install_message:
|
122
102
|
rdoc_options: []
|
123
|
-
|
124
|
-
require_paths:
|
103
|
+
require_paths:
|
125
104
|
- lib
|
126
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
106
|
none: false
|
128
|
-
requirements:
|
129
|
-
- -
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
|
132
|
-
|
133
|
-
version: "0"
|
134
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
112
|
none: false
|
136
|
-
requirements:
|
137
|
-
- -
|
138
|
-
- !ruby/object:Gem::Version
|
139
|
-
|
140
|
-
- 0
|
141
|
-
version: "0"
|
113
|
+
requirements:
|
114
|
+
- - ! '>='
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
142
117
|
requirements: []
|
143
|
-
|
144
|
-
|
145
|
-
rubygems_version: 1.3.7
|
118
|
+
rubyforge_project:
|
119
|
+
rubygems_version: 1.8.8
|
146
120
|
signing_key:
|
147
121
|
specification_version: 3
|
148
122
|
summary: A pure Ruby HTTP Server
|
149
|
-
test_files:
|
150
|
-
- spec/server/
|
151
|
-
- spec/server/
|
152
|
-
- spec/server/parser_spec.rb
|
153
|
-
- spec/server/
|
154
|
-
- spec/server/
|
123
|
+
test_files:
|
124
|
+
- spec/net/http/server/chunked_stream_spec.rb
|
125
|
+
- spec/net/http/server/daemon_spec.rb
|
126
|
+
- spec/net/http/server/parser_spec.rb
|
127
|
+
- spec/net/http/server/requests_spec.rb
|
128
|
+
- spec/net/http/server/responses_spec.rb
|
129
|
+
- spec/net/http/server/server_spec.rb
|
130
|
+
- spec/net/http/server/stream_spec.rb
|
155
131
|
- spec/rack/handler/http_spec.rb
|