net-http-server 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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