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
|
import std/[strutils, net], ../formats/uri
# https://datatracker.ietf.org/doc/html/rfc1945
type Http* = object of RootObj
version*: string # always present
headers*: seq[tuple[header: string, value: string]]
type HttpRequest* = object of Http
`method`*: string # only present in requests
uri*: string # only present in requests
type HttpResponse* = object of Http
status*: int # status code, only in responses
reason*: string # status elaboration, only in responses
body*: string # html document, usually
# This parses a HTTP response that has been split into headers and a body.
func parseResponse*(http: seq[string], body: string): HttpResponse =
# let http: seq[string] = http.split("\r\n")
let split = http[0].split(' ', maxsplit=2)
if split.len == 3:
result.version = split[0]
result.status = split[1].parseInt
result.reason = split[2]
else:
raise newException(RangeDefect, "First line of response is invalid: " & http[0])
# Note: the spec specifies that \r\n\r\n marks the end of a request
for header in http[1 ..< http.len]:
let split = header.split(':', maxsplit=1)
if split.len == 2:
result.headers.add((split[0].toLower, split[1].strip()))
result.body = body
proc httpRequest*(url: Url): HttpResponse =
let exampleRequest = "GET " & url.path & " HTTP/1.0\r\nHost: example.com\r\n\r\n"
let socket = newSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
var port = 80
if url.scheme == "https":
port = 443
let ctx: SSLContext = newContext()
ctx.wrapSocket(socket)
if url.port != 0:
port = url.port
socket.connect(url.host, Port(port))
socket.send(exampleRequest)
var
response: seq[string]
buffer: string
while true:
socket.readLine(buffer)
if buffer == "\r\n":
break
response.add(buffer)
# assert "transfer-encoding" notin parsed.headers
# assert "content-encoding" notin parsed.headers
var body: string
while socket.recv(buffer, 1024) > 0: # why?
body &= buffer
result = parseResponse(response, body)
|