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
         |