Cies
06/12/2025, 8:40 AMhs256 algorithm that Supabase uses?Cies
06/12/2025, 8:42 AMPaul Reijbroek
06/13/2025, 6:52 PMDanielZ
06/13/2025, 8:23 PMconst val USE_REAL_CLIENT = false
fun main() {
val env = Environment.defaults(
AWS_REGION of Region.EU_WEST_1,
AWS_ROLE_ARN of ARN.of("arn:aws:sts:us-east-1:000000000001:role:myrole")
)
val http: HttpHandler = if (USE_REAL_CLIENT) JavaHttpClient() else FakeS3().debug()
val credentialsProvider = CredentialsProvider.STS(env, FakeSTS().debug())
val s3 = S3.Http(credentialsProvider = credentialsProvider, http)
val sourceName = BucketName.of("source-bucket")
val targetName = BucketName.of("target-bucket")
s3.createBucket(sourceName, env[AWS_REGION]).recover(RemoteFailure::throwIt) // <<< fails here: env 'AWS_ACCESS_KEY_ID' is required
s3.createBucket(targetName, env[AWS_REGION]).recover(RemoteFailure::throwIt)
val source = S3Bucket.Http(sourceName, env[AWS_REGION], credentialsProvider, http)
source.putObject(BucketKey.of("hello"), "hello ".byteInputStream()).recover(RemoteFailure::throwIt)
val target = S3Bucket.Http(targetName, env[AWS_REGION], credentialsProvider, http)
target.copyObject(source.bucketName, BucketKey.of("hello"), BucketKey.of("copy"))
.recover(RemoteFailure::throwIt)
}Tudor Luca
06/14/2025, 7:14 PMdave
06/18/2025, 12:27 PMdave
06/20/2025, 9:10 PMleonhardt
06/30/2025, 4:02 AMsinglePageApp helper be used on a subpath? When it's configured on the root path, it works as expected. But when it's configured on a subpath, it still seems to match any path, even ones outside the subpath.Marco Garofalo
06/30/2025, 3:42 PM<http://whatever/me>) or propagate the token down the handlers chain/stack once the authFilter is satisfied.leonhardt
07/12/2025, 5:38 PMorg.http4k:http4k-testing-approval in conjunction with your Okey-doke plugin for a couple months now. The tool + plugin has definitely a huge improvement over renaming the generated files manually, but now that we've been living with the process for a while a few DX issues have surfaced:
1. It's hard to know if the keyboard shortcut (Ctrl + F11) worked. You have to have the cursor focused in the right place. Precise placement in the editor on the test or in the project viewer on the file both work, but (perhaps unintuitively) the test execution's run window/console does not work. And there's no visual indication if it worked, beyond navigating to the file in the project viewer. As a result I've noticed we just hit Ctrl + F11 obsessively in every window "just to make sure."
2. When we rename tests, the generated approval files get abandoned. We often don't notice initially, and only after some time notice when there's many more approval files than tests. The process to clean up the clutter is tricky. We usually make sure all our tests are passing, commit, delete all the approval files, rerun tests, approve all the approval files, and commit again. But this is a little error-prone and tedious.
When we only had 1-20 tests, these weren't big issues. But now that we're in the 100s it's painful enough issue we're looking to address it. Thought we'd check with the community here. Can anybody recommend any better ways to work with org.http4k:http4k-testing-approval that we might have missed?ted
07/28/2025, 11:29 AMCies Breijs
08/07/2025, 3:42 AM@RequiredPermissions(Perm.SEE_GOD, Perm.HEAR_GOD, Perm.FEEL_LOVE) // or: @RequiresNoPermissions
fun ultimateExperienceGetHandler(req: Request): Resposne { /* ... */ }
The were serialized to the db's user_permissions table (with columns: userId, permEnum). Kind of worked well.
I liked that all handlers required such annotation (created some test for that in the test suite).
It feels like in http4k the best place to implement this is simply in the handler body. Functional. No annofuzz...
(But please correct me if I'm wrong about this)
fun ultimateExperienceGetHandler(req: Request): Resposne {
if (!currentUserCtx(req).permissions.contains(Perm.SEE_GOD)) return Response(FORBIDDEN)
// ...
}
But there's one thing I miss: the "closed by default" that I had with the annotations.
So I wondered, would it be possible to have some sort of "closed by default" back by somehow specifying them in the router...
Then:
val portalRouter = routes(
Paths.dashboard bind GET to ::dashboardGetHandler,
Paths.profileEdit bind POST to ::profilePostHandler,
Paths.profileEdit bind GET to ::profileGetHandler,
Paths.retailerList bind GET to ::retailerListGetHandler,
Paths.retailerView bind GET to ::retailerViewGetHandler,
// ...
)
Would become something like:
val portalRouter = authorizedRoutes(
Triple(Paths.dashboard, GET to ::dashboardGetHandler, listOf(Perm.SEE_DASHBOARD)),
Triple(Paths.profileEdit, POST to ::profilePostHandler, listOf(Perm.MODIFY_MY_PROFILE)),
Triple(Paths.profileEdit, GET to ::profileGetHandler, listOf(Perm.MODIFY_MY_PROFILE)),
// ...
)
Or even methods on the UserPermissions object like:
val portalRouter = authorizedRoutes(
Triple(Paths.dashboard, GET to ::dashboardGetHandler, listOf(::seeDashboard)),
Triple(Paths.profileEdit, POST to ::profilePostHandler, listOf(::modifyMyProfile)),
Triple(Paths.profileEdit, GET to ::profileGetHandler, listOf(::userHasOrganizationWithTypeX)), // <--
// ...
)
Maybe my question is: how are y'all implementing authorization? (give that i need a very simple scheme for the foreseeable future)Cies Breijs
08/08/2025, 10:29 AMUriTemplate to build paths in a slightly more typesafe way.
When looking at it's implementation I kind of wish for there to be a version of this function that throws an error if not all parameters are filled. Like generateOrThrow . This may help in some cases where you forget to pass a parameter (and I cannot forsee any situation where you do not want a parameter filled in).
Am I on to something, or is this is stupid idea?
Here the original function:Slackbot
08/14/2025, 2:10 PMMarco Garofalo
08/22/2025, 11:12 AMAndrew O'Hara
09/03/2025, 5:09 PMAlessandro Ciccimarra
09/08/2025, 8:10 PMJavaHttpClient, some IOExceptions are caught (UnknownHostException, ConnectException, HttpTimeoutException).
Other exceptions, for example a SocketException caused by a connection reset, need to be handled at call site.
What’s the reason for that?almeydajuan
09/09/2025, 2:05 PMalmeydajuan
09/10/2025, 7:03 AMBearerAuthSecurity.
I have 2 example implementations:
class Lookup(lookup: (String) -> String?) : Security by BearerAuthSecurity({ lookup(it) != null }), TokenSecurity
class WithKey(lookup: (String) -> String?) :
Security by BearerAuthSecurity(key = Header.required("Authorization"), lookup = { lookup(it) }), TokenSecurity
Then if I do:
val http1: HttpHandler = newBackend(tokenSecurity = WithKey { "hello" })
val http2: HttpHandler = newBackend(tokenSecurity = Lookup { "hello" })
Request(GET, "/hello").header("Authorization", "Bearer 123").use(http1)
Request(GET, "/hello").header("Authorization", "Bearer 123").use(http2)
The backend which uses security in with only lookup receives the bearer header, but the in the call to the 2nd backend (with the required header) Authorization headers is overriden and I get Authorization hello instead of Authorization Bearer 123
For me it is unintuitive because we are using the same BearerAuthSecurity but depending on which parameter we pass, the request that arrives to our system comes already modified.
Is this how this should behave? If so, what is the rational behind it?
cc: @Michal WachowskiMikael Ståldal
09/11/2025, 6:50 PMAndrew O'Hara
09/14/2025, 12:17 AMCies Breijs
09/22/2025, 1:58 PMdave
10/01/2025, 2:01 PMRafael Diaz
10/05/2025, 12:59 AMList<ContractRoute>. However, I'm ending up with the type List<Pair<ContractRouteSpec2<Int, String>.Binder, (Request) -> Response>> when I add lenses to the route path.
I want to add the list of routes to an api contract, which accepts the former type but not the latter, so I was wondering how I could fix it.
More details inside:
Edit: Found the solution.
https://stackoverflow.com/questions/53278208/how-do-you-model-a-path-parameter-in-the-middle-with-http4kRafael Diaz
10/05/2025, 5:36 AMAndrew O'Hara
10/07/2025, 1:43 AMRafael Diaz
10/19/2025, 2:57 AMSteven Webber
10/21/2025, 9:02 AMval customJetty = jettyServer(maxRequestThreads = 200, meterRegistry = prometheusMeterRegistry)
myService.asK8sServer(serverConfig = customJetty, environment, healthApp = healthApp()).start()
With a Quarkus based framework I'd switch to virtual threads using an annotation and that solves the issue.
For http4K, should we switch to a different http server or change config for the existing one? Is the recommended approach to try and use java virtual threads?David Beer
10/23/2025, 3:04 PMclass JettyConfig(private val port: Int) : ServerConfig {
override fun toServer(http: HttpHandler): Http4kServer {
val server = Server().apply {
insertHandler(http.toJettyHandler())
}
return object : Http4kServer {
override fun start(): Http4kServer = apply { server.start() }
override fun stop(): Http4kServer = apply { server.stop() }
override fun port(): Int = port
}
}
}
And Called using App.app.asServer(JettyConfig(App.port)).start()DanielZ
10/28/2025, 6:23 PMhttp4k-connect-amazon-s3 for copying files from one s3 bucket to another. Now I'm facing the situation to deal with some huge files (>5GB). Is it somehow possible to copy those file using http4k-connect-amazon-s3 or do I have to use aws sdk?
Background: I'm aware that CopyObject has a limitation of 5GB, so I tried GetObject and PutObject which fails OOM in StreamBody.
Any ideas? Do you have any experience with such cases?