diff options
Diffstat (limited to 'src/protocols')
-rw-r--r-- | src/protocols/http.nim | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/src/protocols/http.nim b/src/protocols/http.nim new file mode 100644 index 0000000..df812fd --- /dev/null +++ b/src/protocols/http.nim @@ -0,0 +1,67 @@ +import std/[strutils, net], ../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) |