require 'socket' module NetHTTPSpecs class NullWriter def <<(s) end def puts(*args) end def print(*args) end def printf(*args) end end class SmallHTTPServer def initialize(bind_address) @server = TCPServer.new(bind_address, 0) @thread = Thread.new { Thread.current.abort_on_exception = true listen } end def ip @server.addr[3] end def port @server.addr[1] end def listen until @server.closed? client = @server.accept handle_client(client) end end def handle_client(client) begin until client.closed? request = client.gets("\r\n\r\n") break unless request if request == "CLOSE" @server.close break end handle_request(client, request) end ensure client.close end end def parse_request(request) request, *headers = request.chomp.lines.map { |line| line.chomp } request_method, request_uri, _http_version = request.split headers = headers.map { |line| line.split(': ', 2) }.to_h [request_method, request_uri, headers] end def handle_request(client, request) request_method, request_uri, headers = parse_request(request) if headers.include? 'Content-Length' request_body_size = Integer(headers['Content-Length']) request_body = client.read(request_body_size) end case request_uri when '/' raise request_method unless request_method == 'GET' reply(client, "This is the index page.", request_method) when '/request' reply(client, "Request type: #{request_method}", request_method) when '/request/body' reply(client, request_body, request_method) when '/request/header' reply(client, headers.inspect, request_method) when '/request/basic_auth' reply(client, "username: \npassword: ", request_method) else raise request_uri end end def reply(client, body, request_method) client.print "HTTP/1.1 200 OK\r\n" if request_method == 'HEAD' client.close else client.print "Content-Type: text/plain\r\n" client.print "Content-Length: #{body.bytesize}\r\n" client.print "\r\n" client.print body end end def close TCPSocket.open(ip, port) do |socket| socket.write "CLOSE" end @thread.join end end @server = nil class << self def port raise "server not started" unless @server @server.port end def start_server bind_address = platform_is(:windows) ? "localhost" : "127.0.0.1" @server = SmallHTTPServer.new(bind_address) end def stop_server if @server @server.close @server = nil end end end end