Eduardo Narros
10/13/2022, 8:36 AMPhilipp Mayer
10/14/2022, 10:55 AMJippe Holwerda
10/14/2022, 11:14 AMprivate fun writeToDisk(part: StreamingPart, bytes: ByteArray, length: Int) =
File.createTempFile(part.fileName ?: UUID.randomUUID().toString()
+ "-", ".tmp", temporaryFileDirectory).apply {
deleteOnExit()
FileOutputStream(this).apply {
write(bytes, 0, length)
use { part.inputStream.copyTo(it, writeToDiskThreshold) }
}
}
with the deleteOnExit()
call.
We want to keep the file around even when the JVM exits (we're running on k8s so pods can always be killed).
Would it be a good idea to make this behaviour configurable, i.e. through a flag that is passed from MultipartFormBody.from
to the MultipartFormParser
?
If so, I'll make a merge request... Happy to hear your thoughts.Andrew O'Hara
10/25/2022, 6:14 PM4.33.1.0
version? It doesn't seem to actually break anything, but the volume of error logs are a problem.
UT005013: An IOException occurred
java.io.IOException: UT000034: Stream is closed
at io.undertow.channels.DetachableStreamSourceChannel.getOption(DetachableStreamSourceChannel.java:200)
at io.undertow.io.UndertowInputStream.<init>(UndertowInputStream.java:71)
at io.undertow.server.HttpServerExchange$DefaultBlockingHttpExchange.getInputStream(HttpServerExchange.java:2001)
at io.undertow.server.HttpServerExchange.getInputStream(HttpServerExchange.java:1615)
at org.http4k.server.Http4kUndertowHttpHandler.asRequest(Http4kUndertowHttpHandler.kt:34)
at org.http4k.server.Http4kUndertowHttpHandler.handleRequest(Http4kUndertowHttpHandler.kt:37)
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:391)
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:859)
at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
at org.xnio.XnioWorker$WorkerThreadFactory$1$1.run(XnioWorker.java:1282)
at java.base/java.lang.Thread.run(Thread.java:833)
dave
10/27/2022, 8:04 PMMikael Ståldal
10/28/2022, 9:45 AMResponse
with JSON from a string? This seems to work:
Response(Status.OK).header("Content-Type", ContentType.APPLICATION_JSON.value).body("""{"foo":"bar","baz":17}""")
but is there some nicer way?Mikael Ståldal
11/03/2022, 1:24 PMjava.lang.OutOfMemoryError
when using a lens, since it reads the whole payload into memory before parsing it:
class StreamBody(override val stream: InputStream, override val length: Long? = null) : Body {
override val payload: ByteBuffer by lazy { stream.use { ByteBuffer.wrap(it.readBytes()) } }
// ...
}
It would be nice to be able to specify a max payload size.xuemin.guan
11/07/2022, 7:55 PMHTTPHandler
interface which can make testing easier (it is just an idea atm, not sure if this even makes sense). Do people have any other tips? Like what do you use for testing? Anything nice you use for recording and replying requests and responses?
Thank you 🙏Andrew O'Hara
11/08/2022, 4:35 PMhttp4k-format
module? Or has there just been no interest up until now?Ellen Spertus
11/13/2022, 9:29 PMcurl -A "ellens-user-agent" -X POST -d 'grant_type=password&username=USER&password=PASSWORD' --user 'SCRIPTID:SECRET' <https://www.reddit.com/api/v1/access_token>
How do I convert that into an http4k
request?Bear MAN
11/16/2022, 7:25 AMhttp4k-contract
From http4k's documentation https://www.http4k.org/guide/reference/contracts/, the following example is shown
// this route has a dynamic path segment
fun greetRoute(): ContractRoute {
// these lenses define the dynamic parts of the request that will be used in processing
val ageQuery = <http://Query.int|Query.int>().required("age")
val stringBody = Body.string(TEXT_PLAIN).toLens()
// this specifies the route contract, with the desired contract of path, headers, queries and body parameters.
val spec = "/greet" / Path.of("name") meta {
summary = "tells the user hello!"
queries += ageQuery
receiving(stringBody)
} bindContract GET
// the this function will dynamically supply a new HttpHandler for each call. The number of parameters
// matches the number of dynamic sections in the path (1)
fun greet(nameFromPath: String): HttpHandler = { request: Request ->
val age = ageQuery(request)
val sentMessage = stringBody(request)
Response(OK).with(stringBody of "hello $nameFromPath you are $age. You sent $sentMessage")
}
return spec to ::greet
}
The above example is fine for path with Path Lens as the last 'path component', however, for path that has string as the last 'path component', I must include a extra parameter for function greet
, e.g.
// this route has a dynamic path segment
fun greetRoute(): ContractRoute {
// these lenses define the dynamic parts of the request that will be used in processing
val ageQuery = <http://Query.int|Query.int>().required("age")
val stringBody = Body.string(TEXT_PLAIN).toLens()
// this specifies the route contract, with the desired contract of path, headers, queries and body parameters.
val spec = "/greet" / Path.of("name") / "foo" meta {
summary = "tells the user hello!"
queries += ageQuery
receiving(stringBody)
} bindContract GET
// the this function will dynamically supply a new HttpHandler for each call. The number of parameters
// matches the number of dynamic sections in the path (1)
fun greet(nameFromPath: String, foo: String): HttpHandler = { request: Request ->
val age = ageQuery(request)
val sentMessage = stringBody(request)
Response(OK).with(stringBody of "hello $nameFromPath you are $age. You sent $sentMessage")
}
return spec to ::greet
}
foo
's value is always "foo"
in this case
According to Stack Overflow https://stackoverflow.com/questions/53278208/how-do-you-model-a-path-parameter-in-the-middle-with-http4k, it seems that the inclusion of an extra parameter is unavoidable. I also checked http4k internal implementation, and class ContractRouteSpec2
is the reason behind the required foo: String
.
I may replace greet
with a lambda and put _: String
instead of foo: String
, but just before I do the refactoring, I wanna make sure it is the intended way to handle this problem.
Sorry for the superrrrr long question, and thank you reading it all the way here.Mikael Ståldal
11/17/2022, 7:20 AMAndrew O'Hara
11/21/2022, 6:52 PMAndrew O'Hara
11/24/2022, 6:00 PMWsFilter
only works with a Websocket
that's already open. Is the only alternative to close the Websocket
immediately after failing to validate the authorization?Ryunos
11/25/2022, 3:38 PMBear MAN
11/28/2022, 7:48 AMhttp4k-contract
, is it possible to generate array of enum query parameter, e.g.
"schema": {
"type": "array",
"items":{
"type": "string",
"enum": ["foo", "soo", "doo", "coo"]
}
}
dave
12/02/2022, 6:41 AMBob Glamm
12/05/2022, 10:07 PMroutes(
"/projects" bind routes(
"/{projectId:\\d+}" bind { ... parse projectId here, yielding a RoutingHttpHandler that can access something constructed from the projectId ... }
)
)
This pattern would be nested several more times: /project/{projectId}/workspace/{workspaceId}/snippet/{snippetId}
, for instancedave
12/09/2022, 5:22 PMhttps://youtu.be/JgmzgsNYgXg▾
dave
12/09/2022, 5:25 PMFernando
12/15/2022, 12:06 PMFernando
12/15/2022, 12:22 PMorg.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/http4k/core/BodyMode$Stream
, can you help me, please?s4nchez
12/15/2022, 12:33 PMs4nchez
12/15/2022, 12:34 PMs4nchez
12/15/2022, 1:45 PMdave
12/18/2022, 3:32 PMAndrew O'Hara
01/10/2023, 2:22 AMval googleSecurity = ImplicitOAuthSecurity(
Uri.of("<https://accounts.google.com/o/oauth2/v2/auth>"),
listOf(OAuthScope("email")),
Filter.NoOp // todo
)
val api = contract {
renderer = OpenApi3(ApiInfo("My Api", "1")) // jackson renderer
descriptionPath = "/openapi"
routes += generateRoutes()
security = googleSecurity
}
val ui = swaggerUi(
descriptionRoute = Uri.of("/openapi"),
displayOperationId = true,
requestSnippetsEnabled = true
)
routes(api, ui)
.asServer(SunHttp(8000))
.start()
I can get as far as logging in to google and then being redirected back to <app_host>/oauth2-redirect.html
, but then my app obviously doesn't have this /oauth2-redirect.html
path that the Swagger UI seems to have chosen for me.Mikael Ståldal
01/13/2023, 10:36 AMStatus.UNKNOWN_HOST
and Status.CONNECTION_REFUSED
are equal, is that intentional?
Try this:
fun main() {
println(Status.SERVICE_UNAVAILABLE == Status.UNKNOWN_HOST)
println(Status.UNKNOWN_HOST == Status.CONNECTION_REFUSED)
}
Gives:
false
true
The reason is that they have the same code
(503) and both are clientGenerated
.dave
02/04/2023, 6:52 PMAsif Ahmed
02/10/2023, 5:06 AMAsif Ahmed
02/10/2023, 5:06 AMdave
02/10/2023, 6:19 AMAsif Ahmed
02/10/2023, 6:26 AMdave
02/10/2023, 6:28 AMAsif Ahmed
02/10/2023, 6:43 AM