require 'socket' require 'uri' require 'json' # # minimal http server for DataPipeline::RestStorage tests, one request per connection # server = TestHttpServer.new { |req| [200, { 'items' => [] }] } # req: { method:, path:, query:, headers:, body: } -> [status, hash/array or string] # class TestHttpServer attr_reader :requests def initialize(&handler) @handler = handler @requests = [] @server = TCPServer.new('127.0.0.1', 0) @thread = Thread.new { loop { serve(@server.accept) } } end def url = "http://127.0.0.1:#{@server.addr[1]}" def stop @thread.kill @server.close end private def serve client method, target = client.gets.to_s.split return unless method headers = {} while (line = client.gets) && line != "\r\n" key, value = line.split(':', 2) headers[key.downcase] = value.strip end body = headers['content-length'] ? client.read(headers['content-length'].to_i) : '' path, query = target.split('?', 2) request = { method:, path:, query: URI.decode_www_form(query.to_s).to_h, headers:, body: } @requests << request status, payload = @handler.call(request) text = payload.is_a?(String) ? payload : JSON.generate(payload) client.write "HTTP/1.1 #{status} X\r\nContent-Type: application/json\r\n" \ "Content-Length: #{text.bytesize}\r\nConnection: close\r\n\r\n#{text}" rescue StandardError => e warn "TestHttpServer: #{e.class}: #{e.message}" ensure client.close end end