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
|