diff --git a/lib/nanoserve.rb b/lib/nanoserve.rb index d6573fa..3e15e1d 100644 --- a/lib/nanoserve.rb +++ b/lib/nanoserve.rb @@ -11,15 +11,14 @@ module NanoServe @port = port @block = block @thr = nil - @srv = nil end def start(y) - @srv = TCPServer.new(@port) + server = TCPServer.new(@port) @thr = Thread.new do Thread.abort_on_exception = true - conn = @srv.accept + conn = server.accept port, host = conn.peeraddr[1, 2] client = "#{host}:#{port}" logger.debug "#{client}: connected" @@ -28,9 +27,8 @@ module NanoServe @block.call(conn, y) rescue EOFError logger.debug "#{client}: disconnected" - else - logger.debug "#{client}: closed" ensure + logger.debug "#{client}: closed" conn.close end end @@ -40,22 +38,18 @@ module NanoServe yield @thr.join + server.close y end def stop - @srv.close @thr.kill end def logger @logger ||= Logger.new(STDOUT).tap { |l| l.level = Logger::INFO } end - - def logger=(logger) - @logger = logger - end end class HTTPResponder < TCPResponder @@ -65,18 +59,11 @@ module NanoServe buf = +'' loop do line = conn.readline + break if line.chomp == '' req << line - buf << line if logger.debug? - break if req.headers? + buf << line end logger.debug "request:\n" + buf.gsub(/^/, ' ') - length = 0 - while req.content_length? && length < req.content_length - data = conn.readpartial(1024) - length += data.size - req << data - end - logger.debug "request body: #{length} bytes read" res = Response.new logger.debug 'calling' @@ -95,47 +82,10 @@ module NanoServe @http_version = nil @sep = nil @headers = {} - @body = +''.encode('ASCII-8BIT') - end - - def host - @headers['host'] - end - - def path - @uri.path - end - - def query_array - URI.decode_www_form(@uri.query || '') - end - - def form_array - form? ? URI.decode_www_form(body) : [] - end - - def query - Hash[*query_array.flatten] - end - - def form - Hash[*form_array.flatten] end def params - query.merge(form) - end - - def form? - content_type == 'application/x-www-form-urlencoded' - end - - def body - @body - end - - def [](key) - @headers[key.downcase] + Hash[*@uri.query.split('&').map { |kv| kv.split('=') }.flatten] end def <<(line) @@ -144,28 +94,10 @@ module NanoServe elsif @sep.nil? parse_header(line.chomp) else - parse_body(line) + @body << line end end - def headers? - @sep - end - - def content_length - @headers['content-length'].to_i - end - - def content_length? - @headers.key?('content-length') - end - - def content_type - @headers['content-type'] - end - - private - REQ_RE = %r{(?[A-Z]+)\s+(?\S+)\s+(?HTTP/\d+.\d+)$} def parse_request(str) @@ -179,7 +111,7 @@ module NanoServe end def parse_method(str) - str.upcase + str end def parse_path(str) @@ -191,20 +123,13 @@ module NanoServe end def parse_header(str) - if str == '' - @sep = true - return - end + (@sep = '' && return) if str == '' - unless (m = str.match(/(?
[A-Za-z][-A-Za-z]*):\s+(?.+)$/)) + unless (m = str.match(/(?
[A-Z][-A-Za-z]*):\s+(?.+)$/)) raise RequestError, "cannot parse header: '#{str}'" end - @headers[m[:header].downcase] = m[:value] - end - - def parse_body(line) - @body << line + @headers[m[:header]] = m[:value] end end @@ -226,7 +151,7 @@ module NanoServe end def body=(value) - @body = value.tap { @content_length = value.bytes.count.to_s } + @body = value.tap { @content_length = body.bytes.count.to_s } end def to_s @@ -256,7 +181,7 @@ module NanoServe end def content_length - @content_length || '0' + @content_length ||= body.bytes.count.to_s end def content_type diff --git a/nanoserve.gemspec b/nanoserve.gemspec index 81c28a2..32af8e4 100644 --- a/nanoserve.gemspec +++ b/nanoserve.gemspec @@ -2,7 +2,7 @@ Gem::Specification.new do |s| s.name = 'nanoserve' - s.version = '0.3.0' + s.version = '0.1.0' s.licenses = ['3BSD'] s.summary = 'Listen to one-shot connections' s.authors = ['Loic Nageleisen'] diff --git a/test/test_nanoserve.rb b/test/test_nanoserve.rb index 29dd919..f5bf694 100644 --- a/test/test_nanoserve.rb +++ b/test/test_nanoserve.rb @@ -24,12 +24,10 @@ class TestNanoServe < MiniTest::Test s.close end - r.stop - assert_equal(uuid, buf) end - def test_http_responder_get + def test_http_responder uuid = SecureRandom.uuid.encode('UTF-8') uri = URI('http://localhost:2000') @@ -52,50 +50,6 @@ class TestNanoServe < MiniTest::Test Net::HTTP.get(uri + "test?uuid=#{uuid}") end - r.stop - assert_equal(uuid, req.first.params['uuid']) end - - def test_http_responder_post - uuid = SecureRandom.uuid.encode('UTF-8') - uri = URI('http://localhost:2000') - - r = NanoServe::HTTPResponder.new(uri.host, uri.port) do |res, req, y| - y << req - - res.body = <<-EOS.gsub(/^ {8}/, '') - - - An Example Page - - - Hello World, this is a very simple HTML document. - - - EOS - end - - req = r.start([]) do - Net::HTTP.post_form( - uri + "test?uuid=#{uuid}&p=query", - 'p' => 'form', - 'f' => 'foo', - ) - end - - r.stop - - assert_equal(uuid, req.first.params['uuid']) - assert_equal(uuid, req.first.query['uuid']) - assert_nil(req.first.form['uuid']) - - assert_equal('foo', req.first.params['f']) - assert_nil(req.first.query['f']) - assert_equal('foo', req.first.form['f']) - - assert_equal('form', req.first.params['p']) - assert_equal('query', req.first.query['p']) - assert_equal('form', req.first.form['p']) - end end