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.
@@ -0,0 +1,3 @@
1
+ doc/
2
+ pkg/
3
+ vendor/
@@ -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
- * Postmodern (postmodern.mod3 at gmail.com)
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,socket|
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.3.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
- STDERR.puts e.message
11
- STDERR.puts "Run `gem install ore-tasks` to install 'ore/tasks'."
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.0'
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
@@ -11,6 +11,6 @@ dependencies:
11
11
  parslet: ~> 1.0
12
12
 
13
13
  development_dependencies:
14
- ore-tasks: ~> 0.3.0
15
- rspec: ~> 2.4.0
14
+ ore-tasks: ~> 0.4
15
+ rspec: ~> 2.4
16
16
  yard: ~> 0.6.0
@@ -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 (STDERR)
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,STDERR)
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, socket]
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 [TCPSocket] socket
78
- # The TCP socket of the client.
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,socket)
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
- request_line = stream.readline("\r\n")
27
+ begin
28
+ request_line = stream.readline("\r\n")
28
29
 
29
- # the request line must contain 'HTTP/'
30
- return unless request_line.include?('HTTP/')
30
+ # the request line must contain 'HTTP/'
31
+ return unless request_line.include?('HTTP/')
31
32
 
32
- buffer << request_line
33
+ buffer << request_line
33
34
 
34
- stream.each_line("\r\n") do |header|
35
- buffer << header
35
+ stream.each_line("\r\n") do |header|
36
+ buffer << header
36
37
 
37
- # a header line must contain a ':' character followed by
38
- # linear-white-space (either ' ' or "\t").
39
- unless (header.include?(': ') || header.include?(":\t"))
40
- # if this is not a header line, check if it is the end
41
- # of the request
42
- if header == "\r\n"
43
- # end of the request
44
- break
45
- else
46
- # invalid header line
47
- return
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
- write_body stream, body
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
@@ -4,7 +4,7 @@ module Net
4
4
  class HTTP < Protocol
5
5
  module Server
6
6
  # net-http-server version.
7
- VERSION = '0.1.0'
7
+ VERSION = '0.2.0'
8
8
  end
9
9
  end
10
10
  end
@@ -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' => STDERR,
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 [TCPSocket] socket
85
- # The socket that the request was received from.
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,socket)
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'] = socket
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
@@ -1,10 +1,131 @@
1
- # -*- encoding: utf-8 -*-
1
+ # encoding: utf-8
2
2
 
3
- begin
4
- Ore::Specification.new do |gemspec|
5
- # custom logic here
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
@@ -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
- @server = Rack::Handler::HTTP.new(app, :Host => host, :Port => port)
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?
@@ -1,2 +1,2 @@
1
- gem 'rspec', '~> 2.4.0'
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
- prerelease: false
5
- segments:
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
- date: 2011-01-26 00:00:00 -08:00
18
- default_executable:
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
- prerelease: false
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
- segments:
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
- requirement: &id002 !ruby/object:Gem::Requirement
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
- segments:
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
- requirement: &id003 !ruby/object:Gem::Requirement
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
- segments:
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
- requirement: &id004 !ruby/object:Gem::Requirement
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
- version_requirements: *id004
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
- extra_rdoc_files:
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
- segments:
132
- - 0
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
- segments:
140
- - 0
141
- version: "0"
113
+ requirements:
114
+ - - ! '>='
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
142
117
  requirements: []
143
-
144
- rubyforge_project: net-http-server
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/daemon_spec.rb
151
- - spec/server/requests_spec.rb
152
- - spec/server/parser_spec.rb
153
- - spec/server/responses_spec.rb
154
- - spec/server/server_spec.rb
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