aboutsummaryrefslogtreecommitdiff
path: root/src/protocols
diff options
context:
space:
mode:
Diffstat (limited to 'src/protocols')
-rw-r--r--src/protocols/http.nim67
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)