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:
parent
f6879a906d
commit
0758f0996b
|
@ -302,7 +302,7 @@ $client-resolver ? <accepted ?client> $client [
|
||||||
|
|
||||||
# Pass the resolver dataspace to the client.
|
# Pass the resolver dataspace to the client.
|
||||||
? <service-object <daemon http-client> ?cap> [
|
? <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>>
|
<require-service <daemon http-client>>
|
||||||
|
|
10
config.prs
10
config.prs
|
@ -24,7 +24,15 @@ UnixAddress = <unix @path string>.
|
||||||
|
|
||||||
SocketAddress = TcpAddress / UnixAddress .
|
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 { }>.
|
HttpDriverStep= <http-driver { }>.
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"bom-ref": "pkg:nim/syndicate_utils",
|
"bom-ref": "pkg:nim/syndicate_utils",
|
||||||
"name": "syndicate_utils",
|
"name": "syndicate_utils",
|
||||||
"description": "Utilites for Syndicated Actors and Synit",
|
"description": "Utilites for Syndicated Actors and Synit",
|
||||||
"version": "20240621",
|
"version": "20240622",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Emery Hemingway"
|
"name": "Emery Hemingway"
|
||||||
|
|
|
@ -28,7 +28,7 @@ proc url(req: HttpRequest): Uri =
|
||||||
|
|
||||||
proc toContent(body: Value; contentType: var string): string =
|
proc toContent(body: Value; contentType: var string): string =
|
||||||
case contentType
|
case contentType
|
||||||
of "application/json":
|
of "application/json", "text/javascript":
|
||||||
var stream = newStringStream()
|
var stream = newStringStream()
|
||||||
writeText(stream, body, textJson)
|
writeText(stream, body, textJson)
|
||||||
return stream.data.move
|
return stream.data.move
|
||||||
|
@ -52,9 +52,9 @@ proc toContent(body: Value; contentType: var string): string =
|
||||||
raise newException(ValueError, "unknown content type")
|
raise newException(ValueError, "unknown content type")
|
||||||
|
|
||||||
proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} =
|
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):
|
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):
|
linkActor(turn, "session") do (turn: Turn):
|
||||||
let ds = turn.newDataspace()
|
let ds = turn.newDataspace()
|
||||||
discard publish(turn, observer, ResolvedAccepted(responderSession: ds))
|
discard publish(turn, observer, ResolvedAccepted(responderSession: ds))
|
||||||
|
@ -64,7 +64,7 @@ proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} =
|
||||||
try:
|
try:
|
||||||
var
|
var
|
||||||
headers = newHttpHeaders()
|
headers = newHttpHeaders()
|
||||||
contentType = ""
|
contentType: string
|
||||||
for key, val in ctx.req.headers:
|
for key, val in ctx.req.headers:
|
||||||
if key == Symbol"content-type" or key == Symbol"Content-Type":
|
if key == Symbol"content-type" or key == Symbol"Content-Type":
|
||||||
contentType = val
|
contentType = val
|
||||||
|
@ -74,20 +74,23 @@ proc spawnHttpClient*(turn: Turn; relay: Cap): Actor {.discardable.} =
|
||||||
ctx.req.method.string.toUpper,
|
ctx.req.method.string.toUpper,
|
||||||
ctx.req.body.toContent(contentType), headers
|
ctx.req.body.toContent(contentType), headers
|
||||||
)
|
)
|
||||||
client.headers["content-type"] = contentType
|
|
||||||
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
var resp = HttpResponse(orKind: HttpResponseKind.status)
|
||||||
resp.status.code = stdRes.status[0 .. 2].parseInt
|
resp.status.code = stdRes.status[0 .. 2].parseInt
|
||||||
resp.status.message = stdRes.status[3 .. ^1]
|
resp.status.message = stdRes.status[3 .. ^1]
|
||||||
message(turn, peer, resp)
|
message(turn, peer, resp)
|
||||||
resp = HttpResponse(orKind: HttpResponseKind.header)
|
resp = HttpResponse(orKind: HttpResponseKind.header)
|
||||||
for key, val in stdRes.headers:
|
for key, vals in stdRes.headers.table:
|
||||||
if key == "Content-Type":
|
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
|
contentType = val
|
||||||
resp.header.name = key.Symbol
|
|
||||||
resp.header.value = val
|
|
||||||
message(turn, peer, resp)
|
|
||||||
case contentType
|
case contentType
|
||||||
of "application/json", "text/preserves":
|
of "application/json", "text/preserves", "text/javascript":
|
||||||
message(turn, peer,
|
message(turn, peer,
|
||||||
initRecord("done", stdRes.bodyStream.readAll.parsePreserves))
|
initRecord("done", stdRes.bodyStream.readAll.parsePreserves))
|
||||||
of "application/preserves":
|
of "application/preserves":
|
||||||
|
|
|
@ -39,6 +39,9 @@ type
|
||||||
XsltArguments* {.preservesRecord: "xslt".} = object
|
XsltArguments* {.preservesRecord: "xslt".} = object
|
||||||
`field0`*: XsltArgumentsField0
|
`field0`*: XsltArgumentsField0
|
||||||
|
|
||||||
|
HttpClientStepDetail* {.preservesDictionary.} = object
|
||||||
|
`response-content-type-override`*: string
|
||||||
|
|
||||||
JsonSocketTranslatorStepField0* {.preservesDictionary.} = object
|
JsonSocketTranslatorStepField0* {.preservesDictionary.} = object
|
||||||
`socket`*: SocketAddress
|
`socket`*: SocketAddress
|
||||||
|
|
||||||
|
@ -51,10 +54,8 @@ type
|
||||||
FileSystemUsageArguments* {.preservesRecord: "file-system-usage".} = object
|
FileSystemUsageArguments* {.preservesRecord: "file-system-usage".} = object
|
||||||
`field0`*: FileSystemUsageArgumentsField0
|
`field0`*: FileSystemUsageArgumentsField0
|
||||||
|
|
||||||
HttpClientStepField0* {.preservesDictionary.} = object
|
|
||||||
|
|
||||||
HttpClientStep* {.preservesRecord: "http-client".} = object
|
HttpClientStep* {.preservesRecord: "http-client".} = object
|
||||||
`field0`*: HttpClientStepField0
|
`detail`*: HttpClientStepDetail
|
||||||
|
|
||||||
HttpDriverStepField0* {.preservesDictionary.} = object
|
HttpDriverStepField0* {.preservesDictionary.} = object
|
||||||
|
|
||||||
|
@ -109,6 +110,7 @@ type
|
||||||
proc `$`*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments |
|
proc `$`*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments |
|
||||||
SqliteStep |
|
SqliteStep |
|
||||||
XsltArguments |
|
XsltArguments |
|
||||||
|
HttpClientStepDetail |
|
||||||
JsonSocketTranslatorStep |
|
JsonSocketTranslatorStep |
|
||||||
FileSystemUsageArguments |
|
FileSystemUsageArguments |
|
||||||
HttpClientStep |
|
HttpClientStep |
|
||||||
|
@ -127,6 +129,7 @@ proc `$`*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments |
|
||||||
proc encode*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments |
|
proc encode*(x: JsonTranslatorArguments | SocketAddress | Base64DecoderArguments |
|
||||||
SqliteStep |
|
SqliteStep |
|
||||||
XsltArguments |
|
XsltArguments |
|
||||||
|
HttpClientStepDetail |
|
||||||
JsonSocketTranslatorStep |
|
JsonSocketTranslatorStep |
|
||||||
FileSystemUsageArguments |
|
FileSystemUsageArguments |
|
||||||
HttpClientStep |
|
HttpClientStep |
|
||||||
|
|
Loading…
Reference in New Issue