92 lines
3.3 KiB
Nim
92 lines
3.3 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
# TODO: write a TAPS HTTP client. Figure out how to externalise TLS.
|
|
|
|
import
|
|
std/[httpclient, options, streams, strutils, tables, uri],
|
|
pkg/taps,
|
|
pkg/preserves,
|
|
pkg/syndicate, pkg/syndicate/protocols/http,
|
|
./schema/config
|
|
|
|
proc url(req: HttpRequest): Uri =
|
|
result.scheme = if req.port == 80: "http" else: "https"
|
|
result.hostname = req.host.present
|
|
result.port = $req.port
|
|
for i, p in req.path:
|
|
if 0 < i: result.path.add '/'
|
|
result.path.add p.encodeUrl
|
|
for key, vals in req.query:
|
|
if result.query.len > 0:
|
|
result.query.add '&'
|
|
result.query.add key.string.encodeUrl
|
|
for i, val in vals:
|
|
if i == 0: result.query.add '='
|
|
elif i < vals.high: result.query.add ','
|
|
result.query.add val.string.encodeUrl
|
|
|
|
proc bodyString(req: HttpRequest): string =
|
|
if req.body.orKind == RequestBodyKind.present:
|
|
return cast[string](req.body.present)
|
|
|
|
proc spawnHttpClient*(turn: Turn; root: Cap): Actor {.discardable.} =
|
|
|
|
during(turn, root, ?:HttpClientArguments) do (ds: Cap):
|
|
spawn("http-client", turn) do (turn: Turn):
|
|
during(turn, ds, HttpContext.grabType) do (ctx: HttpContext):
|
|
let peer = ctx.res.unembed(Cap).get
|
|
var client = newHttpClient()
|
|
try:
|
|
var
|
|
headers = newHttpHeaders()
|
|
contentType = ""
|
|
for key, val in ctx.req.headers:
|
|
if key == Symbol"Content-Type":
|
|
contentType = val
|
|
client.headers[key.string] = val
|
|
let stdRes = client.request(
|
|
ctx.req.url,
|
|
ctx.req.method.string.toUpper,
|
|
ctx.req.bodyString, headers
|
|
)
|
|
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
|
resp.status.code = stdRes.status[0 .. 2].parseInt
|
|
resp.status.message = stdRes.status[3 .. ^1]
|
|
message(turn, peer, resp)
|
|
resp = HttpResponse(orKind: HttpResponseKind.header)
|
|
for key, val in stdRes.headers:
|
|
if key == "Content-Type":
|
|
contentType = val
|
|
resp.header.name = key.Symbol
|
|
resp.header.value = val
|
|
message(turn, peer, resp)
|
|
case contentType
|
|
of "application/json", "text/preserves":
|
|
message(turn, peer,
|
|
initRecord("done", stdRes.bodyStream.readAll.parsePreserves))
|
|
of "application/preserves":
|
|
message(turn, peer,
|
|
initRecord("done", stdRes.bodyStream.decodePreserves))
|
|
else:
|
|
resp = HttpResponse(orKind: HttpResponseKind.done)
|
|
resp.done.chunk.string = stdRes.bodyStream.readAll()
|
|
message(turn, peer, resp)
|
|
except CatchableError as err:
|
|
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
|
resp.status.code = 400
|
|
resp.status.message = "Internal client error"
|
|
message(turn, peer, resp)
|
|
resp = HttpResponse(orKind: HttpResponseKind.done)
|
|
resp.done.chunk.string = err.msg
|
|
message(turn, peer, resp)
|
|
client.close()
|
|
do:
|
|
client.close()
|
|
|
|
when isMainModule:
|
|
import syndicate/relays
|
|
runActor("main") do (turn: Turn):
|
|
resolveEnvironment(turn) do (turn: Turn; ds: Cap):
|
|
spawnHttpClient(turn, ds)
|