From 0758f0996b954017735e28bfb93b2ba917b61958 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Sat, 22 Jun 2024 09:11:03 +0300 Subject: [PATCH] http-client: content-type override, split header values Add a "response-content-type-override" field to the resolve step to override body parsing behavior. Send each reponse header as a seperate message rather than concatenated by the Nim HTTP client library. --- README.md | 2 +- config.prs | 10 +++++++++- sbom.json | 2 +- src/http_client.nim | 25 ++++++++++++++----------- src/schema/config.nim | 9 ++++++--- 5 files changed, 31 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index f7d3bdf..fefd54b 100644 --- a/README.md +++ b/README.md @@ -302,7 +302,7 @@ $client-resolver ? $client [ # Pass the resolver dataspace to the client. ? ?cap> [ - $cap $client-resolver> + $cap $client-resolver> ] > diff --git a/config.prs b/config.prs index 41b4a90..f5821dd 100644 --- a/config.prs +++ b/config.prs @@ -24,7 +24,15 @@ UnixAddress = . SocketAddress = TcpAddress / UnixAddress . -HttpClientStep = . +HttpClientStep = . +HttpClientStepDetail = { + # Body parsing happens according to a heuristic interpretation + # of Content-Type headers. + # Set this field as "application/octet-stream" to never parse + # response bodies or to "application/json" to parse all response + # bodies as JSON. + response-content-type-override: string +} . HttpDriverStep= . diff --git a/sbom.json b/sbom.json index 9382af0..e32b02e 100644 --- a/sbom.json +++ b/sbom.json @@ -7,7 +7,7 @@ "bom-ref": "pkg:nim/syndicate_utils", "name": "syndicate_utils", "description": "Utilites for Syndicated Actors and Synit", - "version": "20240621", + "version": "20240622", "authors": [ { "name": "Emery Hemingway" diff --git a/src/http_client.nim b/src/http_client.nim index 7841ad2..ca207db 100644 --- a/src/http_client.nim +++ b/src/http_client.nim @@ -28,7 +28,7 @@ proc url(req: HttpRequest): Uri = proc toContent(body: Value; contentType: var string): string = case contentType - of "application/json": + of "application/json", "text/javascript": var stream = newStringStream() writeText(stream, body, textJson) return stream.data.move @@ -52,9 +52,9 @@ proc toContent(body: Value; contentType: var string): string = raise newException(ValueError, "unknown content type") proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} = - let pat = Resolve?:{ 0: HttpClientStep.matchType, 1: grab() } + let pat = Resolve?:{ 0: HttpClientStep.grabWithin, 1: grab() } result = spawnActor(turn, "http-client") do (turn: Turn): - during(turn, relay, pat) do (observer: Cap): + during(turn, relay, pat) do (detail: HttpClientStepDetail, observer: Cap): linkActor(turn, "session") do (turn: Turn): let ds = turn.newDataspace() discard publish(turn, observer, ResolvedAccepted(responderSession: ds)) @@ -64,7 +64,7 @@ proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} = try: var headers = newHttpHeaders() - contentType = "" + contentType: string for key, val in ctx.req.headers: if key == Symbol"content-type" or key == Symbol"Content-Type": contentType = val @@ -74,20 +74,23 @@ proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} = ctx.req.method.string.toUpper, ctx.req.body.toContent(contentType), headers ) - client.headers["content-type"] = contentType 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": + for key, vals in stdRes.headers.table: + for val in vals.items: + resp.header.name = key.Symbol + resp.header.value = val + message(turn, peer, resp) + if detail.`response-content-type-override` != "": + contentType = detail.`response-content-type-override` + else: + for val in stdRes.headers.table.getOrDefault("content-type").items: contentType = val - resp.header.name = key.Symbol - resp.header.value = val - message(turn, peer, resp) case contentType - of "application/json", "text/preserves": + of "application/json", "text/preserves", "text/javascript": message(turn, peer, initRecord("done", stdRes.bodyStream.readAll.parsePreserves)) of "application/preserves": diff --git a/src/schema/config.nim b/src/schema/config.nim index bf17801..2d505b5 100644 --- a/src/schema/config.nim +++ b/src/schema/config.nim @@ -39,6 +39,9 @@ type XsltArguments* {.preservesRecord: "xslt".} = object `field0`*: XsltArgumentsField0 + HttpClientStepDetail* {.preservesDictionary.} = object + `response-content-type-override`*: string + JsonSocketTranslatorStepField0* {.preservesDictionary.} = object `socket`*: SocketAddress @@ -51,10 +54,8 @@ type FileSystemUsageArguments* {.preservesRecord: "file-system-usage".} = object `field0`*: FileSystemUsageArgumentsField0 - HttpClientStepField0* {.preservesDictionary.} = object - HttpClientStep* {.preservesRecord: "http-client".} = object - `field0`*: HttpClientStepField0 + `detail`*: HttpClientStepDetail HttpDriverStepField0* {.preservesDictionary.} = object @@ -109,6 +110,7 @@ type proc `$`*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments | SqliteStep | XsltArguments | + HttpClientStepDetail | JsonSocketTranslatorStep | FileSystemUsageArguments | HttpClientStep | @@ -127,6 +129,7 @@ proc `$`*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments | proc encode*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments | SqliteStep | XsltArguments | + HttpClientStepDetail | JsonSocketTranslatorStep | FileSystemUsageArguments | HttpClientStep |