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.
This commit is contained in:
Emery Hemingway 2024-06-22 09:11:03 +03:00
parent f6879a906d
commit 0758f0996b
5 changed files with 31 additions and 17 deletions

View File

@ -302,7 +302,7 @@ $client-resolver ? <accepted ?client> $client [
# Pass the resolver dataspace to the client.
? <service-object <daemon http-client> ?cap> [
$cap <resolve <http-client {}> $client-resolver>
$cap <resolve <http-client { response-content-type-override: "" }> $client-resolver>
]
<require-service <daemon http-client>>

View File

@ -24,7 +24,15 @@ UnixAddress = <unix @path string>.
SocketAddress = TcpAddress / UnixAddress .
HttpClientStep = <http-client { }>.
HttpClientStep = <http-client @detail HttpClientStepDetail>.
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= <http-driver { }>.

View File

@ -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"

View File

@ -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":

View File

@ -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 |