summaryrefslogtreecommitdiff
path: root/spec/ruby/library/net/http/http/fixtures/http_server.rb
blob: c1cedbfa765b3a0dd819c37278aab9e7edce0b38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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