From 4807106b5ed77985d9af1bb13fba65039f7731fe Mon Sep 17 00:00:00 2001 From: <> Date: Mon, 19 Aug 2024 15:03:55 +0000 Subject: [PATCH] Deployed 091d957 with MkDocs version: 1.2.3 --- documentation/index.html | 2 +- .../arrange_filters_into_stacks/index.html | 2 +- .../attach_context_to_a_request/index.html | 2 +- guide/howto/client_as_a_function/index.html | 2 +- .../configure_an_oauth_server/index.html | 2 +- .../index.html | 2 +- guide/howto/create_a_swagger_ui/index.html | 4 ++-- .../customise_a_server_backend/index.html | 2 +- guide/howto/deploy_webjars/index.html | 2 +- guide/howto/integrate_with_openapi/index.html | 2 +- guide/howto/leverage_graphql/index.html | 2 +- .../howto/lookup_a_user_principal/index.html | 2 +- guide/howto/make_json_faster/index.html | 4 ++-- guide/howto/monitor_http4k/index.html | 2 +- guide/howto/nestable_routes/index.html | 2 +- .../record_and_replay_http_traffic/index.html | 2 +- guide/howto/secure_and_auth_http/index.html | 2 +- .../index.html | 2 +- guide/howto/serve_sse/index.html | 2 +- guide/howto/serve_websockets/index.html | 2 +- guide/howto/server_as_a_function/index.html | 2 +- guide/howto/simple_routing/index.html | 2 +- .../index.html | 2 +- .../typesafe_your_api_with_lenses/index.html | 2 +- .../use_a_custom_oauth_provider/index.html | 2 +- guide/howto/use_a_server_backend/index.html | 2 +- .../howto/use_a_templating_engine/index.html | 2 +- .../use_auto_content_negotiation/index.html | 4 ++-- guide/howto/use_html_forms/index.html | 2 +- guide/howto/use_multipart_forms/index.html | 2 +- guide/reference/approvaltests/index.html | 2 +- guide/reference/aws/index.html | 2 +- guide/reference/chaos/index.html | 2 +- guide/reference/clients/index.html | 2 +- guide/reference/cloud_events/index.html | 4 ++-- guide/reference/cloud_native/index.html | 2 +- guide/reference/config/index.html | 2 +- guide/reference/contracts/index.html | 2 +- guide/reference/core/index.html | 2 +- guide/reference/dataframe/index.html | 2 +- guide/reference/digest/index.html | 2 +- guide/reference/failsafe/index.html | 2 +- guide/reference/graphql/index.html | 2 +- guide/reference/hamkrest/index.html | 2 +- guide/reference/htmx/index.html | 2 +- guide/reference/json/index.html | 2 +- guide/reference/jsonrpc/index.html | 2 +- guide/reference/kotest/index.html | 2 +- guide/reference/micrometer/index.html | 2 +- guide/reference/multipart/index.html | 2 +- guide/reference/oauth/index.html | 2 +- guide/reference/opentelemetry/index.html | 2 +- guide/reference/playwright/index.html | 2 +- guide/reference/resilience4j/index.html | 2 +- guide/reference/serverless/index.html | 2 +- guide/reference/servers/index.html | 2 +- .../servicevirtualisation/index.html | 2 +- guide/reference/strikt/index.html | 2 +- guide/reference/templating/index.html | 2 +- guide/reference/webdriver/index.html | 2 +- guide/reference/webhooks/index.html | 2 +- guide/reference/xml/index.html | 2 +- guide/reference/yaml/index.html | 2 +- quickstart/index.html | 2 +- search/search_index.json | 2 +- sitemap.xml.gz | Bin 1161 -> 1161 bytes 66 files changed, 69 insertions(+), 69 deletions(-) diff --git a/documentation/index.html b/documentation/index.html index 2a3e5cd9a..b8741a168 100644 --- a/documentation/index.html +++ b/documentation/index.html @@ -1452,7 +1452,7 @@
To install, add these dependencies to your Gradle file:
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-server-jetty")
implementation("org.http4k:http4k-client-okhttp")
diff --git a/guide/howto/arrange_filters_into_stacks/index.html b/guide/howto/arrange_filters_into_stacks/index.html
index b012a7c6e..85063ad3c 100644
--- a/guide/howto/arrange_filters_into_stacks/index.html
+++ b/guide/howto/arrange_filters_into_stacks/index.html
@@ -1203,7 +1203,7 @@ ClientsideGradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/attach_context_to_a_request/index.html b/guide/howto/attach_context_to_a_request/index.html
index 5e178830a..37eaf51f8 100644
--- a/guide/howto/attach_context_to_a_request/index.html
+++ b/guide/howto/attach_context_to_a_request/index.html
@@ -1183,7 +1183,7 @@ Attach context to a request
the shared bag of state for each request, and to remove the state after the request is complete.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/client_as_a_function/index.html b/guide/howto/client_as_a_function/index.html
index 18fd98c07..49a0714ab 100644
--- a/guide/howto/client_as_a_function/index.html
+++ b/guide/howto/client_as_a_function/index.html
@@ -1174,7 +1174,7 @@ Basic: Client as a function
This example demonstrates using http4k as a client, to consume HTTP services. A client is just another HttpHandler.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/configure_an_oauth_server/index.html b/guide/howto/configure_an_oauth_server/index.html
index fbcf00050..80c002d2e 100644
--- a/guide/howto/configure_an_oauth_server/index.html
+++ b/guide/howto/configure_an_oauth_server/index.html
@@ -1173,7 +1173,7 @@ Configure an OAuth Server
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-security-oauth")
}
diff --git a/guide/howto/create_a_custom_json_marshaller/index.html b/guide/howto/create_a_custom_json_marshaller/index.html
index a624a3696..f55233499 100644
--- a/guide/howto/create_a_custom_json_marshaller/index.html
+++ b/guide/howto/create_a_custom_json_marshaller/index.html
@@ -1173,7 +1173,7 @@ Create a custom JSON marshaller
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-format-jackson")
}
diff --git a/guide/howto/create_a_swagger_ui/index.html b/guide/howto/create_a_swagger_ui/index.html
index e8ca5e0d7..7eca276c7 100644
--- a/guide/howto/create_a_swagger_ui/index.html
+++ b/guide/howto/create_a_swagger_ui/index.html
@@ -1229,7 +1229,7 @@ Build the UI
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-contract")
}
@@ -1276,7 +1276,7 @@ Bundle the UI with WebjarsInstallation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-contract-ui-swagger")
implementation("org.http4k:http4k-contract-ui-redoc")
}
diff --git a/guide/howto/customise_a_server_backend/index.html b/guide/howto/customise_a_server_backend/index.html
index 7134deec3..b26917ee8 100644
--- a/guide/howto/customise_a_server_backend/index.html
+++ b/guide/howto/customise_a_server_backend/index.html
@@ -1175,7 +1175,7 @@ How to write a custom server
Whilst the http4k server modules ship with a sensibly configured standard server-backend setup, a lot of projects will require specialised implementations of the underlying server backend. http4k makes this easy with the ServerConfig
interface.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-server-jetty")
}
diff --git a/guide/howto/deploy_webjars/index.html b/guide/howto/deploy_webjars/index.html
index 56edcb80e..fd2c9305c 100644
--- a/guide/howto/deploy_webjars/index.html
+++ b/guide/howto/deploy_webjars/index.html
@@ -1173,7 +1173,7 @@ Deploy WebJars
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
// for the example...
diff --git a/guide/howto/integrate_with_openapi/index.html b/guide/howto/integrate_with_openapi/index.html
index 6d5bc73e3..d4133de04 100644
--- a/guide/howto/integrate_with_openapi/index.html
+++ b/guide/howto/integrate_with_openapi/index.html
@@ -1181,7 +1181,7 @@ Integrate with OpenAPI
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-contract")
implementation("org.http4k:http4k-format-argo")
diff --git a/guide/howto/leverage_graphql/index.html b/guide/howto/leverage_graphql/index.html
index 52668bcd3..2fb1ae45f 100644
--- a/guide/howto/leverage_graphql/index.html
+++ b/guide/howto/leverage_graphql/index.html
@@ -1173,7 +1173,7 @@ Leverage GraphQL
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-graphql")
}
diff --git a/guide/howto/lookup_a_user_principal/index.html b/guide/howto/lookup_a_user_principal/index.html
index f9e01c989..965297c28 100644
--- a/guide/howto/lookup_a_user_principal/index.html
+++ b/guide/howto/lookup_a_user_principal/index.html
@@ -1173,7 +1173,7 @@ Lookup a User Principal
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/make_json_faster/index.html b/guide/howto/make_json_faster/index.html
index d269a3512..666782e17 100644
--- a/guide/howto/make_json_faster/index.html
+++ b/guide/howto/make_json_faster/index.html
@@ -1190,7 +1190,7 @@ Gradle}
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-format-kotlinx-serialization")
}
@@ -1239,7 +1239,7 @@ Moshi Metadata Reflect
Gradle¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-format-moshi") {
exclude("org.jetbrains.kotlin", "kotlin-reflect") // Exclude kotlin-reflect
}
diff --git a/guide/howto/monitor_http4k/index.html b/guide/howto/monitor_http4k/index.html
index c928c5ce4..6f41d3ec7 100644
--- a/guide/howto/monitor_http4k/index.html
+++ b/guide/howto/monitor_http4k/index.html
@@ -1174,7 +1174,7 @@ Monitor http4k
Measuring performance of application estate is crucial in today's microservice world - it is crucial that dev-ops enabled teams can monitor, react and scale dynamically to changes in the runtime environment. However, because of the plethora of monitoring tools on the market, and because http4k is a toolkit and not a complete "batteries included" framework, it provides a number of integration points to enable monitoring systems to be plugged in as required. Additionally, it is envisaged that users will probably want to provide their own implementations of the http4k ServerConfig
classes (Jetty
, Undertow
etc..) so that tweaking and tuning to their exact requirements is accessible, instead of http4k attempting to provide some generic configuration API to achieve it.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-metrics-micrometer")
}
diff --git a/guide/howto/nestable_routes/index.html b/guide/howto/nestable_routes/index.html
index 13fa267fe..7ea9d49e9 100644
--- a/guide/howto/nestable_routes/index.html
+++ b/guide/howto/nestable_routes/index.html
@@ -1209,7 +1209,7 @@ Dynamic Paths / Path Variables will match www.example.com/news/2018/05/26
, making request.path("date")
equal to 2018/05/26
. This may be exactly what you want, or it may produce unexpected results, depending on how your URLs are structured.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/record_and_replay_http_traffic/index.html b/guide/howto/record_and_replay_http_traffic/index.html
index 6ebfe48f0..5d2cf08fd 100644
--- a/guide/howto/record_and_replay_http_traffic/index.html
+++ b/guide/howto/record_and_replay_http_traffic/index.html
@@ -1174,7 +1174,7 @@ Record & replay HTTP traffic
A set of classes to provide simple recording/replaying of HTTP traffic. This is perfect for testing purposes, or in short lived, low traffic environments where no proper caches are available.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/secure_and_auth_http/index.html b/guide/howto/secure_and_auth_http/index.html
index 538ad3077..d89f28428 100644
--- a/guide/howto/secure_and_auth_http/index.html
+++ b/guide/howto/secure_and_auth_http/index.html
@@ -1173,7 +1173,7 @@ Secure and auth HTTP services
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
// for OAuth examples
diff --git a/guide/howto/self_document_systems_with_tests/index.html b/guide/howto/self_document_systems_with_tests/index.html
index 9d86c4c07..3c5214244 100644
--- a/guide/howto/self_document_systems_with_tests/index.html
+++ b/guide/howto/self_document_systems_with_tests/index.html
@@ -1173,7 +1173,7 @@ Self document systems with tests
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
testImplementation("org.http4k:http4k-testing-tracerbullet")
}
diff --git a/guide/howto/serve_sse/index.html b/guide/howto/serve_sse/index.html
index 7bb86b211..734d26a5e 100644
--- a/guide/howto/serve_sse/index.html
+++ b/guide/howto/serve_sse/index.html
@@ -1173,7 +1173,7 @@ Serve Server-Sent Events
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-server-undertow")
implementation("org.http4k:http4k-server-jetty")
diff --git a/guide/howto/serve_websockets/index.html b/guide/howto/serve_websockets/index.html
index 3e3dc6fdd..ef507248e 100644
--- a/guide/howto/serve_websockets/index.html
+++ b/guide/howto/serve_websockets/index.html
@@ -1173,7 +1173,7 @@ Serve Websockets
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-server-undertow")
implementation("org.http4k:http4k-client-websocket")
diff --git a/guide/howto/server_as_a_function/index.html b/guide/howto/server_as_a_function/index.html
index ad75f78ae..0a5e40ab5 100644
--- a/guide/howto/server_as_a_function/index.html
+++ b/guide/howto/server_as_a_function/index.html
@@ -1174,7 +1174,7 @@ Basic: Server as a function
This example is the simplest possible "server" implementation. Note that we are not spinning up a server-backend here - but the entire application(!) is testable by firing HTTP requests at it as if it were.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/simple_routing/index.html b/guide/howto/simple_routing/index.html
index a42469fb6..6acdd3f12 100644
--- a/guide/howto/simple_routing/index.html
+++ b/guide/howto/simple_routing/index.html
@@ -1175,7 +1175,7 @@ Basic: Simple routing
For the typesafe contract-style routing, refer to this recipe instead,
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/structure_your_logs_with_events/index.html b/guide/howto/structure_your_logs_with_events/index.html
index f257d6ff7..f9a8f2861 100644
--- a/guide/howto/structure_your_logs_with_events/index.html
+++ b/guide/howto/structure_your_logs_with_events/index.html
@@ -1173,7 +1173,7 @@ Structure your logs with Events
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-format-jackson")
}
diff --git a/guide/howto/typesafe_your_api_with_lenses/index.html b/guide/howto/typesafe_your_api_with_lenses/index.html
index 6cefd5bb5..df20abfd8 100644
--- a/guide/howto/typesafe_your_api_with_lenses/index.html
+++ b/guide/howto/typesafe_your_api_with_lenses/index.html
@@ -1174,7 +1174,7 @@ Typesafe your API with lenses
Example showing how to create and apply lenses to requests and responses to both extract and inject typesafe values out of and into HTTP messages. Note that since the http4k Request/Response
objects are immutable, all injection occurs via copy.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/use_a_custom_oauth_provider/index.html b/guide/howto/use_a_custom_oauth_provider/index.html
index f02c2da54..d4217bad1 100644
--- a/guide/howto/use_a_custom_oauth_provider/index.html
+++ b/guide/howto/use_a_custom_oauth_provider/index.html
@@ -1174,7 +1174,7 @@ Use a custom OAuth Provider
It is very easy to configure http4k to integrate with any OAuth2 provider who supports the Authorisation Code Grant.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-security-oauth")
}
diff --git a/guide/howto/use_a_server_backend/index.html b/guide/howto/use_a_server_backend/index.html
index 89ce2d77a..4e0e2cd3d 100644
--- a/guide/howto/use_a_server_backend/index.html
+++ b/guide/howto/use_a_server_backend/index.html
@@ -1175,7 +1175,7 @@ Basic: Use a Server backend
Alternatively, any http4k application can be mounted into any Servlet container using the asServlet()
extension method. This is the mechanism used in the Jetty implementation.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-client-apache")
implementation("org.http4k:http4k-server-jetty")
diff --git a/guide/howto/use_a_templating_engine/index.html b/guide/howto/use_a_templating_engine/index.html
index 843ca772d..621d073d6 100644
--- a/guide/howto/use_a_templating_engine/index.html
+++ b/guide/howto/use_a_templating_engine/index.html
@@ -1174,7 +1174,7 @@ Use a templating engine
Example showing how to use the Templating modules - in this case Handlebars, both by standard response manipulation and via a typesafe view lens.
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-template-handlebars")
}
diff --git a/guide/howto/use_auto_content_negotiation/index.html b/guide/howto/use_auto_content_negotiation/index.html
index 17f763dab..71d8463ae 100644
--- a/guide/howto/use_auto_content_negotiation/index.html
+++ b/guide/howto/use_auto_content_negotiation/index.html
@@ -1175,14 +1175,14 @@ Use Auto Content Negotiation
Gradle setup¶
Auto Content Negotiation is available in the core http4k module.
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-format-core")
}
But it also integrates with the contract module.
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-contract")
}
diff --git a/guide/howto/use_html_forms/index.html b/guide/howto/use_html_forms/index.html
index 736a8fd0a..9bb42a5f1 100644
--- a/guide/howto/use_html_forms/index.html
+++ b/guide/howto/use_html_forms/index.html
@@ -1178,7 +1178,7 @@ Use HTML forms
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/howto/use_multipart_forms/index.html b/guide/howto/use_multipart_forms/index.html
index b4d026398..1f02f80bb 100644
--- a/guide/howto/use_multipart_forms/index.html
+++ b/guide/howto/use_multipart_forms/index.html
@@ -1178,7 +1178,7 @@ Use Multipart forms
Gradle setup¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-multipart")
}
diff --git a/guide/reference/approvaltests/index.html b/guide/reference/approvaltests/index.html
index d1b6f8f4f..c54109714 100644
--- a/guide/reference/approvaltests/index.html
+++ b/guide/reference/approvaltests/index.html
@@ -1173,7 +1173,7 @@ Approval Testing
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-approval")
}
diff --git a/guide/reference/aws/index.html b/guide/reference/aws/index.html
index 60624aad5..a4d6df339 100644
--- a/guide/reference/aws/index.html
+++ b/guide/reference/aws/index.html
@@ -1173,7 +1173,7 @@ AWS
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-aws")
}
diff --git a/guide/reference/chaos/index.html b/guide/reference/chaos/index.html
index c35fe2b70..b98ac458f 100644
--- a/guide/reference/chaos/index.html
+++ b/guide/reference/chaos/index.html
@@ -1173,7 +1173,7 @@ Chaos Testing
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-chaos")
}
diff --git a/guide/reference/clients/index.html b/guide/reference/clients/index.html
index e7dd6b613..9a58ec8a4 100644
--- a/guide/reference/clients/index.html
+++ b/guide/reference/clients/index.html
@@ -1173,7 +1173,7 @@ HTTP & Websocket clients
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// Java (for development only):
implementation("org.http4k:http4k-core")
diff --git a/guide/reference/cloud_events/index.html b/guide/reference/cloud_events/index.html
index feec8b58f..5ba912708 100644
--- a/guide/reference/cloud_events/index.html
+++ b/guide/reference/cloud_events/index.html
@@ -1173,7 +1173,7 @@ Cloud Events
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-cloudevents")
}
@@ -1183,7 +1183,7 @@ Installation (Gradle)Example¶
In this example we are using the Jackson JSONFormat which is included by default with the http4k-cloudevents
module. If you want to also use the lenses to access typed EventData, you will also need this in your Gradle file:
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// to access the lenses in the Jackson module
implementation("org.http4k:http4k-format-jackson")
}
diff --git a/guide/reference/cloud_native/index.html b/guide/reference/cloud_native/index.html
index 9fc8cd14d..1296b1ca3 100644
--- a/guide/reference/cloud_native/index.html
+++ b/guide/reference/cloud_native/index.html
@@ -1173,7 +1173,7 @@ Cloud Native Extensions
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-cloudnative")
}
diff --git a/guide/reference/config/index.html b/guide/reference/config/index.html
index 90ddd5f13..cc6ae0576 100644
--- a/guide/reference/config/index.html
+++ b/guide/reference/config/index.html
@@ -1173,7 +1173,7 @@ Typesafe Configuration
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-config")
}
diff --git a/guide/reference/contracts/index.html b/guide/reference/contracts/index.html
index 3c0dc22b4..1ea184c3e 100644
--- a/guide/reference/contracts/index.html
+++ b/guide/reference/contracts/index.html
@@ -1173,7 +1173,7 @@ Typesafe contracts (OpenAPI3)
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-contract")
implementation("org.http4k:http4k-format-<insert json lib>")
}
diff --git a/guide/reference/core/index.html b/guide/reference/core/index.html
index 5a96a4d10..098274c8c 100644
--- a/guide/reference/core/index.html
+++ b/guide/reference/core/index.html
@@ -1173,7 +1173,7 @@ Core
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
}
diff --git a/guide/reference/dataframe/index.html b/guide/reference/dataframe/index.html
index 4ae8a3061..97b632a41 100644
--- a/guide/reference/dataframe/index.html
+++ b/guide/reference/dataframe/index.html
@@ -1173,7 +1173,7 @@ DataFrame
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-format-dataframe")
}
diff --git a/guide/reference/digest/index.html b/guide/reference/digest/index.html
index 190176b56..799e0abb8 100644
--- a/guide/reference/digest/index.html
+++ b/guide/reference/digest/index.html
@@ -1173,7 +1173,7 @@ Digest
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-security-digest")
}
diff --git a/guide/reference/failsafe/index.html b/guide/reference/failsafe/index.html
index 58c0b4d49..f0f5a04ff 100644
--- a/guide/reference/failsafe/index.html
+++ b/guide/reference/failsafe/index.html
@@ -1173,7 +1173,7 @@ Failsafe
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-failsafe")
}
diff --git a/guide/reference/graphql/index.html b/guide/reference/graphql/index.html
index e1117dbda..5c1f24066 100644
--- a/guide/reference/graphql/index.html
+++ b/guide/reference/graphql/index.html
@@ -1173,7 +1173,7 @@ GraphQL
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-graphql")
// for the example below you will also need this dependency...
diff --git a/guide/reference/hamkrest/index.html b/guide/reference/hamkrest/index.html
index b215dddc8..3b2df0b7d 100644
--- a/guide/reference/hamkrest/index.html
+++ b/guide/reference/hamkrest/index.html
@@ -1173,7 +1173,7 @@ Hamkrest
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-hamkrest")
}
diff --git a/guide/reference/htmx/index.html b/guide/reference/htmx/index.html
index 134e1df5e..0af26defe 100644
--- a/guide/reference/htmx/index.html
+++ b/guide/reference/htmx/index.html
@@ -1173,7 +1173,7 @@ htmx
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-htmx")
implementation("org.http4k:http4k-template-handlebars") // Handlebars
diff --git a/guide/reference/json/index.html b/guide/reference/json/index.html
index 6bed35b7a..ba2a42ca3 100644
--- a/guide/reference/json/index.html
+++ b/guide/reference/json/index.html
@@ -1173,7 +1173,7 @@ JSON handling
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// Argo:
implementation("org.http4k:http4k-format-argo")
diff --git a/guide/reference/jsonrpc/index.html b/guide/reference/jsonrpc/index.html
index 85303360a..26c6dcc81 100644
--- a/guide/reference/jsonrpc/index.html
+++ b/guide/reference/jsonrpc/index.html
@@ -1173,7 +1173,7 @@ JSON RPC
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-jsonrpc")
}
diff --git a/guide/reference/kotest/index.html b/guide/reference/kotest/index.html
index 82138c42c..7f1337e41 100644
--- a/guide/reference/kotest/index.html
+++ b/guide/reference/kotest/index.html
@@ -1173,7 +1173,7 @@ Kotest
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-kotest")
}
diff --git a/guide/reference/micrometer/index.html b/guide/reference/micrometer/index.html
index 7c1ad8474..b1a2dfabb 100644
--- a/guide/reference/micrometer/index.html
+++ b/guide/reference/micrometer/index.html
@@ -1173,7 +1173,7 @@ Micrometer
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-metrics-micrometer")
}
diff --git a/guide/reference/multipart/index.html b/guide/reference/multipart/index.html
index 3bdf26d91..ee9a24324 100644
--- a/guide/reference/multipart/index.html
+++ b/guide/reference/multipart/index.html
@@ -1173,7 +1173,7 @@ Multipart forms
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-multipart")
}
diff --git a/guide/reference/oauth/index.html b/guide/reference/oauth/index.html
index e7eab5132..668616874 100644
--- a/guide/reference/oauth/index.html
+++ b/guide/reference/oauth/index.html
@@ -1173,7 +1173,7 @@ OAuth
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-security-oauth")
}
diff --git a/guide/reference/opentelemetry/index.html b/guide/reference/opentelemetry/index.html
index d038c2c3d..eb282b0ba 100644
--- a/guide/reference/opentelemetry/index.html
+++ b/guide/reference/opentelemetry/index.html
@@ -1173,7 +1173,7 @@ OpenTelemetry
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-opentelemetry")
}
diff --git a/guide/reference/playwright/index.html b/guide/reference/playwright/index.html
index 4307da01f..973ca3eaa 100644
--- a/guide/reference/playwright/index.html
+++ b/guide/reference/playwright/index.html
@@ -1173,7 +1173,7 @@ http4k Webdriver Module
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-playwright")
}
diff --git a/guide/reference/resilience4j/index.html b/guide/reference/resilience4j/index.html
index 0ca366308..5ef9f1ca9 100644
--- a/guide/reference/resilience4j/index.html
+++ b/guide/reference/resilience4j/index.html
@@ -1173,7 +1173,7 @@ Resilience4J
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-resilience4j")
}
diff --git a/guide/reference/serverless/index.html b/guide/reference/serverless/index.html
index 6b09aa2d9..f8cddc607 100644
--- a/guide/reference/serverless/index.html
+++ b/guide/reference/serverless/index.html
@@ -1173,7 +1173,7 @@ Serverless backend
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// AWS Lambda:
implementation("org.http4k:http4k-serverless-lambda")
diff --git a/guide/reference/servers/index.html b/guide/reference/servers/index.html
index 80f0f7ea1..474b4496d 100644
--- a/guide/reference/servers/index.html
+++ b/guide/reference/servers/index.html
@@ -1173,7 +1173,7 @@ Server backend
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// Apache v5:
implementation("org.http4k:http4k-server-apache")
diff --git a/guide/reference/servicevirtualisation/index.html b/guide/reference/servicevirtualisation/index.html
index 7fd601c4e..e4d1b9f6a 100644
--- a/guide/reference/servicevirtualisation/index.html
+++ b/guide/reference/servicevirtualisation/index.html
@@ -1173,7 +1173,7 @@ Service Virtualisation
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-servirtium")
}
diff --git a/guide/reference/strikt/index.html b/guide/reference/strikt/index.html
index d8e04fcc7..5cbf28aef 100644
--- a/guide/reference/strikt/index.html
+++ b/guide/reference/strikt/index.html
@@ -1173,7 +1173,7 @@ Strikt
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-strikt")
}
diff --git a/guide/reference/templating/index.html b/guide/reference/templating/index.html
index 1a3f445a8..c9f3dbfeb 100644
--- a/guide/reference/templating/index.html
+++ b/guide/reference/templating/index.html
@@ -1173,7 +1173,7 @@ Templating
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// Dust:
implementation("org.http4k:http4k-template-dust")
diff --git a/guide/reference/webdriver/index.html b/guide/reference/webdriver/index.html
index 8101ba253..25b033598 100644
--- a/guide/reference/webdriver/index.html
+++ b/guide/reference/webdriver/index.html
@@ -1173,7 +1173,7 @@ WebDriver
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-testing-webdriver")
}
diff --git a/guide/reference/webhooks/index.html b/guide/reference/webhooks/index.html
index 0a3b7c52d..d83c092be 100644
--- a/guide/reference/webhooks/index.html
+++ b/guide/reference/webhooks/index.html
@@ -1173,7 +1173,7 @@ Webhooks
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-webhooks")
}
diff --git a/guide/reference/xml/index.html b/guide/reference/xml/index.html
index 7a0e78394..e68bb620f 100644
--- a/guide/reference/xml/index.html
+++ b/guide/reference/xml/index.html
@@ -1173,7 +1173,7 @@ XML handling
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
// json.org XML:
implementation("org.http4k:http4k-format-xml")
diff --git a/guide/reference/yaml/index.html b/guide/reference/yaml/index.html
index 3a25a74dd..13cfcb1fa 100644
--- a/guide/reference/yaml/index.html
+++ b/guide/reference/yaml/index.html
@@ -1173,7 +1173,7 @@ YAML handling
Installation (Gradle)¶
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-format-jackson-yaml")
implementation("org.http4k:http4k-format-moshi-yaml")
}
diff --git a/quickstart/index.html b/quickstart/index.html
index 0a7fb6c08..e71eec4c3 100644
--- a/quickstart/index.html
+++ b/quickstart/index.html
@@ -1183,7 +1183,7 @@ I'd like a helping handI'm already set up and just need to integrate!¶
Add http4k into an existing project: This simple example demonstrates how to serve and consume HTTP services using http4k. To install, add these dependencies to your Gradle file:
dependencies {
- implementation(platform("org.http4k:http4k-bom:5.27.0.0"))
+ implementation(platform("org.http4k:http4k-bom:5.28.0.0"))
implementation("org.http4k:http4k-core")
implementation("org.http4k:http4k-server-undertow")
implementation("org.http4k:http4k-client-apache")
diff --git a/search/search_index.json b/search/search_index.json
index 3882b9852..69a4b7373 100644
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"blog/","text":"http4k blog \u00b6 * 2020/11: http4k v4 Unleashed - There's a new major http4k release! Read about all the new stuff the team have been working on for http4k v4. 2021/2: Reassurance to http4k users regarding JCenter shutdown Regarding the JCenter shutdown. 2021/1: http4k v4: 17 platforms and counting... Announcement post for http4k v4. 2020/11: http4k Toolbox: Guns for show, knives for a pro Introduction to the http4k Toolbox website and CLI. 2020/10: Nanoservices: The Power of Composition You thought that microservices were a thing? Pah! The powerful abstractions in the http4k toolkit allow you to write entire useful apps which fit in a Tweet. 2020/09: A retrospective on http4k v3 In preparation for the upcoming release of v4, the http4k team thought we'd do a bit of a retrospective about all the things that have gone in the >260 releases of v3. 2019/05: Documenting http4k APIs with OpenAPI3 Overview of the OpenApi3 support available in the http4k library. 2018/11: Add typesafe 12-factor configuration to http4k apps with Environments Overview of how to configure http4k applications using the http4k-cloudnative module. 2018/02: TDDing http4k Step-by-step guide to TDDing a simple http4k application. 2017/12: Websockets. But typesafe. And testable. Without the Server Overview of typesafe Websocket support in http4k. 2017/11: Server as a Function. In Kotlin. Typesafe. Without the Server Overview of the Kotlin \"Server as a Function\" library, http4k.","title":"Overview"},{"location":"blog/#http4k_blog","text":"* 2020/11: http4k v4 Unleashed - There's a new major http4k release! Read about all the new stuff the team have been working on for http4k v4. 2021/2: Reassurance to http4k users regarding JCenter shutdown Regarding the JCenter shutdown. 2021/1: http4k v4: 17 platforms and counting... Announcement post for http4k v4. 2020/11: http4k Toolbox: Guns for show, knives for a pro Introduction to the http4k Toolbox website and CLI. 2020/10: Nanoservices: The Power of Composition You thought that microservices were a thing? Pah! The powerful abstractions in the http4k toolkit allow you to write entire useful apps which fit in a Tweet. 2020/09: A retrospective on http4k v3 In preparation for the upcoming release of v4, the http4k team thought we'd do a bit of a retrospective about all the things that have gone in the >260 releases of v3. 2019/05: Documenting http4k APIs with OpenAPI3 Overview of the OpenApi3 support available in the http4k library. 2018/11: Add typesafe 12-factor configuration to http4k apps with Environments Overview of how to configure http4k applications using the http4k-cloudnative module. 2018/02: TDDing http4k Step-by-step guide to TDDing a simple http4k application. 2017/12: Websockets. But typesafe. And testable. Without the Server Overview of typesafe Websocket support in http4k. 2017/11: Server as a Function. In Kotlin. Typesafe. Without the Server Overview of the Kotlin \"Server as a Function\" library, http4k.","title":"http4k blog"},{"location":"blog/documenting_apis_with_openapi/","text":"Documenting http4k APIs with OpenApi3 \u00b6 may 2019 / @daviddenton \u00b6 This post describes http4k support for fully describing and securing HTTP endpoints using version 3 of the OpenApi specification, providing typesafe JSON-schema documentation for messages and automatically validating incoming HTTP traffic. About OpenApi \u00b6 In microservice environments, some of the biggest challenges exist around the communications between processes that simply aren't present when you're doing monolith-based development. This manifests in many different operational ways such as monitoring, discovery and fault tolerance, but one of the key aspects is communicating the the HTTP contract provided by a particular service. There have been various efforts to standardise these aspects, and one of the most popular is OpenApi , which grew out of the original Swagger project. There are 3 key advantages to OpenApi: It provides a standardised way of documenting APIs, including routes, parameter optionality and format, security models and JSON Schema breakdown of JSON messages. It has standardised support from cloud providers such as Google Cloud Endpoints and AWS API Gateway . The OpenApi UI allows a very simple and developer-focused way of exploring and interacting with HTTP services from a browser environment. It is cross-platform and has good tooling support. Using OpenApi Generators , a specification document can be used to generate HTTP server stubs and working HTTP clients in a variety of languages, thus reducing integration efforts. Typesafe HTTP contracts with http4k-contract \u00b6 http4k has supported generating version 2 of OpenApi docs since all the way back in 2017 (v1.16) via it's http4k-contract module, and after a couple of releases ironing out the niggles (and some amazing help from the community), the team is now happy to announce OpenApi3 support with the release of http4k version 3.179.0. In line with the overall ethos of the project , http4k OpenApi support is done entirely through code and in a typesafe and refactorable way. This is somewhat of a departure from how most other libraries have implemented OpenApi (where often annotations and other compile-time magic are used) and means that in http4k the spec defined in code is the same one that is used to generate the API documentation and the same one used to validate incoming HTTP messages, meaning that it can never go stale. This focus on runtime code also allows for dynamic behaviours which would be very difficult to replicate at compile-time. Out of the box, http4k-contract the module now provides the following features when configured for OpenApi3: Automatic generation of route documentation in OpenApi v3 format, including the JSON Schema models for example incoming and outgoing messages (which arguably provide at least 50% of the value of using OpenApi). Complete auto-validation of the defined HTTP contract through the typesafe http4k Lens mechanism - violations are automatically detected and a BAD_REQUEST returned to the caller. This means that zero custom validation code is required to clutter up your routing layer and you can concentrate on working with meaningful domain types instead of primitives. Support/implementation of all defined OpenApi security models at both a global and per-route scope - BearerToken, ApiKey, OAuth and BasicAuth, although you can of course define and use custom implementations. Simple API for defining custom OpenApi extensions to extend the outputted specification document, for example using http4k in with AWS API Gateway or Google Cloud Endpoints So, how does we do all this using the http4k API? Let's find out with a worked example. 1. Your first endpoint \u00b6 After importing the http4k-core and http4k-contract dependencies into your project, we can write a new endpoint aka ContractRoute . The first thing to note is that we will be using a slightly different routing DSL the standard http4k one, one which provides a richer way to document endpoints - but don't worry - at it's core it utilises the same simple http4k building blocks of HttpHandler and Filter , as well as leveraging the http4k Lens API to automatically extract and convert incoming parameters into richer domain types. As ever, routes can (and should) be written and testing independently, which aids code decomposition and reuse. In this simple example, we're going to use a path with two dynamic parameters; name - a String, and the Integer age - which will be extracted and \"mapped\" into the constructor of a simple validated domain wrapper type. If the basic format of the path or the values for these path parameters cannot be extracted correctly, the endpoint fails to match and is skipped - this allows for several different variations of the same URI path signature to co-exist. Once the values have been extracted, they are passed as arguments to a function which will return a pre-configured HttpHandler for that call: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.div import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.int data class Age ( val value : Int ) { init { require ( value >= 0 ) } } fun basicHandler ( name : String , age : Age ): HttpHandler = { req : Request -> val beverage = if ( age . value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } val basicRoute : ContractRoute = \"/greet\" / Path . of ( \"name\" ) / Path . int (). map ( :: Age ). of ( \"age\" ) bindContract GET to :: basicHandler And here's a unit test for that endpoint - the good news is that it's no more complex than a standard http4k unit test because ContractRoute is also an HttpHandler so can just be invoked as a function. Here, we're also leveraging the http4k-testing-hamkrest module to supply Hamkrest Matchers for validating the response message: package blog.documenting_apis_with_openapi import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.hamkrest.hasBody import org.junit.jupiter.api.Test class BasicGreetingRouteTest { @Test fun `greets an adult` () { assertThat ( basicRoute ( Request ( GET , \"/greet/Bob/21\" )), hasBody ( \"Hello Bob, would you like some beer?\" ) ) } } 2. Defining an HTTP contract \u00b6 Now that we've got our endpoint, we want to be able to actually serve it with the OpenApi documentation. For contract-based routing, we use the contract {} routing DSL which allows us to specify a richer set of details about the API definition, but exposes exactly the same API semantics as the standard routes() block - it is also an HttpHandler and can therefore be composed together to form standard route-matching trees. For rendering the API documentation, we configure an OpenApi object, supplying a standard http4k JSON adapter instance - the recommended one to use is Jackson from the http4k-format-jackson module, so we'll need to import that module into our project as well. Whilst all of the settings used in this DSL above are optional (and default to sensible values if not overridden), here we are updating the URL where the OpenApi spec is served and supplying an instance of Security that we will use to protect our routes (more about that later). package blog.documenting_apis_with_openapi import org.http4k.contract.contract import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.BasicAuthSecurity import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.format.Jackson import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val http : HttpHandler = contract { renderer = OpenApi3 ( ApiInfo ( \"my secure api\" , \"v1.0\" , \"API description\" ), Jackson ) descriptionPath = \"/reference/api/swagger.json\" security = BasicAuthSecurity ( \"realm\" , Credentials ( \"user\" , \"password\" )) routes += basicRoute } http . asServer ( Undertow ( 9000 )). start () } Now we've got a complete contract, we can simply start the server and browse to http://localhost:9000/api/swagger.json to see the basic API spec in the OpenApi UI (or see the online version here ) to see how the endpoint contract looks and how the process of supplying credentials is done through the UI by clicking Authorize . This covers the very basics of generating API docs, but there is still a lot more http4k can do for us... 3. Auto-validating incoming HTTP messages \u00b6 For a better standard of API docs, we should add more details to the endpoint definition. The OpenAPI spec allows us to add this detail, but this normally comes with a maintenance cost - especially when the documentation is static or disparate from the location of the actual code serving requests, and we want to minimise the risk of stale documentation. In http4k, the extended contract metadata is kept close to the endpoint code and mostly type-checked by the compiler, so this threat is minimised as far as practical. Metadata for endpoints can be supplied via inserting a meta {} DSL block, which contains a mixture of 2 main types of property: Informational properties - such as summary , description and tags simply improve the experience of the user of the UI. Contractual properties define parameters using the http4k Lens API (in the same way as we used for the path) for the Query , Header or Body parts of the request. Once added to the contract, these items will also be auto-validated for form and presence before the contract HttpHandler is invoked, thus eliminating the need for any custom validation code to be written. We can then use the same lenses to confidently extract those values inside our HttpHandler code. Let's demonstrate by writing a slightly different version of the same endpoint, but move age to be a required query parameter, and also add the option to override the drink we offer: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int data class Drink ( val name : String ) { init { require ( name . isNotEmpty ()) } } fun Greetings (): ContractRoute { val age = Query . int (). map ( :: Age ). required ( \"age\" , \"Your age\" ) val favouriteDrink = Query . map ( :: Drink ). optional ( \"drink\" , \"Your favourite beverage\" ) fun handler ( name : String ): HttpHandler = { req : Request -> val drinkToOffer : Drink? = favouriteDrink ( req ) val beverage : String = drinkToOffer ?. name ?: if ( age ( req ). value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } return \"/greet\" / Path . of ( \"name\" , \"Your name\" ) meta { summary = \"Send greetings\" description = \"Greets the stupid human by offering them a beverage suitable for their age\" tags += Tag ( \"query\" ) queries += favouriteDrink queries += age produces += TEXT_PLAIN returning ( OK to \"A successful offer of a drink to the lowly meatbag.\" ) } bindContract GET to :: handler } If we then add the Greetings endpoint to the contract and make a call omitting age ... http://localhost:9000/greet/Bob?drink=cola ... the contract validation will fail and a HTTP Bad Request (400) returned to the client with a JSON body describing the error: HTTP / 1.1 400 Bad Request content - type : application / json ; charset = utf - 8 { \"message\" : \"Missing/invalid parameters\" , \"params\" : [ { \"name\" : \"age\" , \"type\" : \"query\" , \"datatype\" : \"integer\" , \"required\" : true , \"reason\" : \"Missing\" } ] } We can see the updated OpenApi UI here . Note that because request parameters are validated before sending, we cannot replicate the above invalid request in the UI. 4. Modelling HTTP body messages \u00b6 The most exciting part http4k supporting OpenApi3 is the ability to represent HTTP messages in JSON Schema form in the documentation. This facility is what unlocks the true cross-language support and takes the usefulness of the OpenApi UI to another level, for both exploratory and support functions. Request and response messages can both be specified in the meta {} block using overloads of the receiving() and returning() functions. By using these functions, we can supply an example object to the DSL - this is what drives the generation of the JSON Schema and, more importantly, ensures that the documentation cannot go stale as it is driven by code. Lets add another route to the mix which returns a JSON body object modelled with a Kotlin Data class and once again using the http4k Lens API . Here, the lens not only provides the validating (de)serialisation mechanism, but also activates the Content-Type header injection and parsing behaviour - this will ensure that all incoming and outgoing messages have the correct headers. For JSON bodies, the lens is created with Body.auto<>().toLens() ( auto() is an extension function imported from Jackson ) which provides the typed injection and extraction functions. Notice here that for injection we are using the more fluent API with() and of() extension functions, as opposed to the standard lens injection function (X, HttpMessage) -> HttpMessage : package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.lens.Path data class Person ( val name : String , val age : Age , val children : List < Person > = emptyList ()) fun Family (): ContractRoute { val familyData = Person ( \"Bob\" , Age ( 85 ), listOf ( Person ( \"Anita\" , Age ( 55 )), Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))) ) ) val responseLens = Body . auto < Person > ( \"The matched family tree\" ). toLens () fun handler ( queryName : String ): HttpHandler = { fun Person . search (): Person? = when ( name ) { queryName -> this else -> children . firstOrNull { it . search () != null } } familyData . search () ?. let { Response ( OK ). with ( responseLens of it ) } ?: Response ( NOT_FOUND ) } return \"/search\" / Path . of ( \"name\" , \"The name to search for in the tree\" ) meta { summary = \"Search family tree\" description = \"Given a name, returns a sub family tree starting with that person\" tags += Tag ( \"query\" ) returning ( OK , responseLens to Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))), \"Cut down family tree\" ) returning ( NOT_FOUND to \"That person does not exist the family\" ) } bindContract GET to :: handler } Taking a final look at the OpenApi UI here shows that not just has the UI been updated with the new route, but that example entries for the expected response are now displayed, as well as JSON Schema entries for the Person and Age classes in the Schemas section at the bottom. Wrapping up... \u00b6 Once we have the final specification document available, users of our API can use the various OpenApi Generators to generate HTTP clients in various languages for interacting with it, or to generate fake services that provide our API in their own environments (and thus enabling more simple end-to-end testing). The \"Fake HTTP services\" technique also enables the creation of Consumer-Driven-Contract style tests, and opens up possibilities for all kinds of interesting Chaos/failure-mode testing (you can even use the http4k-testing-chaos module to help with this \ud83d\ude09). The full source for this tutorial can be found here , or for a sense of how this all looks in when mixed into a complete http4k project, check out the http4k-by-example repo, which contains an entire TDD'd project showcasing a multitude of http4k features and testing styles.","title":"Documenting http4k apps with OpenApi3"},{"location":"blog/documenting_apis_with_openapi/#documenting_http4k_apis_with_openapi3","text":"","title":"Documenting http4k APIs with OpenApi3"},{"location":"blog/documenting_apis_with_openapi/#may_2019_daviddenton","text":"This post describes http4k support for fully describing and securing HTTP endpoints using version 3 of the OpenApi specification, providing typesafe JSON-schema documentation for messages and automatically validating incoming HTTP traffic.","title":"may 2019 / @daviddenton"},{"location":"blog/documenting_apis_with_openapi/#about_openapi","text":"In microservice environments, some of the biggest challenges exist around the communications between processes that simply aren't present when you're doing monolith-based development. This manifests in many different operational ways such as monitoring, discovery and fault tolerance, but one of the key aspects is communicating the the HTTP contract provided by a particular service. There have been various efforts to standardise these aspects, and one of the most popular is OpenApi , which grew out of the original Swagger project. There are 3 key advantages to OpenApi: It provides a standardised way of documenting APIs, including routes, parameter optionality and format, security models and JSON Schema breakdown of JSON messages. It has standardised support from cloud providers such as Google Cloud Endpoints and AWS API Gateway . The OpenApi UI allows a very simple and developer-focused way of exploring and interacting with HTTP services from a browser environment. It is cross-platform and has good tooling support. Using OpenApi Generators , a specification document can be used to generate HTTP server stubs and working HTTP clients in a variety of languages, thus reducing integration efforts.","title":"About OpenApi"},{"location":"blog/documenting_apis_with_openapi/#typesafe_http_contracts_with_http4k-contract","text":"http4k has supported generating version 2 of OpenApi docs since all the way back in 2017 (v1.16) via it's http4k-contract module, and after a couple of releases ironing out the niggles (and some amazing help from the community), the team is now happy to announce OpenApi3 support with the release of http4k version 3.179.0. In line with the overall ethos of the project , http4k OpenApi support is done entirely through code and in a typesafe and refactorable way. This is somewhat of a departure from how most other libraries have implemented OpenApi (where often annotations and other compile-time magic are used) and means that in http4k the spec defined in code is the same one that is used to generate the API documentation and the same one used to validate incoming HTTP messages, meaning that it can never go stale. This focus on runtime code also allows for dynamic behaviours which would be very difficult to replicate at compile-time. Out of the box, http4k-contract the module now provides the following features when configured for OpenApi3: Automatic generation of route documentation in OpenApi v3 format, including the JSON Schema models for example incoming and outgoing messages (which arguably provide at least 50% of the value of using OpenApi). Complete auto-validation of the defined HTTP contract through the typesafe http4k Lens mechanism - violations are automatically detected and a BAD_REQUEST returned to the caller. This means that zero custom validation code is required to clutter up your routing layer and you can concentrate on working with meaningful domain types instead of primitives. Support/implementation of all defined OpenApi security models at both a global and per-route scope - BearerToken, ApiKey, OAuth and BasicAuth, although you can of course define and use custom implementations. Simple API for defining custom OpenApi extensions to extend the outputted specification document, for example using http4k in with AWS API Gateway or Google Cloud Endpoints So, how does we do all this using the http4k API? Let's find out with a worked example.","title":"Typesafe HTTP contracts with http4k-contract"},{"location":"blog/documenting_apis_with_openapi/#1_your_first_endpoint","text":"After importing the http4k-core and http4k-contract dependencies into your project, we can write a new endpoint aka ContractRoute . The first thing to note is that we will be using a slightly different routing DSL the standard http4k one, one which provides a richer way to document endpoints - but don't worry - at it's core it utilises the same simple http4k building blocks of HttpHandler and Filter , as well as leveraging the http4k Lens API to automatically extract and convert incoming parameters into richer domain types. As ever, routes can (and should) be written and testing independently, which aids code decomposition and reuse. In this simple example, we're going to use a path with two dynamic parameters; name - a String, and the Integer age - which will be extracted and \"mapped\" into the constructor of a simple validated domain wrapper type. If the basic format of the path or the values for these path parameters cannot be extracted correctly, the endpoint fails to match and is skipped - this allows for several different variations of the same URI path signature to co-exist. Once the values have been extracted, they are passed as arguments to a function which will return a pre-configured HttpHandler for that call: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.div import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.int data class Age ( val value : Int ) { init { require ( value >= 0 ) } } fun basicHandler ( name : String , age : Age ): HttpHandler = { req : Request -> val beverage = if ( age . value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } val basicRoute : ContractRoute = \"/greet\" / Path . of ( \"name\" ) / Path . int (). map ( :: Age ). of ( \"age\" ) bindContract GET to :: basicHandler And here's a unit test for that endpoint - the good news is that it's no more complex than a standard http4k unit test because ContractRoute is also an HttpHandler so can just be invoked as a function. Here, we're also leveraging the http4k-testing-hamkrest module to supply Hamkrest Matchers for validating the response message: package blog.documenting_apis_with_openapi import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.hamkrest.hasBody import org.junit.jupiter.api.Test class BasicGreetingRouteTest { @Test fun `greets an adult` () { assertThat ( basicRoute ( Request ( GET , \"/greet/Bob/21\" )), hasBody ( \"Hello Bob, would you like some beer?\" ) ) } }","title":"1. Your first endpoint"},{"location":"blog/documenting_apis_with_openapi/#2_defining_an_http_contract","text":"Now that we've got our endpoint, we want to be able to actually serve it with the OpenApi documentation. For contract-based routing, we use the contract {} routing DSL which allows us to specify a richer set of details about the API definition, but exposes exactly the same API semantics as the standard routes() block - it is also an HttpHandler and can therefore be composed together to form standard route-matching trees. For rendering the API documentation, we configure an OpenApi object, supplying a standard http4k JSON adapter instance - the recommended one to use is Jackson from the http4k-format-jackson module, so we'll need to import that module into our project as well. Whilst all of the settings used in this DSL above are optional (and default to sensible values if not overridden), here we are updating the URL where the OpenApi spec is served and supplying an instance of Security that we will use to protect our routes (more about that later). package blog.documenting_apis_with_openapi import org.http4k.contract.contract import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.BasicAuthSecurity import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.format.Jackson import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val http : HttpHandler = contract { renderer = OpenApi3 ( ApiInfo ( \"my secure api\" , \"v1.0\" , \"API description\" ), Jackson ) descriptionPath = \"/reference/api/swagger.json\" security = BasicAuthSecurity ( \"realm\" , Credentials ( \"user\" , \"password\" )) routes += basicRoute } http . asServer ( Undertow ( 9000 )). start () } Now we've got a complete contract, we can simply start the server and browse to http://localhost:9000/api/swagger.json to see the basic API spec in the OpenApi UI (or see the online version here ) to see how the endpoint contract looks and how the process of supplying credentials is done through the UI by clicking Authorize . This covers the very basics of generating API docs, but there is still a lot more http4k can do for us...","title":"2. Defining an HTTP contract"},{"location":"blog/documenting_apis_with_openapi/#3_auto-validating_incoming_http_messages","text":"For a better standard of API docs, we should add more details to the endpoint definition. The OpenAPI spec allows us to add this detail, but this normally comes with a maintenance cost - especially when the documentation is static or disparate from the location of the actual code serving requests, and we want to minimise the risk of stale documentation. In http4k, the extended contract metadata is kept close to the endpoint code and mostly type-checked by the compiler, so this threat is minimised as far as practical. Metadata for endpoints can be supplied via inserting a meta {} DSL block, which contains a mixture of 2 main types of property: Informational properties - such as summary , description and tags simply improve the experience of the user of the UI. Contractual properties define parameters using the http4k Lens API (in the same way as we used for the path) for the Query , Header or Body parts of the request. Once added to the contract, these items will also be auto-validated for form and presence before the contract HttpHandler is invoked, thus eliminating the need for any custom validation code to be written. We can then use the same lenses to confidently extract those values inside our HttpHandler code. Let's demonstrate by writing a slightly different version of the same endpoint, but move age to be a required query parameter, and also add the option to override the drink we offer: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int data class Drink ( val name : String ) { init { require ( name . isNotEmpty ()) } } fun Greetings (): ContractRoute { val age = Query . int (). map ( :: Age ). required ( \"age\" , \"Your age\" ) val favouriteDrink = Query . map ( :: Drink ). optional ( \"drink\" , \"Your favourite beverage\" ) fun handler ( name : String ): HttpHandler = { req : Request -> val drinkToOffer : Drink? = favouriteDrink ( req ) val beverage : String = drinkToOffer ?. name ?: if ( age ( req ). value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } return \"/greet\" / Path . of ( \"name\" , \"Your name\" ) meta { summary = \"Send greetings\" description = \"Greets the stupid human by offering them a beverage suitable for their age\" tags += Tag ( \"query\" ) queries += favouriteDrink queries += age produces += TEXT_PLAIN returning ( OK to \"A successful offer of a drink to the lowly meatbag.\" ) } bindContract GET to :: handler } If we then add the Greetings endpoint to the contract and make a call omitting age ... http://localhost:9000/greet/Bob?drink=cola ... the contract validation will fail and a HTTP Bad Request (400) returned to the client with a JSON body describing the error: HTTP / 1.1 400 Bad Request content - type : application / json ; charset = utf - 8 { \"message\" : \"Missing/invalid parameters\" , \"params\" : [ { \"name\" : \"age\" , \"type\" : \"query\" , \"datatype\" : \"integer\" , \"required\" : true , \"reason\" : \"Missing\" } ] } We can see the updated OpenApi UI here . Note that because request parameters are validated before sending, we cannot replicate the above invalid request in the UI.","title":"3. Auto-validating incoming HTTP messages"},{"location":"blog/documenting_apis_with_openapi/#4_modelling_http_body_messages","text":"The most exciting part http4k supporting OpenApi3 is the ability to represent HTTP messages in JSON Schema form in the documentation. This facility is what unlocks the true cross-language support and takes the usefulness of the OpenApi UI to another level, for both exploratory and support functions. Request and response messages can both be specified in the meta {} block using overloads of the receiving() and returning() functions. By using these functions, we can supply an example object to the DSL - this is what drives the generation of the JSON Schema and, more importantly, ensures that the documentation cannot go stale as it is driven by code. Lets add another route to the mix which returns a JSON body object modelled with a Kotlin Data class and once again using the http4k Lens API . Here, the lens not only provides the validating (de)serialisation mechanism, but also activates the Content-Type header injection and parsing behaviour - this will ensure that all incoming and outgoing messages have the correct headers. For JSON bodies, the lens is created with Body.auto<>().toLens() ( auto() is an extension function imported from Jackson ) which provides the typed injection and extraction functions. Notice here that for injection we are using the more fluent API with() and of() extension functions, as opposed to the standard lens injection function (X, HttpMessage) -> HttpMessage : package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.lens.Path data class Person ( val name : String , val age : Age , val children : List < Person > = emptyList ()) fun Family (): ContractRoute { val familyData = Person ( \"Bob\" , Age ( 85 ), listOf ( Person ( \"Anita\" , Age ( 55 )), Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))) ) ) val responseLens = Body . auto < Person > ( \"The matched family tree\" ). toLens () fun handler ( queryName : String ): HttpHandler = { fun Person . search (): Person? = when ( name ) { queryName -> this else -> children . firstOrNull { it . search () != null } } familyData . search () ?. let { Response ( OK ). with ( responseLens of it ) } ?: Response ( NOT_FOUND ) } return \"/search\" / Path . of ( \"name\" , \"The name to search for in the tree\" ) meta { summary = \"Search family tree\" description = \"Given a name, returns a sub family tree starting with that person\" tags += Tag ( \"query\" ) returning ( OK , responseLens to Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))), \"Cut down family tree\" ) returning ( NOT_FOUND to \"That person does not exist the family\" ) } bindContract GET to :: handler } Taking a final look at the OpenApi UI here shows that not just has the UI been updated with the new route, but that example entries for the expected response are now displayed, as well as JSON Schema entries for the Person and Age classes in the Schemas section at the bottom.","title":"4. Modelling HTTP body messages"},{"location":"blog/documenting_apis_with_openapi/#wrapping_up","text":"Once we have the final specification document available, users of our API can use the various OpenApi Generators to generate HTTP clients in various languages for interacting with it, or to generate fake services that provide our API in their own environments (and thus enabling more simple end-to-end testing). The \"Fake HTTP services\" technique also enables the creation of Consumer-Driven-Contract style tests, and opens up possibilities for all kinds of interesting Chaos/failure-mode testing (you can even use the http4k-testing-chaos module to help with this \ud83d\ude09). The full source for this tutorial can be found here , or for a sense of how this all looks in when mixed into a complete http4k project, check out the http4k-by-example repo, which contains an entire TDD'd project showcasing a multitude of http4k features and testing styles.","title":"Wrapping up..."},{"location":"blog/http4k_v4/","text":"http4k v4: 17 platforms and counting... \u00b6 january 2021 / the http4k team \u00b6 Well, at last it's here - after 3 years - http4k v4! Following on from the retrospective that we did on version 3, we've been busy polishing, tidying up the edges, and pushing out a bunch of changes to make the project sparkle. Ready? Then let's dive into the good stuff that's been going on at http4k Towers. Four digits good, three digits bad. The new http4k versioning scheme \u00b6 Ah yes - versioning - everyone's favourite topic. Part of the reason that http4k v3 has been around so long is that we've somewhat been abusing the Semantic versioning system, something which we've been unhappy with. Here's how it should work: For Version .. A = We broke something on purpose. (Breaking API change) B = Profit. (Feature / Improvement) C = We broke something by accident. (Bug) Up until now, both breaking and non-breaking API changes on v3 have been done through the second (B) digit of the version - which doesn't allow API users to know if they are expecting a break. At the same time, we wanted to keep major (A) version changes for when there's a big \"marketing\" release. To get around this, we are introducing a new versioning scheme based on 4 digits: For Version ... A = There's something we'd like the world to know. (Major change / Marketing) B = We broke something on purpose. (Breaking API change) C = Profit. (Feature / Improvement) D = We broke something by accident. (Bug) As you can see, for our users we'll be concentrating on changes in numbers A (occasional) and C (standard). \"Platforms, Guv! Thousands of 'em!\" (well, more than a few...) \u00b6 When http4k v3 was released, we only supported 3 JVM Server backends and 1 Serverless platform. Since then, we've added a bunch, and are now up to a very respectable 17 standard deployment options for http4k apps... 10 JVM Backends - Apache 4 & 5, Jetty, Ktor CIO & Netty, Netty, Ratpack, SunHttp and Undertow (+ any Servlet container) 6 Serverless platforms - Alibaba, AWS Lamba, Azure, Google Cloud, OpenWhisk (IBM/Adobe/Nimbella/Cloudstation), Tencent 1 Native platform - GraalVM (+ Quarkus) Switching between all platforms is super easy - just plug the standard HttpHandler into the the relevant http4k module class with a single line of code. Serverless modules all require just one more line, plus configuring the Serverless platform to call the relevant function. Here's examples for both: val app : HttpHandler = { req : Request -> Response ( OK ). body ( \"hello world!\" ) } val jvmApp = app . asServer ( Netty ( 8080 )). start () class MyServerlessFunction : GoogleCloudFunction ( app ) The even better news is that testing your http4k apps locally (regardless of platform) is simple - and as ever there's no magic involved - just test them entirely in-memory, or bind them to a standard backend Server. http4k Toolbox: your new Swiss Army Knife \u00b6 As documented in the Toolbox announcement post , we've been busy consolidating a bunch of handy tools for generating code to work with http4k projects, and we christened this the http4k Toolbox and it's available in both online and a CLI flavours (available from Brew and SDKMan!). From Project Generation to our own more sophisticated OpenAPI3 Generator , we hope that this become an essential tool in every http4k developer's pocket. Infinirouting \u00b6 The v3 routing scheme was pretty good as you could bind routes on static or dynamic paths and HTTP verbs, but being rampant power seekers, we wanted it to be better. We reasoned that if we could route traffic to HttpHandlers based on those things, then why not be able to route on any part of the request? We'd like to be able to do complicated matching - so for instance: \"Match the /name path, but only when the host header is http4k.org . Then add 2 submatches, one where there is a query parameter named queryName , the other where the body is > 50 bytes long.\" val app = routes ( \"/{name}\" bind POST to ( header ( \"host\" ) { it == \"http4k.org\" } bind routes ( queries ( \"queryName\" ) bind { Response ( OK ). body ( \"i had a query\" ) }, body { body : String -> body . length > 50 } bind { Response ( OK ). body ( \"I was long\" ) } )) ) So after a lot of clattering and banging of heads, we cracked it - and in doing so managed to rewrite the entire of the http4k routing layer in terms of these predicate Routers . It's really neat, infinitely(ish) nestable, and makes us feel just a little bit smug for getting it working. Graph power \u00b6 Traditionally, http4k has concentrated on providing routing for REST-style APIs. However, there is this new thing called GraphQL that has suddenly sprung up overnight and seems quite popular. Not wanting our users to miss out on anything, we've added support for simply integrating http4k with the official Java implementation of the library, GraphQL-Java . This module allows you to both serve and consume GQL APIs, and as per tradition allows you to test your APIs entirely in-memory making for super-fast test suites. OpenTelemetry: Monitor all the things! \u00b6 The OpenTelemetry project describes itself as... \"... a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior.\" - OpenTelemetry.io It's a great project run by the CNCF and very well fits in with the ethos that the http4k team believes in. As with all http4k integration modules, we want to enable http4k developers to be able to plug in their apps as simply as possible - in this case, just configure the OpenTelemetry API or Java-agent, then just add some simple Filters to your code to start collecting Distributed Traces or Metrics. Several tracing schemes are supported, including Amazon XRay, Jaeger and Zipkin. For more docs on how to get it all working, head over to the docs . Upgrading & library API changes \u00b6 Like the neat little worker bunnies we are, we've also taken the opportunity to clean up the http4k source code. All previously deprecated code has been removed, leaving the codebase nice and tidy. If you are upgrading, the best idea is to first upgrade to the last v3 version (v3.285.2), deal with any existing deprecations in place, then simply upgrade again to v4.0.0.0. http4k.org \u00b6 One of the things that our users feedback about was that the structure of the docs in http4k.org could be improved, so we've begun overhauling the site to simplify the content. Expect this to be a continual improvement thing, but on the whole the content will be organised as follows: Concepts will contain descriptions of the underlying concepts in and around the http4k libraries. Tutorials will be step-by-step guides to getting up and running for various use-cases. How-tos will contain extended examples of how to accomplish particular tasks. eg. provide a custom ServerConfig implementation. Code here will generally be complete and contain runnable examples. Module Reference will contain descriptions of the various features in the different http4k modules. Code in the guide will be snippet-based. Additionally, the Examples repo hosts fully self-contained, runnable projects that can be used as a baseline for particular features - eg. how to write and run an app on Quarkus or use the cloudnative module to enable typesafe configuration Support & training \u00b6 There has been a decent amount of interest lately from our users to come to us to ask for advice about how we can help teams get the best out of http4k. In that vein, we have also been busy building training materials which we can deliver to teams either new to (or experienced in) the library, or to visit teams (currently virtually) to help them out. If your team would also like to take advantage of our experience in delivering projects using http4k, then please visit the support/training page, reach out and we'd love to see how we can help. http4k Connect - Flyweight 3rd party adapters \u00b6 http4k-connect is the team's newest side project, the purpose of which is to eventually standardise patterns for building 3rd party system adapters to various backend services, and for building your own Fakes (backed by data-stores such as InMemory, S3 or Redis). So far (v2.8.0.0), http4k-connect supports at least the common-use case actions for the following systems (and the API is easily extendable for non-supplied actions): AWS KMS: Key Management Service AWS Lambda AWS S3: Simple Storage Service AWS Secrets Manager AWS SQS: Simple Queue Service AWS SSM: Systems Manager AWS STS: Security Token Service Google Analytics Mostly, the existence of the project is has been driven by 2 factors: to reduce dependency weight of bringing in SDK modules, especially when in a Serverless context. The AWS service SDKs are especially heavy for dependency weight. Using http4k-connect instead of official SDKs, overall Serverless Function distribution size should be reduced by at least an order of magnitude. to avoid us having to reinvent the same things again and again! (Because we're very very lazy developers!) It's pretty hot off the press, but will be receiving a lot of attention over the coming weeks and months, and we'll be documenting the mechanisms in both web and live talks. That's all folks... (for the moment) \u00b6 We're pretty excited about this release and hope the library will continue to provide powerful tools to make all of our existing (and new!) users' lives easier in creating kick ass and rock solid HTTP applications. In the meantime, if you are using http4k, please consider sponsoring the project to help offset the costs of development, documentation, and support. If you're using it commercially, we offer Commercial Support and Consulting to ensure you're getting maximum value from the toolkit and its related techniques. As ever, we'd love to hear how we're doing, so please drop into the comm channels to get in touch. Peace out. // the http4k team \u00b6","title":"http4k v4 - 17 platforms and counting..."},{"location":"blog/http4k_v4/#http4k_v4_17_platforms_and_counting","text":"","title":"http4k v4: 17 platforms and counting..."},{"location":"blog/http4k_v4/#january_2021_the_http4k_team","text":"Well, at last it's here - after 3 years - http4k v4! Following on from the retrospective that we did on version 3, we've been busy polishing, tidying up the edges, and pushing out a bunch of changes to make the project sparkle. Ready? Then let's dive into the good stuff that's been going on at http4k Towers.","title":"january 2021 / the http4k team"},{"location":"blog/http4k_v4/#four_digits_good_three_digits_bad_the_new_http4k_versioning_scheme","text":"Ah yes - versioning - everyone's favourite topic. Part of the reason that http4k v3 has been around so long is that we've somewhat been abusing the Semantic versioning system, something which we've been unhappy with. Here's how it should work: For Version .. A = We broke something on purpose. (Breaking API change) B = Profit. (Feature / Improvement) C = We broke something by accident. (Bug) Up until now, both breaking and non-breaking API changes on v3 have been done through the second (B) digit of the version - which doesn't allow API users to know if they are expecting a break. At the same time, we wanted to keep major (A) version changes for when there's a big \"marketing\" release. To get around this, we are introducing a new versioning scheme based on 4 digits: For Version ... A = There's something we'd like the world to know. (Major change / Marketing) B = We broke something on purpose. (Breaking API change) C = Profit. (Feature / Improvement) D = We broke something by accident. (Bug) As you can see, for our users we'll be concentrating on changes in numbers A (occasional) and C (standard).","title":"Four digits good, three digits bad. The new http4k versioning scheme"},{"location":"blog/http4k_v4/#platforms_guv_thousands_of_em_well_more_than_a_few","text":"When http4k v3 was released, we only supported 3 JVM Server backends and 1 Serverless platform. Since then, we've added a bunch, and are now up to a very respectable 17 standard deployment options for http4k apps... 10 JVM Backends - Apache 4 & 5, Jetty, Ktor CIO & Netty, Netty, Ratpack, SunHttp and Undertow (+ any Servlet container) 6 Serverless platforms - Alibaba, AWS Lamba, Azure, Google Cloud, OpenWhisk (IBM/Adobe/Nimbella/Cloudstation), Tencent 1 Native platform - GraalVM (+ Quarkus) Switching between all platforms is super easy - just plug the standard HttpHandler into the the relevant http4k module class with a single line of code. Serverless modules all require just one more line, plus configuring the Serverless platform to call the relevant function. Here's examples for both: val app : HttpHandler = { req : Request -> Response ( OK ). body ( \"hello world!\" ) } val jvmApp = app . asServer ( Netty ( 8080 )). start () class MyServerlessFunction : GoogleCloudFunction ( app ) The even better news is that testing your http4k apps locally (regardless of platform) is simple - and as ever there's no magic involved - just test them entirely in-memory, or bind them to a standard backend Server.","title":"\"Platforms, Guv! Thousands of 'em!\" (well, more than a few...)"},{"location":"blog/http4k_v4/#http4k_toolbox_your_new_swiss_army_knife","text":"As documented in the Toolbox announcement post , we've been busy consolidating a bunch of handy tools for generating code to work with http4k projects, and we christened this the http4k Toolbox and it's available in both online and a CLI flavours (available from Brew and SDKMan!). From Project Generation to our own more sophisticated OpenAPI3 Generator , we hope that this become an essential tool in every http4k developer's pocket.","title":"http4k Toolbox: your new Swiss Army Knife"},{"location":"blog/http4k_v4/#infinirouting","text":"The v3 routing scheme was pretty good as you could bind routes on static or dynamic paths and HTTP verbs, but being rampant power seekers, we wanted it to be better. We reasoned that if we could route traffic to HttpHandlers based on those things, then why not be able to route on any part of the request? We'd like to be able to do complicated matching - so for instance: \"Match the /name path, but only when the host header is http4k.org . Then add 2 submatches, one where there is a query parameter named queryName , the other where the body is > 50 bytes long.\" val app = routes ( \"/{name}\" bind POST to ( header ( \"host\" ) { it == \"http4k.org\" } bind routes ( queries ( \"queryName\" ) bind { Response ( OK ). body ( \"i had a query\" ) }, body { body : String -> body . length > 50 } bind { Response ( OK ). body ( \"I was long\" ) } )) ) So after a lot of clattering and banging of heads, we cracked it - and in doing so managed to rewrite the entire of the http4k routing layer in terms of these predicate Routers . It's really neat, infinitely(ish) nestable, and makes us feel just a little bit smug for getting it working.","title":"Infinirouting"},{"location":"blog/http4k_v4/#graph_power","text":"Traditionally, http4k has concentrated on providing routing for REST-style APIs. However, there is this new thing called GraphQL that has suddenly sprung up overnight and seems quite popular. Not wanting our users to miss out on anything, we've added support for simply integrating http4k with the official Java implementation of the library, GraphQL-Java . This module allows you to both serve and consume GQL APIs, and as per tradition allows you to test your APIs entirely in-memory making for super-fast test suites.","title":"Graph power"},{"location":"blog/http4k_v4/#opentelemetry_monitor_all_the_things","text":"The OpenTelemetry project describes itself as... \"... a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior.\" - OpenTelemetry.io It's a great project run by the CNCF and very well fits in with the ethos that the http4k team believes in. As with all http4k integration modules, we want to enable http4k developers to be able to plug in their apps as simply as possible - in this case, just configure the OpenTelemetry API or Java-agent, then just add some simple Filters to your code to start collecting Distributed Traces or Metrics. Several tracing schemes are supported, including Amazon XRay, Jaeger and Zipkin. For more docs on how to get it all working, head over to the docs .","title":"OpenTelemetry: Monitor all the things!"},{"location":"blog/http4k_v4/#upgrading_library_api_changes","text":"Like the neat little worker bunnies we are, we've also taken the opportunity to clean up the http4k source code. All previously deprecated code has been removed, leaving the codebase nice and tidy. If you are upgrading, the best idea is to first upgrade to the last v3 version (v3.285.2), deal with any existing deprecations in place, then simply upgrade again to v4.0.0.0.","title":"Upgrading & library API changes"},{"location":"blog/http4k_v4/#http4korg","text":"One of the things that our users feedback about was that the structure of the docs in http4k.org could be improved, so we've begun overhauling the site to simplify the content. Expect this to be a continual improvement thing, but on the whole the content will be organised as follows: Concepts will contain descriptions of the underlying concepts in and around the http4k libraries. Tutorials will be step-by-step guides to getting up and running for various use-cases. How-tos will contain extended examples of how to accomplish particular tasks. eg. provide a custom ServerConfig implementation. Code here will generally be complete and contain runnable examples. Module Reference will contain descriptions of the various features in the different http4k modules. Code in the guide will be snippet-based. Additionally, the Examples repo hosts fully self-contained, runnable projects that can be used as a baseline for particular features - eg. how to write and run an app on Quarkus or use the cloudnative module to enable typesafe configuration","title":"http4k.org"},{"location":"blog/http4k_v4/#support_training","text":"There has been a decent amount of interest lately from our users to come to us to ask for advice about how we can help teams get the best out of http4k. In that vein, we have also been busy building training materials which we can deliver to teams either new to (or experienced in) the library, or to visit teams (currently virtually) to help them out. If your team would also like to take advantage of our experience in delivering projects using http4k, then please visit the support/training page, reach out and we'd love to see how we can help.","title":"Support & training"},{"location":"blog/http4k_v4/#http4k_connect_-_flyweight_3rd_party_adapters","text":"http4k-connect is the team's newest side project, the purpose of which is to eventually standardise patterns for building 3rd party system adapters to various backend services, and for building your own Fakes (backed by data-stores such as InMemory, S3 or Redis). So far (v2.8.0.0), http4k-connect supports at least the common-use case actions for the following systems (and the API is easily extendable for non-supplied actions): AWS KMS: Key Management Service AWS Lambda AWS S3: Simple Storage Service AWS Secrets Manager AWS SQS: Simple Queue Service AWS SSM: Systems Manager AWS STS: Security Token Service Google Analytics Mostly, the existence of the project is has been driven by 2 factors: to reduce dependency weight of bringing in SDK modules, especially when in a Serverless context. The AWS service SDKs are especially heavy for dependency weight. Using http4k-connect instead of official SDKs, overall Serverless Function distribution size should be reduced by at least an order of magnitude. to avoid us having to reinvent the same things again and again! (Because we're very very lazy developers!) It's pretty hot off the press, but will be receiving a lot of attention over the coming weeks and months, and we'll be documenting the mechanisms in both web and live talks.","title":"http4k Connect - Flyweight 3rd party adapters"},{"location":"blog/http4k_v4/#thats_all_folks_for_the_moment","text":"We're pretty excited about this release and hope the library will continue to provide powerful tools to make all of our existing (and new!) users' lives easier in creating kick ass and rock solid HTTP applications. In the meantime, if you are using http4k, please consider sponsoring the project to help offset the costs of development, documentation, and support. If you're using it commercially, we offer Commercial Support and Consulting to ensure you're getting maximum value from the toolkit and its related techniques. As ever, we'd love to hear how we're doing, so please drop into the comm channels to get in touch. Peace out.","title":"That's all folks... (for the moment)"},{"location":"blog/http4k_v4/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/http4k_v5/","text":"http4k Platform v5: New Servers, Loom, TracerBullet, OpenAI plugin SDK and more \u00b6 june 2023 / the http4k team \u00b6 We\u2019re thrilled to announce the next major release of http4k! Since the last major release, the team has been busy enhancing existing features, adding new capabilities with help from our amazing community, things that we've needed on our own real world projects, and a sprinkling of new magic to keep pushing the boundaries of what's possible with the http4k technology - all 127 modules of it. The first thing to tell you is that we've hit a significant milestone and are now getting about 1 million downloads per month from Maven Central. From these numbers and our interactions with the global http4k community, it makes us very happy that so many teams are seeing the power of the Server-as-a-Function model. Partnership programme \u00b6 Another thing to celebrate is that http4k now has it's first major corporate partner - the wonderful SpringerNature Technology , where http4k powers a sizeable percentage of their global content delivery platform. The http4k team thanks all the folks at SN Technology for their ongoing collaboration and support - undoubtedly the project would not be where it is now without them! If your organisation is using the http4k platform and would like to explore potential partnership options - including access to priority support channels, training opportunities, and input into the development plan, then please reach out and we'll be in touch to see how we can work together. With those things out of the way, let's get on to what you really came for - features! Introducing the http4k Platform: 127 modules and counting \u00b6 Before we dive into the details of the library updates themselves, we're announcing today that for the simplicity (to both the http4k community and the team!), we are unifying all http4k ecosystem projects (currently http4k and http4k-connect) under a single major platform version number - starting with v5. In addition, to keep pace with Java's own rapidly evolving ecosystem, we're going to be switching to aligning major releases of \"the Platform\" with every JDK release - currently scheduled for every 6 months. This will allow us to track major-version support as features are added and removed from Java and provide an easy way for our users to track the this compatibility. For the moment, http4k and http4k-connect will still have different release cadence and individual release numbers and which are now developed using Java 20 as a base, but in the future there are plans to consolidate the projects under a single platform version. In terms of Java compatibility - for the foreseeable future, we are still going to compile http4k for older Java versions (we still target Java 8 as a base). Over time though, we plan to adjust our standard of support for ancient versions and introduce a paid support program for those who still need to run http4k against legacy versions of Java and require updates such as security patches. If that\u2019s already the case for your team or project, be sure to get in touch with the http4k team to discuss your particular needs. Now - 127 is a pretty big number of modules, especially for what started out with a modest 43 line spike . But we're definitely not done yet - http4k is a solid foundation - it's arguably one of the simplest and most test-driven web libraries on the planet. We've also proven in http4k-connect that those parts can be composed into an powerful integration layer. Now the http4k team intend to continue that journey - and v5 is just the start. http4k Updates \u00b6 In v4, we shipped a massive 197 releases (an average of 1.5 per week!) and introduced 12 new modules in a variety of areas: deployment ( serverless-lambda-runtime ), wire formats (moshi-yaml, jackson-csv, kondor-json), security ( digest ), testing ( strikt ), and around simpler usages of OpenAPI (Redoc/SwaggerUI). http4k 5 introduces 5 new modules and tidies up the decks for future developments. http4k v5 is available right now from the Toolbox , where you can configure an download a fully compilable project with any combination of the 70 available http4k modules. Loom support \u00b6 We\u2019re excited to see the re-introduction of virtual threads in Java and the performance improvements that can bring to well-established servers. Loom goes gold in Java 21 and we're going to be ready for our users to use it straight away. For this new major version, we\u2019re introducing three new server backends taking advantage of Java Loom virtual threads: the SDK built-in SunHttp, Jetty , and Helidon . Servers are only half of the story though - http4k will also add support for Loom-friendy HTTP clients as they get released, and is introducing support for the Loom-native Helidon client in this release. We'll be sure to keep up-to-date with the other major HTTP client libraries as they update to support Loom. TracerBullet: a brand new way of getting test feedback and documenting your systems \u00b6 We're incredibly proud to be introducing TracerBullet , an innovative testing add-on that integrates with the http4k events system. TracerBullet enables teams to focus on how their services work, not just if they work. This powerful tool will change how you approach testing and help you gain deeper insights into your services. As a side-effect of introducing TracerBullet, http4k can automatically generate sequence and interaction diagrams , taking advantage of existing tools such as PlantUML , Mermaid , and d2 to create living documentation for your services after each test run! The http4k team were excited to be invited back to KotlinConf 2023 , where we presented how we used the TracerBullet along with Hexagonal Design to visually document multi-service tests. You can check out the video of the presentation here or see a full example of how it works in the demo repo . Remodelling of Websocket and SSE support \u00b6 Various http4k backends have supported SSE and Websockets for a while, but whilst the innovation was that you could unit-test them entirely in-memory (a world first?), the models did have some restrictions in handling request filtering. As such we have reworked the support to be more consistent with the HTTP handling. This is a breaking change, but it very important for us to be able to more fully support these protocols. Removal of deprecated and unsupported features \u00b6 As part of the major release cycle, we\u2019re removing all code marked as deprecated in v4 of http4k. We have also removed the http4k-templates-dust module due to the removal of Nashorn from the Java distribution. We understand this may impact some users, and we recommend seeking alternative solutions in the long term, but you can continue to use the existing http4k-template-dust assets which are still binary compatible with v5 of http4k. Upgrading from http4k v4 \u00b6 If you are upgrading, the best idea is to first upgrade to the last v4 version, deal with any existing deprecations in place, then simply upgrade again the latest v5 version and repeat. http4k-connect Updates \u00b6 http4k-connect was conceived as a library of providing client adapters for popular cloud services based on the innovative Connect pattern . v3 of the library added 20 different modules, and covered a number of popular AWS services, as well as integrating with Kafka via HTTP. The power of http4k-connect is it's ability to leverage Kotlin to provide modular adapters based around a common core. Each client endpoint is modeled as a separate \"Action\"? Is the action you want not supported by the core library, or do you want less information to be parsed? Simple - just write your own endpoint adapter and plug it in! Today with the v5 release, we're expanding the focus slightly - http4k-connect will now be the home of all official http4k platform integrations with third party services, to cover: Featherweight, zero-reflection adapters, for HTTP integrations with cloud services (eg. AWS, Confluent) Fake implementations of cloud services, which allow lightning-fast in-memory testing or local testing without Docker/LocalStack. A pluggable unified Storage interface with five different storage backends - including in-memory, Redis and S3, as well as a StorageExplorer that provides an OpenAPI UI to allow interaction with the storage via browser. Plugin SDKs for building integrations with popular platforms - starting with... OpenAI/ChatGPT support \u00b6 It's 2023, and the new hotness that everyone is talking about is AI - and who are we to go against the weight of the hypemachine? After playing with GPT-4, one of the most frustrating things we found was the slowness of the responses and the effect that had on our testing-cycle, so the http4k team started to build a client adapter and fake OpenAI server so that Kotlin developer teams could integrate and test with OpenAI APIs quickly without waiting ages for the model to respond, and burning through those precious GPT-4 tokens and API limits. And thus http4k-connect-openai was born. The Fake server even allows you to generate images. A little later, the team received an early access invite to the ChatGPT plugin programme and it immediately occured that the requirements for developing plugins were all already supported within existing http4k libraries. So we set out to develop a plugin SDK, and http4k-connect-openai-plugin was born. The SDK provides a simple API for developers to simply compose their plugins by supplying some config and a standard http4k-contract endpoints, which generate the required OpenAPI specifications. The SDK does the rest for you, providing the required OpenAI plugin manifest, and then protects the API with one of 4 security models - including the ability to \"login with ChatGPT\" and OAuth into your plugin. Once created, plugins can be installed into the FakeOpenAI server, and expose a standard http4k HttpHandler which means they can be tested in memory, run locally standalone, or composed into part of a larger http4k application. It's pretty neat and we can't wait to see what the community uses it for! There's a full example of how to build a plugin with the http4k-connect-openai-plugin SDK and deploy it to AWS Lambda with Pulumi - see the repo for details. Removal of deprecated and unsupported features \u00b6 As part of the major release cycle, we\u2019re removing all code marked as deprecated in v3 of http4k-connect. Upgrading from http4k-connect v3 \u00b6 If you are upgrading, the best idea is to first upgrade to the last v3 version, deal with any existing deprecations in place, then simply upgrade again to the latest v5 version and repeat. And that's it (for now!). We hope you're as excited about these updates to the http4k platform as we are! This release brings a wealth of new features and improvements, and we believe it will make your experience using http4k technologies even better. Cheers // the http4k team \u00b6","title":"http4k Platform v5 - New Servers, Loom, TracerBullet, OpenAI plugin SDK and more"},{"location":"blog/http4k_v5/#http4k_platform_v5_new_servers_loom_tracerbullet_openai_plugin_sdk_and_more","text":"","title":"http4k Platform v5: New Servers, Loom, TracerBullet, OpenAI plugin SDK and more"},{"location":"blog/http4k_v5/#june_2023_the_http4k_team","text":"We\u2019re thrilled to announce the next major release of http4k! Since the last major release, the team has been busy enhancing existing features, adding new capabilities with help from our amazing community, things that we've needed on our own real world projects, and a sprinkling of new magic to keep pushing the boundaries of what's possible with the http4k technology - all 127 modules of it. The first thing to tell you is that we've hit a significant milestone and are now getting about 1 million downloads per month from Maven Central. From these numbers and our interactions with the global http4k community, it makes us very happy that so many teams are seeing the power of the Server-as-a-Function model.","title":"june 2023 / the http4k team"},{"location":"blog/http4k_v5/#partnership_programme","text":"Another thing to celebrate is that http4k now has it's first major corporate partner - the wonderful SpringerNature Technology , where http4k powers a sizeable percentage of their global content delivery platform. The http4k team thanks all the folks at SN Technology for their ongoing collaboration and support - undoubtedly the project would not be where it is now without them! If your organisation is using the http4k platform and would like to explore potential partnership options - including access to priority support channels, training opportunities, and input into the development plan, then please reach out and we'll be in touch to see how we can work together. With those things out of the way, let's get on to what you really came for - features!","title":"Partnership programme"},{"location":"blog/http4k_v5/#introducing_the_http4k_platform_127_modules_and_counting","text":"Before we dive into the details of the library updates themselves, we're announcing today that for the simplicity (to both the http4k community and the team!), we are unifying all http4k ecosystem projects (currently http4k and http4k-connect) under a single major platform version number - starting with v5. In addition, to keep pace with Java's own rapidly evolving ecosystem, we're going to be switching to aligning major releases of \"the Platform\" with every JDK release - currently scheduled for every 6 months. This will allow us to track major-version support as features are added and removed from Java and provide an easy way for our users to track the this compatibility. For the moment, http4k and http4k-connect will still have different release cadence and individual release numbers and which are now developed using Java 20 as a base, but in the future there are plans to consolidate the projects under a single platform version. In terms of Java compatibility - for the foreseeable future, we are still going to compile http4k for older Java versions (we still target Java 8 as a base). Over time though, we plan to adjust our standard of support for ancient versions and introduce a paid support program for those who still need to run http4k against legacy versions of Java and require updates such as security patches. If that\u2019s already the case for your team or project, be sure to get in touch with the http4k team to discuss your particular needs. Now - 127 is a pretty big number of modules, especially for what started out with a modest 43 line spike . But we're definitely not done yet - http4k is a solid foundation - it's arguably one of the simplest and most test-driven web libraries on the planet. We've also proven in http4k-connect that those parts can be composed into an powerful integration layer. Now the http4k team intend to continue that journey - and v5 is just the start.","title":"Introducing the http4k Platform: 127 modules and counting"},{"location":"blog/http4k_v5/#http4k_updates","text":"In v4, we shipped a massive 197 releases (an average of 1.5 per week!) and introduced 12 new modules in a variety of areas: deployment ( serverless-lambda-runtime ), wire formats (moshi-yaml, jackson-csv, kondor-json), security ( digest ), testing ( strikt ), and around simpler usages of OpenAPI (Redoc/SwaggerUI). http4k 5 introduces 5 new modules and tidies up the decks for future developments. http4k v5 is available right now from the Toolbox , where you can configure an download a fully compilable project with any combination of the 70 available http4k modules.","title":"http4k Updates"},{"location":"blog/http4k_v5/#loom_support","text":"We\u2019re excited to see the re-introduction of virtual threads in Java and the performance improvements that can bring to well-established servers. Loom goes gold in Java 21 and we're going to be ready for our users to use it straight away. For this new major version, we\u2019re introducing three new server backends taking advantage of Java Loom virtual threads: the SDK built-in SunHttp, Jetty , and Helidon . Servers are only half of the story though - http4k will also add support for Loom-friendy HTTP clients as they get released, and is introducing support for the Loom-native Helidon client in this release. We'll be sure to keep up-to-date with the other major HTTP client libraries as they update to support Loom.","title":"Loom support"},{"location":"blog/http4k_v5/#tracerbullet_a_brand_new_way_of_getting_test_feedback_and_documenting_your_systems","text":"We're incredibly proud to be introducing TracerBullet , an innovative testing add-on that integrates with the http4k events system. TracerBullet enables teams to focus on how their services work, not just if they work. This powerful tool will change how you approach testing and help you gain deeper insights into your services. As a side-effect of introducing TracerBullet, http4k can automatically generate sequence and interaction diagrams , taking advantage of existing tools such as PlantUML , Mermaid , and d2 to create living documentation for your services after each test run! The http4k team were excited to be invited back to KotlinConf 2023 , where we presented how we used the TracerBullet along with Hexagonal Design to visually document multi-service tests. You can check out the video of the presentation here or see a full example of how it works in the demo repo .","title":"TracerBullet: a brand new way of getting test feedback and documenting your systems"},{"location":"blog/http4k_v5/#remodelling_of_websocket_and_sse_support","text":"Various http4k backends have supported SSE and Websockets for a while, but whilst the innovation was that you could unit-test them entirely in-memory (a world first?), the models did have some restrictions in handling request filtering. As such we have reworked the support to be more consistent with the HTTP handling. This is a breaking change, but it very important for us to be able to more fully support these protocols.","title":"Remodelling of Websocket and SSE support"},{"location":"blog/http4k_v5/#removal_of_deprecated_and_unsupported_features","text":"As part of the major release cycle, we\u2019re removing all code marked as deprecated in v4 of http4k. We have also removed the http4k-templates-dust module due to the removal of Nashorn from the Java distribution. We understand this may impact some users, and we recommend seeking alternative solutions in the long term, but you can continue to use the existing http4k-template-dust assets which are still binary compatible with v5 of http4k.","title":"Removal of deprecated and unsupported features"},{"location":"blog/http4k_v5/#upgrading_from_http4k_v4","text":"If you are upgrading, the best idea is to first upgrade to the last v4 version, deal with any existing deprecations in place, then simply upgrade again the latest v5 version and repeat.","title":"Upgrading from http4k v4"},{"location":"blog/http4k_v5/#http4k-connect_updates","text":"http4k-connect was conceived as a library of providing client adapters for popular cloud services based on the innovative Connect pattern . v3 of the library added 20 different modules, and covered a number of popular AWS services, as well as integrating with Kafka via HTTP. The power of http4k-connect is it's ability to leverage Kotlin to provide modular adapters based around a common core. Each client endpoint is modeled as a separate \"Action\"? Is the action you want not supported by the core library, or do you want less information to be parsed? Simple - just write your own endpoint adapter and plug it in! Today with the v5 release, we're expanding the focus slightly - http4k-connect will now be the home of all official http4k platform integrations with third party services, to cover: Featherweight, zero-reflection adapters, for HTTP integrations with cloud services (eg. AWS, Confluent) Fake implementations of cloud services, which allow lightning-fast in-memory testing or local testing without Docker/LocalStack. A pluggable unified Storage interface with five different storage backends - including in-memory, Redis and S3, as well as a StorageExplorer that provides an OpenAPI UI to allow interaction with the storage via browser. Plugin SDKs for building integrations with popular platforms - starting with...","title":"http4k-connect Updates"},{"location":"blog/http4k_v5/#openaichatgpt_support","text":"It's 2023, and the new hotness that everyone is talking about is AI - and who are we to go against the weight of the hypemachine? After playing with GPT-4, one of the most frustrating things we found was the slowness of the responses and the effect that had on our testing-cycle, so the http4k team started to build a client adapter and fake OpenAI server so that Kotlin developer teams could integrate and test with OpenAI APIs quickly without waiting ages for the model to respond, and burning through those precious GPT-4 tokens and API limits. And thus http4k-connect-openai was born. The Fake server even allows you to generate images. A little later, the team received an early access invite to the ChatGPT plugin programme and it immediately occured that the requirements for developing plugins were all already supported within existing http4k libraries. So we set out to develop a plugin SDK, and http4k-connect-openai-plugin was born. The SDK provides a simple API for developers to simply compose their plugins by supplying some config and a standard http4k-contract endpoints, which generate the required OpenAPI specifications. The SDK does the rest for you, providing the required OpenAI plugin manifest, and then protects the API with one of 4 security models - including the ability to \"login with ChatGPT\" and OAuth into your plugin. Once created, plugins can be installed into the FakeOpenAI server, and expose a standard http4k HttpHandler which means they can be tested in memory, run locally standalone, or composed into part of a larger http4k application. It's pretty neat and we can't wait to see what the community uses it for! There's a full example of how to build a plugin with the http4k-connect-openai-plugin SDK and deploy it to AWS Lambda with Pulumi - see the repo for details.","title":"OpenAI/ChatGPT support"},{"location":"blog/http4k_v5/#removal_of_deprecated_and_unsupported_features_1","text":"As part of the major release cycle, we\u2019re removing all code marked as deprecated in v3 of http4k-connect.","title":"Removal of deprecated and unsupported features"},{"location":"blog/http4k_v5/#upgrading_from_http4k-connect_v3","text":"If you are upgrading, the best idea is to first upgrade to the last v3 version, deal with any existing deprecations in place, then simply upgrade again to the latest v5 version and repeat. And that's it (for now!). We hope you're as excited about these updates to the http4k platform as we are! This release brings a wealth of new features and improvements, and we believe it will make your experience using http4k technologies even better. Cheers","title":"Upgrading from http4k-connect v3"},{"location":"blog/http4k_v5/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/meet_http4k/","text":"Server as a Function. In Kotlin. Typesafe. Without the Server. \u00b6 november 2017 / @daviddenton \u00b6 Meet http4k \u00b6 http4k is an HTTP toolkit written in Kotlin that enables the serving and consuming of HTTP services in a functional and consistent way. Whenever (yet another) new JVM HTTP framework is released, the inevitable question that rightly get asked is \"How it this different to X?\" . In this post, I'm going to briefly cover what http4k is, how we think it's different, and address some of those bold claims from the title of this post. Here's a quick rundown of what we think those differences are: http4k is small. Written in pure, functional Kotlin, with zero dependencies. http4k is simple. Like, really simple. No static API magic, no annotations, no reflection. http4k is immutable. It relies on an immutable HTTP model, which makes it a snap to test and debug. http4k is symmetric. It supports remote calls as a first-class concern, and the remote HTTP model is identical to the incoming HTTP model. http4k is typesafe. Say goodbye to all your validation and marshalling boilerplate and hello to automatic request validation and data class-based contracts for HTTP bodies using the Lens API. http4k is serverless. Or rather - server independent. Test an app out of container and then deploy it into any supported local container with 1 LOC - or as a function into AWS Lambda. Oh god, not another framework! Why does this even exist?!? \u00b6 Firstly - we don't consider http4k to be a framework - it's a set of libraries providing a functional toolkit to serve and consume HTTP services, focusing on simple, consistent, and testable APIs. Hence, whilst it does provide support for various APIs relevant to serving and consuming HTTP , it does not provide every integration under the sun - merely simple points to allow those integrations to be hooked in. Another thing to say is that (not very much) of http4k is new - it's rather the distillation of 15 years worth of experience of using various server-side libraries and hence most of the good ideas are stolen. For instance - the routing module is inspired by UtterlyIdle , the basic \"Server as a function\" model is stolen from Finagle , and the contract module OpenApi/Swagger generator is ported from Fintrospect . With the growing adoption of Kotlin, we wanted something that would fully leverage the functional features of the language and it felt like a good time to start something from scratch, whilst avoiding the magic that plagues other frameworks. Hence, http4k is primarily designed to be a Kotlin-first library. Claim A: Small, simple, immutable. \u00b6 Based on the awesome \"Your Server as a Function\" paper from Twitter, http4k apps are modelled by composing 2 types of simple, independent function. Function 1: HttpHandler \u00b6 An HttpHandler represents an HTTP endpoint. It's not even an Interface, modelled merely as a Typealias : typealias HttpHandler = ( Request ) -> Response Below is a entire http4k application that echoes the request body back in the response. It only relies on the http4k-core module, which itself has zero dependencies: val app = { request : Request -> Response ( OK ). body ( request . body ) } val server = app . asServer ( SunHttp ( 8000 )). start () The Request and Response objects in there are immutable data classes/POKOs, so testing the app requires absolutely no extra infrastructure - just call the function, it's as easy as: class AppTest { @Test fun `echoes request body` () { assertThat ( app ( Request ( POST , \"/\" ). body ( \"hello\" )), equalTo ( Response ( OK ). body ( \"hello\" ))) } } To plug it into a different Server-backend, just depend on the relevant module (Jetty, Undertow, Netty, Apache (httpcore), Ktor CIO, Ktor Netty, and SunHttp are available) and change the call to asServer() . Function 2: Filter \u00b6 Filters provides pre and post Request processing and are simply: interface Filter : ( HttpHandler ) -> HttpHandler For API conciseness and discoverability reasons this is modelled as an Interface and not a Typealias - it also has a couple of Kotlin extension methods to allow you to compose Filters with HttpHandlers and other Filters : val setContentType = Filter { next -> { request -> next ( request ). header ( \"Content-Type\" , \"text/plain\" ) } } val repeatBody = Filter { next -> { request -> next ( request . body ( request . bodyString () + request . bodyString ())) } } val composedFilter : Filter = repeatBody . then ( setContentType ) val decoratedApp : HttpHandler = composedFilter . then ( app ) Filters are also trivial to test independently, because they are generally just stateless functions. Routing \u00b6 http4k's nestable routing looks a lot like every other Sinatra-style framework these days, and allows for infinitely nesting HttpHandlers - this just exposes another HttpHandler so you can easily extract, test and reuse sets of routes as easily as you could with one: val app : HttpHandler = routes ( \"/app\" bind GET to decoratedApp , \"/other\" bind routes ( \"/delete\" bind DELETE to { _ : Request -> Response ( OK ) }, \"/post/{name}\" bind POST to { request : Request -> Response ( OK ). body ( \"you POSTed to ${ request . path ( \" name \" ) } \" ) } ) ) And that it - those functions are everything you need to know to write a simple http4k application. The http4k-core module rocks in at about 700kb, and has zero dependencies (other than the Kotlin language itself). Additionally, everything in the core is functional and predictable - there is no static API magic going on under the covers (making it difficult to have multiple apps in the same JVM), no annotations, no compiler-plugins, and no reflection. Claim B. Symmetric HTTP \u00b6 Out of the multitude of JVM http frameworks out there, not many actually consider how you app talks to other services, yet in this Microservice\u2122 world that's an absolutely massive part of what many apps do! As per a core principle behind \"Server as a Function\", http4k provides a symmetric API for HTTP clients - ie. it's exactly the same API as is exposed in http4k server applications - the HttpHandler . Here's that entire API again, just in case you've forgotten: typealias HttpHandler = ( Request ) -> Response What does that mean in practice? Well - for one thing, it's less for your brain to learn because you already know the API: val client : HttpHandler = ApacheClient () val response : Response = client ( Request ( GET , \"http://server/path\" )) For another, it means that since clients are just function s you can easily stub them for testing, and since applications and clients are interchangeable, they can be plugged together in memory without putting them on the network - which makes testing insanely fast: fun MyApp1 (): HttpHandler = { Response ( OK ) } fun MyApp2 ( app1 : HttpHandler ): HttpHandler = { app1 ( it ) } val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 ) http4k provides a HTTP client adapters for both Apache and OkHttp , all with streaming support. Claim C. Typesafe HTTP with Lenses \u00b6 The immutable http4k model for HTTP objects contains all the usual suspect methods for getting values from the messages. For instance, if we are expecting a search parameter with a query containing a page number: val request = Request ( GET , \"http://server/search?page=123\" ) val page : Int = request . query ( \"page\" ) !! . toInt ...but we also want to ensure that the expected values are both present and valid, since the above example will fail if either of those things is not true. For this purpose, we can use a Lens to enforce the expected HTTP contract. The use of Lenses in http4k applications can remove the need for writing any parsing or validation code for all incoming data (including Forms), as validations are taken care of by the library. Lens basics \u00b6 A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional) and the type. For the above example, we could use the Query Lens builder and then invoke() the Lens on the message to extract the target value: val pageLens = Query . int (). required ( \"page\" ) val page : Int = pageLens ( Request ( GET , \"http://server/search?page=123\" )) If the query parameter is missing or not an Int, the lens extraction operation will fail. There are similar Lens builder functions for all parts of the HTTP message ( Header , Path , Body , FormField etc..), and functions for all common JVM primitive types. They are all completely typesafe - there is no reflection or magic going on - just marshalling of the various entities (in this case String to Int conversion). In the case of failure, we need to apply a Filter to detect the errors and convert them to a BAD_REQUEST response: val queryName = Query . string (). required ( \"name\" ) val app : HttpHandler = routes ( \"/post\" bind POST to { request : Request -> Response ( OK ). body ( queryName ( request )) } ) val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" )) Lenses can also be applied with a correctly typed value (via invoke() ) to set it onto a target object - and as HTTP messages in http4k are immutable, this results in a copy of the modified message: val pageSizeLens = Header . int (). required ( \"page\" ) val page : Response = pageLens ( Response ( OK ), 123 ) // or apply multiple lenses using with() val updated : Request = Request ( GET , \"/foo\" ). with ( pageLens of 123 , pageSizeLens of 10 ) Securely extracting JDK primitives from HTTP messages is great, but we really want to avoid primitives entirely and go straight to domain types. During construction of Lens, the builders allow mapping to occur so we can leverage Kotlin data classes. This works for both get and set operations: data class MyDate ( val value : LocalDate ) val dateQuery = Query . localDate (). map ( :: MyDate , MyDate :: value ). required ( \"date\" ) val myDate : MyDate = dateQuery ( Request ( GET , \"http://server/search?date=2000-01-01\" )) Lensing HTTP bodies with Data classes \u00b6 Some of the supported message libraries (eg. GSON, Jackson, Moshi, XML) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection (oops - looks like we broke our reflection promise - but technically we're not doing it ;) !). This behaviour is supported in http4k Lenses through the use of the auto() method, which will marshall objects to/from HTTP messages: data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) val messageLens = Body . auto < Message > (). toLens () val body = \"\"\"{\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}}\"\"\" val message : Message = messageLens ( Request ( GET , \"/\" ). body ( body )) This mechanism works for all incoming and outgoing JSON and XML Requests and Responses. To assist with developing whilst using this type of auto-marshalling, we have created a tool to automatically generate a set of data classes for a given messages. Claim D. Serverless \u00b6 Ah yes - Serverless - the latest in the Cool Kids Club and killer fodder for the resume. Well, since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. In order to achieve this, only a single interface AppLoader needs to be implemented - this is responsible for creating the HttpHandler which is adapted to the API of the ApiGatewayProxyRequest/ApiGatewayProxyResponse used by AWS, and then a single class ApiGatewayV2LambdaFunction extended. As this is AWS, there is a fair amount of configuration required to make this possible, but the only http4k specific config is to set the function execution to call org.http4k.MyLambdaFunction Here's a simple example: object TweetEcho : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 140 )) } } class MyLambdaFunction : ApiGatewayV2LambdaFunction ( TweetEcho ) Since http4k is very dependency-light, full binary uploads of these AWS Lambdas tend to be very small - and by utilising Proguard we've seen the size of a Lambda UberJar go as small as 150kb. Introduced in v3.0.0, this support is available in the http4k-serverless-lambda module. The final word(s)! \u00b6 As pointed out above, http4k-core module has zero dependencies. It is also small, even though it also provides: Support for static file-serving with HotReload. A bunch of useful Filters for stuff like Zipkin Request Tracing. Support for Request Contexts. Facilities to record and replay HTTP traffic. There are also a bunch of other modules available, all presented with the same concentration on Testability, API simplicity and consistency: ViewModel driven templating engine support (Handlerbars etc) with HotReload. Popular JSON/XML (Gson, Jackson, Moshi, etc) library support for HTTP bodies. Typesafe HTML Form and Multipart Forms processing, with support for Streaming uploads to a storage service. Forms can also be configured to collect errors instead of just rejecting outright. Typesafe contract module, providing live [OpenApi v2 & v3] documentation. AWS request signing. Resilience4j integration, including Circuit Breakers & Rate Limiting. Testing support via Hamkrest matchers and an in-memory WebDriver implementation. Finally, http4k is proven in production , it has been adopted in at least 2 global investment banks and is serving the vast majority of traffic for a major publishing website (in the top 1000 sites globally according to alexa.com - ie. easily serving 10s of million hits per day on a few nodes) since March 2017. You can see a few example applications here , including a bootstrap project for creating a Github -> Travis -> Heroku CD pipeline in a single command. Well, that's it for this whirlwind tour - we hope you found it worth reading this far! We'd love you to try out http4k and feedback why you love/hate/are indifferent to it :) . And if you want to get involved or chat to the authors, we hang out in the friendly #http4k channel @ slack.kotlinlang,org . Footnotes \u00b6 \"But... but... but... asynchronous! And Webscale!\" , I heard them froth . Yes, you are correct - \"Server as a Function\" is based on asynchronous functions and http4k exposes a synchronous API. However, our experience suggests that for the vast majority of apps, this actually makes API integration harder unless you've got async all the way down - and that is assuming that async clients are actually available for all your various remote dependencies. We found that this plainly didn't matter for our use-cases so went for Simple API\u2122 instead... it's possible however that Kotlin co-routines will allow us to revisit this decision. (UPDATE) Websockets? Yep - simple, testable, and now available in v3.2.1! See the introductory blog post for details!","title":"Meet http4k"},{"location":"blog/meet_http4k/#server_as_a_function_in_kotlin_typesafe_without_the_server","text":"","title":"Server as a Function. In Kotlin. Typesafe. Without the Server."},{"location":"blog/meet_http4k/#november_2017_daviddenton","text":"","title":"november 2017 / @daviddenton"},{"location":"blog/meet_http4k/#meet_http4k","text":"http4k is an HTTP toolkit written in Kotlin that enables the serving and consuming of HTTP services in a functional and consistent way. Whenever (yet another) new JVM HTTP framework is released, the inevitable question that rightly get asked is \"How it this different to X?\" . In this post, I'm going to briefly cover what http4k is, how we think it's different, and address some of those bold claims from the title of this post. Here's a quick rundown of what we think those differences are: http4k is small. Written in pure, functional Kotlin, with zero dependencies. http4k is simple. Like, really simple. No static API magic, no annotations, no reflection. http4k is immutable. It relies on an immutable HTTP model, which makes it a snap to test and debug. http4k is symmetric. It supports remote calls as a first-class concern, and the remote HTTP model is identical to the incoming HTTP model. http4k is typesafe. Say goodbye to all your validation and marshalling boilerplate and hello to automatic request validation and data class-based contracts for HTTP bodies using the Lens API. http4k is serverless. Or rather - server independent. Test an app out of container and then deploy it into any supported local container with 1 LOC - or as a function into AWS Lambda.","title":"Meet http4k"},{"location":"blog/meet_http4k/#oh_god_not_another_framework_why_does_this_even_exist","text":"Firstly - we don't consider http4k to be a framework - it's a set of libraries providing a functional toolkit to serve and consume HTTP services, focusing on simple, consistent, and testable APIs. Hence, whilst it does provide support for various APIs relevant to serving and consuming HTTP , it does not provide every integration under the sun - merely simple points to allow those integrations to be hooked in. Another thing to say is that (not very much) of http4k is new - it's rather the distillation of 15 years worth of experience of using various server-side libraries and hence most of the good ideas are stolen. For instance - the routing module is inspired by UtterlyIdle , the basic \"Server as a function\" model is stolen from Finagle , and the contract module OpenApi/Swagger generator is ported from Fintrospect . With the growing adoption of Kotlin, we wanted something that would fully leverage the functional features of the language and it felt like a good time to start something from scratch, whilst avoiding the magic that plagues other frameworks. Hence, http4k is primarily designed to be a Kotlin-first library.","title":"Oh god, not another framework! Why does this even exist?!?"},{"location":"blog/meet_http4k/#claim_a_small_simple_immutable","text":"Based on the awesome \"Your Server as a Function\" paper from Twitter, http4k apps are modelled by composing 2 types of simple, independent function.","title":"Claim A: Small, simple, immutable."},{"location":"blog/meet_http4k/#function_1_httphandler","text":"An HttpHandler represents an HTTP endpoint. It's not even an Interface, modelled merely as a Typealias : typealias HttpHandler = ( Request ) -> Response Below is a entire http4k application that echoes the request body back in the response. It only relies on the http4k-core module, which itself has zero dependencies: val app = { request : Request -> Response ( OK ). body ( request . body ) } val server = app . asServer ( SunHttp ( 8000 )). start () The Request and Response objects in there are immutable data classes/POKOs, so testing the app requires absolutely no extra infrastructure - just call the function, it's as easy as: class AppTest { @Test fun `echoes request body` () { assertThat ( app ( Request ( POST , \"/\" ). body ( \"hello\" )), equalTo ( Response ( OK ). body ( \"hello\" ))) } } To plug it into a different Server-backend, just depend on the relevant module (Jetty, Undertow, Netty, Apache (httpcore), Ktor CIO, Ktor Netty, and SunHttp are available) and change the call to asServer() .","title":"Function 1: HttpHandler"},{"location":"blog/meet_http4k/#function_2_filter","text":"Filters provides pre and post Request processing and are simply: interface Filter : ( HttpHandler ) -> HttpHandler For API conciseness and discoverability reasons this is modelled as an Interface and not a Typealias - it also has a couple of Kotlin extension methods to allow you to compose Filters with HttpHandlers and other Filters : val setContentType = Filter { next -> { request -> next ( request ). header ( \"Content-Type\" , \"text/plain\" ) } } val repeatBody = Filter { next -> { request -> next ( request . body ( request . bodyString () + request . bodyString ())) } } val composedFilter : Filter = repeatBody . then ( setContentType ) val decoratedApp : HttpHandler = composedFilter . then ( app ) Filters are also trivial to test independently, because they are generally just stateless functions.","title":"Function 2: Filter"},{"location":"blog/meet_http4k/#routing","text":"http4k's nestable routing looks a lot like every other Sinatra-style framework these days, and allows for infinitely nesting HttpHandlers - this just exposes another HttpHandler so you can easily extract, test and reuse sets of routes as easily as you could with one: val app : HttpHandler = routes ( \"/app\" bind GET to decoratedApp , \"/other\" bind routes ( \"/delete\" bind DELETE to { _ : Request -> Response ( OK ) }, \"/post/{name}\" bind POST to { request : Request -> Response ( OK ). body ( \"you POSTed to ${ request . path ( \" name \" ) } \" ) } ) ) And that it - those functions are everything you need to know to write a simple http4k application. The http4k-core module rocks in at about 700kb, and has zero dependencies (other than the Kotlin language itself). Additionally, everything in the core is functional and predictable - there is no static API magic going on under the covers (making it difficult to have multiple apps in the same JVM), no annotations, no compiler-plugins, and no reflection.","title":"Routing"},{"location":"blog/meet_http4k/#claim_b_symmetric_http","text":"Out of the multitude of JVM http frameworks out there, not many actually consider how you app talks to other services, yet in this Microservice\u2122 world that's an absolutely massive part of what many apps do! As per a core principle behind \"Server as a Function\", http4k provides a symmetric API for HTTP clients - ie. it's exactly the same API as is exposed in http4k server applications - the HttpHandler . Here's that entire API again, just in case you've forgotten: typealias HttpHandler = ( Request ) -> Response What does that mean in practice? Well - for one thing, it's less for your brain to learn because you already know the API: val client : HttpHandler = ApacheClient () val response : Response = client ( Request ( GET , \"http://server/path\" )) For another, it means that since clients are just function s you can easily stub them for testing, and since applications and clients are interchangeable, they can be plugged together in memory without putting them on the network - which makes testing insanely fast: fun MyApp1 (): HttpHandler = { Response ( OK ) } fun MyApp2 ( app1 : HttpHandler ): HttpHandler = { app1 ( it ) } val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 ) http4k provides a HTTP client adapters for both Apache and OkHttp , all with streaming support.","title":"Claim B. Symmetric HTTP"},{"location":"blog/meet_http4k/#claim_c_typesafe_http_with_lenses","text":"The immutable http4k model for HTTP objects contains all the usual suspect methods for getting values from the messages. For instance, if we are expecting a search parameter with a query containing a page number: val request = Request ( GET , \"http://server/search?page=123\" ) val page : Int = request . query ( \"page\" ) !! . toInt ...but we also want to ensure that the expected values are both present and valid, since the above example will fail if either of those things is not true. For this purpose, we can use a Lens to enforce the expected HTTP contract. The use of Lenses in http4k applications can remove the need for writing any parsing or validation code for all incoming data (including Forms), as validations are taken care of by the library.","title":"Claim C. Typesafe HTTP with Lenses"},{"location":"blog/meet_http4k/#lens_basics","text":"A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional) and the type. For the above example, we could use the Query Lens builder and then invoke() the Lens on the message to extract the target value: val pageLens = Query . int (). required ( \"page\" ) val page : Int = pageLens ( Request ( GET , \"http://server/search?page=123\" )) If the query parameter is missing or not an Int, the lens extraction operation will fail. There are similar Lens builder functions for all parts of the HTTP message ( Header , Path , Body , FormField etc..), and functions for all common JVM primitive types. They are all completely typesafe - there is no reflection or magic going on - just marshalling of the various entities (in this case String to Int conversion). In the case of failure, we need to apply a Filter to detect the errors and convert them to a BAD_REQUEST response: val queryName = Query . string (). required ( \"name\" ) val app : HttpHandler = routes ( \"/post\" bind POST to { request : Request -> Response ( OK ). body ( queryName ( request )) } ) val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" )) Lenses can also be applied with a correctly typed value (via invoke() ) to set it onto a target object - and as HTTP messages in http4k are immutable, this results in a copy of the modified message: val pageSizeLens = Header . int (). required ( \"page\" ) val page : Response = pageLens ( Response ( OK ), 123 ) // or apply multiple lenses using with() val updated : Request = Request ( GET , \"/foo\" ). with ( pageLens of 123 , pageSizeLens of 10 ) Securely extracting JDK primitives from HTTP messages is great, but we really want to avoid primitives entirely and go straight to domain types. During construction of Lens, the builders allow mapping to occur so we can leverage Kotlin data classes. This works for both get and set operations: data class MyDate ( val value : LocalDate ) val dateQuery = Query . localDate (). map ( :: MyDate , MyDate :: value ). required ( \"date\" ) val myDate : MyDate = dateQuery ( Request ( GET , \"http://server/search?date=2000-01-01\" ))","title":"Lens basics"},{"location":"blog/meet_http4k/#lensing_http_bodies_with_data_classes","text":"Some of the supported message libraries (eg. GSON, Jackson, Moshi, XML) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection (oops - looks like we broke our reflection promise - but technically we're not doing it ;) !). This behaviour is supported in http4k Lenses through the use of the auto() method, which will marshall objects to/from HTTP messages: data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) val messageLens = Body . auto < Message > (). toLens () val body = \"\"\"{\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}}\"\"\" val message : Message = messageLens ( Request ( GET , \"/\" ). body ( body )) This mechanism works for all incoming and outgoing JSON and XML Requests and Responses. To assist with developing whilst using this type of auto-marshalling, we have created a tool to automatically generate a set of data classes for a given messages.","title":"Lensing HTTP bodies with Data classes"},{"location":"blog/meet_http4k/#claim_d_serverless","text":"Ah yes - Serverless - the latest in the Cool Kids Club and killer fodder for the resume. Well, since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. In order to achieve this, only a single interface AppLoader needs to be implemented - this is responsible for creating the HttpHandler which is adapted to the API of the ApiGatewayProxyRequest/ApiGatewayProxyResponse used by AWS, and then a single class ApiGatewayV2LambdaFunction extended. As this is AWS, there is a fair amount of configuration required to make this possible, but the only http4k specific config is to set the function execution to call org.http4k.MyLambdaFunction Here's a simple example: object TweetEcho : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 140 )) } } class MyLambdaFunction : ApiGatewayV2LambdaFunction ( TweetEcho ) Since http4k is very dependency-light, full binary uploads of these AWS Lambdas tend to be very small - and by utilising Proguard we've seen the size of a Lambda UberJar go as small as 150kb. Introduced in v3.0.0, this support is available in the http4k-serverless-lambda module.","title":"Claim D. Serverless"},{"location":"blog/meet_http4k/#the_final_words","text":"As pointed out above, http4k-core module has zero dependencies. It is also small, even though it also provides: Support for static file-serving with HotReload. A bunch of useful Filters for stuff like Zipkin Request Tracing. Support for Request Contexts. Facilities to record and replay HTTP traffic. There are also a bunch of other modules available, all presented with the same concentration on Testability, API simplicity and consistency: ViewModel driven templating engine support (Handlerbars etc) with HotReload. Popular JSON/XML (Gson, Jackson, Moshi, etc) library support for HTTP bodies. Typesafe HTML Form and Multipart Forms processing, with support for Streaming uploads to a storage service. Forms can also be configured to collect errors instead of just rejecting outright. Typesafe contract module, providing live [OpenApi v2 & v3] documentation. AWS request signing. Resilience4j integration, including Circuit Breakers & Rate Limiting. Testing support via Hamkrest matchers and an in-memory WebDriver implementation. Finally, http4k is proven in production , it has been adopted in at least 2 global investment banks and is serving the vast majority of traffic for a major publishing website (in the top 1000 sites globally according to alexa.com - ie. easily serving 10s of million hits per day on a few nodes) since March 2017. You can see a few example applications here , including a bootstrap project for creating a Github -> Travis -> Heroku CD pipeline in a single command. Well, that's it for this whirlwind tour - we hope you found it worth reading this far! We'd love you to try out http4k and feedback why you love/hate/are indifferent to it :) . And if you want to get involved or chat to the authors, we hang out in the friendly #http4k channel @ slack.kotlinlang,org .","title":"The final word(s)!"},{"location":"blog/meet_http4k/#footnotes","text":"\"But... but... but... asynchronous! And Webscale!\" , I heard them froth . Yes, you are correct - \"Server as a Function\" is based on asynchronous functions and http4k exposes a synchronous API. However, our experience suggests that for the vast majority of apps, this actually makes API integration harder unless you've got async all the way down - and that is assuming that async clients are actually available for all your various remote dependencies. We found that this plainly didn't matter for our use-cases so went for Simple API\u2122 instead... it's possible however that Kotlin co-routines will allow us to revisit this decision. (UPDATE) Websockets? Yep - simple, testable, and now available in v3.2.1! See the introductory blog post for details!","title":"Footnotes"},{"location":"blog/nanoservices/","text":"Nanoservices: The Power of Composition \u00b6 october 2020 / @daviddenton \u00b6 http4k is a small library with a zero dependencies (apart from Kotlin StdLib), but what really makes it shine is the power afforded by the combination of the \"Server as a Function\" concepts of HttpHandler and Filter . Skeptical? We would be disappointed if you weren't! Hence, we decided to prove the types of things that can be accomplished with the APIs provided by http4k and a little ingenuity. For each of the examples below, there is a fully formed http4k application declared inside a function, and the scaffolding to demonstrating it working in an accompanying main() using one of the swappable server backends. Even better, each of app's code (excluding import statements \ud83d\ude42 ) fits in a single Tweet. 1. Build a simple proxy \u00b6 Requires: http4k-core This simple proxy converts HTTP requests to HTTPS. Because of the symmetrical server/client HttpHandler signature, we can simply pipe an HTTP Client onto a server, then add a ProxyHost filter to do the protocol conversion. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `simple proxy` () = JavaHttpClient () . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `simple proxy` (). use { println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" ))) } } 2. Report latency through a proxy \u00b6 Requires: http4k-core Building on the Simple Proxy example, we can simply layer on extra filters to add features to the proxy, in this case reporting the latency of each call. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportRouteLatency import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency reporting proxy` () = ProxyHost ( Https ) . then ( ReportRouteLatency { req , ms -> println ( \" $ req took $ ms \" ) }) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency reporting proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) } } 3. Build a Wireshark to sniff inter-service traffic \u00b6 Requires: http4k-core Applying a DebuggingFilter to the HTTP calls in a proxy dumps the entire contents out to StdOut (or other stream). package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `wire sniffing proxy` () = ProxyHost ( Https ) . then ( PrintRequestAndResponse ()) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `wire sniffing proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) } } 4. Build a ticking Websocket clock \u00b6 Requires: http4k-core , http4k-server-netty Like HTTP handlers, Websockets in http4k can be modelled as simple functions that can be mounted onto a Server, or combined with path patterns if required. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.time.Instant fun `ticking websocket clock` () = { ws : Websocket -> while ( true ) { ws . send ( WsMessage ( Instant . now (). toString ())) Thread . sleep ( 1000 ) } }. asServer ( Netty ()). start () fun main () { `ticking websocket clock` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } } 5. Build a web cache \u00b6 Requires: http4k-core , http4k-server-ktorcio Recording all traffic to disk can be achieved by just creating a ReadWriteCache and then adding a couple of pre-supplied Filters to a proxy. When running this example you can see that only the first request is audited. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.filter.TrafficFilters.ServeCachedFrom import org.http4k.server.Http4kServer import org.http4k.server.KtorCIO import org.http4k.server.asServer import org.http4k.traffic.ReadWriteCache import java.io.File fun `disk cache!` ( dir : String ): Http4kServer { val cache = ReadWriteCache . Disk ( dir ) return ProxyHost ( Https ) . then ( RecordTo ( cache )) . then ( ServeCachedFrom ( cache )) . then ( ReportHttpTransaction { println ( it . request . uri ) }) . then ( JavaHttpClient ()) . asServer ( KtorCIO ()) . start () } fun main () { System . setProperty ( \"http.proxyHost\" , \"localhost\" ) System . setProperty ( \"http.proxyPort\" , \"8000\" ) System . setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) val client = JavaHttpClient () val dir = \"store\" File ( dir ). deleteRecursively () `disk cache!` ( dir ). use { val request = Request ( GET , \"http://api.github.com/users/http4k\" ) println ( client ( request ). bodyString ()) // this request is served from the cache, so will not generate a call println ( client ( request ). bodyString ()) } } 6. Record all traffic to disk and replay it later \u00b6 Requires: http4k-core This example contains two apps. The first is a proxy which captures streams of traffic and records it to a directory on disk. The second app is configured to replay the requests from that disk store at the original server. This kind of traffic capture/replay is very useful for load testing or for tracking down hard-to-diagnose bugs - and it's easy to write other other stores such as an S3 bucket etc. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.traffic.ReadWriteStream.Companion.Disk import java.lang.System.setProperty fun `recording traffic to disk proxy` () = ProxyHost ( Https ) . then ( RecordTo ( Disk ( \"store\" ))) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun `replay previously recorded traffic from a disk store` () = JavaHttpClient (). let { client -> Disk ( \"store\" ). requests () . forEach { println ( it ) client ( it ) } } fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `recording traffic to disk proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k/http4k\" )) } `replay previously recorded traffic from a disk store` () } 7. Watch your FS for file changes \u00b6 Requires: http4k-core , http4k-server-undertow Back to Websockets, we can watch the file system for changes and subscribe to the event feed. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.nio.file.FileSystems.getDefault import java.nio.file.Paths import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY fun `file watcher` () = { ws : Websocket -> val w = getDefault (). newWatchService () Paths . get ( \"\" ). register ( w , ENTRY_MODIFY ) val key = w . take () while ( true ) key . pollEvents () . forEach { ws . send ( WsMessage ( it . context (). toString ())) } }. asServer ( Jetty ()). start () fun main () { `file watcher` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } } 8. Serve static files from disk \u00b6 Requires: http4k-core , http4k-server-undertow Longer than the Python SimpleHttpServer , but still pretty small! package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.routing.ResourceLoader.Companion.Directory import org.http4k.routing.static import org.http4k.server.Undertow import org.http4k.server.asServer fun `static file server` () = static ( Directory ()) . asServer ( Undertow ()) . start () fun main () { `static file server` (). use { // by default, static servers will only serve known file types, // or those registered on construction println ( JavaHttpClient ()( Request ( GET , \"http://localhost:8000/version.json\" ))) } } 9. Build your own ChaosMonkey \u00b6 Requires: http4k-core , http4k-testing-chaos As per the [Principles of Chaos], this proxy adds Chaotic behaviour to a remote service, which is useful for modelling how a system might behave under various failure modes. Chaos can be dynamically injected via an OpenApi documented set of RPC endpoints. package blog.nanoservices import org.http4k.chaos.ChaosBehaviours.Latency import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency injection proxy (between 100ms-500ms)` () = ProxyHost ( Https ) . then ( JavaHttpClient ()) . withChaosApi ( ChaosEngine ( Latency ()). enable ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency injection proxy (between 100ms-500ms)` (). use { println ( JavaHttpClient ()( Request ( POST , \"http://localhost:8000/chaos/activate\" ))) println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" )). header ( \"X-http4k-chaos\" )) } } 10. Build a remote terminal! \u00b6 Requires: http4k-core , http4k-server-netty Use Websockets to remote control a terminal!* Run the example and just type commands into the prompt to have them magicked to the server backend *Obviously this is, in general, a really (really) bad idea. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.lang.Runtime.getRuntime import java.util.Scanner @Suppress ( \"DEPRECATION\" ) fun `websocket terminal` () = { ws : Websocket -> ws . onMessage { val text = getRuntime (). exec ( it . bodyString ()) . inputStream . reader () . readText () ws . send ( WsMessage ( text )) } }. asServer ( Netty ()). start () fun main () { `websocket terminal` () val ws = WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )) ws . onMessage { println ( it . bodyString ()) } val scan = Scanner ( System . `in` ) while ( true ) { ws . send ( WsMessage ( scan . nextLine ())) } } Obviously we haven't thought of everything here. We'd love to hear your ideas about other clever uses of the http4k building blocks, or to take PRs to integrate them into the library for wider use. You can get in touch through GitHub or the usual channels . Principles of Chaos","title":"Nanoservices"},{"location":"blog/nanoservices/#nanoservices_the_power_of_composition","text":"","title":"Nanoservices: The Power of Composition"},{"location":"blog/nanoservices/#october_2020_daviddenton","text":"http4k is a small library with a zero dependencies (apart from Kotlin StdLib), but what really makes it shine is the power afforded by the combination of the \"Server as a Function\" concepts of HttpHandler and Filter . Skeptical? We would be disappointed if you weren't! Hence, we decided to prove the types of things that can be accomplished with the APIs provided by http4k and a little ingenuity. For each of the examples below, there is a fully formed http4k application declared inside a function, and the scaffolding to demonstrating it working in an accompanying main() using one of the swappable server backends. Even better, each of app's code (excluding import statements \ud83d\ude42 ) fits in a single Tweet.","title":"october 2020 / @daviddenton"},{"location":"blog/nanoservices/#1_build_a_simple_proxy","text":"Requires: http4k-core This simple proxy converts HTTP requests to HTTPS. Because of the symmetrical server/client HttpHandler signature, we can simply pipe an HTTP Client onto a server, then add a ProxyHost filter to do the protocol conversion. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `simple proxy` () = JavaHttpClient () . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `simple proxy` (). use { println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" ))) } }","title":"1. Build a simple proxy"},{"location":"blog/nanoservices/#2_report_latency_through_a_proxy","text":"Requires: http4k-core Building on the Simple Proxy example, we can simply layer on extra filters to add features to the proxy, in this case reporting the latency of each call. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportRouteLatency import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency reporting proxy` () = ProxyHost ( Https ) . then ( ReportRouteLatency { req , ms -> println ( \" $ req took $ ms \" ) }) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency reporting proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) } }","title":"2. Report latency through a proxy"},{"location":"blog/nanoservices/#3_build_a_wireshark_to_sniff_inter-service_traffic","text":"Requires: http4k-core Applying a DebuggingFilter to the HTTP calls in a proxy dumps the entire contents out to StdOut (or other stream). package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `wire sniffing proxy` () = ProxyHost ( Https ) . then ( PrintRequestAndResponse ()) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `wire sniffing proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) } }","title":"3. Build a Wireshark to sniff inter-service traffic"},{"location":"blog/nanoservices/#4_build_a_ticking_websocket_clock","text":"Requires: http4k-core , http4k-server-netty Like HTTP handlers, Websockets in http4k can be modelled as simple functions that can be mounted onto a Server, or combined with path patterns if required. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.time.Instant fun `ticking websocket clock` () = { ws : Websocket -> while ( true ) { ws . send ( WsMessage ( Instant . now (). toString ())) Thread . sleep ( 1000 ) } }. asServer ( Netty ()). start () fun main () { `ticking websocket clock` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } }","title":"4. Build a ticking Websocket clock"},{"location":"blog/nanoservices/#5_build_a_web_cache","text":"Requires: http4k-core , http4k-server-ktorcio Recording all traffic to disk can be achieved by just creating a ReadWriteCache and then adding a couple of pre-supplied Filters to a proxy. When running this example you can see that only the first request is audited. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.filter.TrafficFilters.ServeCachedFrom import org.http4k.server.Http4kServer import org.http4k.server.KtorCIO import org.http4k.server.asServer import org.http4k.traffic.ReadWriteCache import java.io.File fun `disk cache!` ( dir : String ): Http4kServer { val cache = ReadWriteCache . Disk ( dir ) return ProxyHost ( Https ) . then ( RecordTo ( cache )) . then ( ServeCachedFrom ( cache )) . then ( ReportHttpTransaction { println ( it . request . uri ) }) . then ( JavaHttpClient ()) . asServer ( KtorCIO ()) . start () } fun main () { System . setProperty ( \"http.proxyHost\" , \"localhost\" ) System . setProperty ( \"http.proxyPort\" , \"8000\" ) System . setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) val client = JavaHttpClient () val dir = \"store\" File ( dir ). deleteRecursively () `disk cache!` ( dir ). use { val request = Request ( GET , \"http://api.github.com/users/http4k\" ) println ( client ( request ). bodyString ()) // this request is served from the cache, so will not generate a call println ( client ( request ). bodyString ()) } }","title":"5. Build a web cache"},{"location":"blog/nanoservices/#6_record_all_traffic_to_disk_and_replay_it_later","text":"Requires: http4k-core This example contains two apps. The first is a proxy which captures streams of traffic and records it to a directory on disk. The second app is configured to replay the requests from that disk store at the original server. This kind of traffic capture/replay is very useful for load testing or for tracking down hard-to-diagnose bugs - and it's easy to write other other stores such as an S3 bucket etc. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.traffic.ReadWriteStream.Companion.Disk import java.lang.System.setProperty fun `recording traffic to disk proxy` () = ProxyHost ( Https ) . then ( RecordTo ( Disk ( \"store\" ))) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun `replay previously recorded traffic from a disk store` () = JavaHttpClient (). let { client -> Disk ( \"store\" ). requests () . forEach { println ( it ) client ( it ) } } fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `recording traffic to disk proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k/http4k\" )) } `replay previously recorded traffic from a disk store` () }","title":"6. Record all traffic to disk and replay it later"},{"location":"blog/nanoservices/#7_watch_your_fs_for_file_changes","text":"Requires: http4k-core , http4k-server-undertow Back to Websockets, we can watch the file system for changes and subscribe to the event feed. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.nio.file.FileSystems.getDefault import java.nio.file.Paths import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY fun `file watcher` () = { ws : Websocket -> val w = getDefault (). newWatchService () Paths . get ( \"\" ). register ( w , ENTRY_MODIFY ) val key = w . take () while ( true ) key . pollEvents () . forEach { ws . send ( WsMessage ( it . context (). toString ())) } }. asServer ( Jetty ()). start () fun main () { `file watcher` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } }","title":"7. Watch your FS for file changes"},{"location":"blog/nanoservices/#8_serve_static_files_from_disk","text":"Requires: http4k-core , http4k-server-undertow Longer than the Python SimpleHttpServer , but still pretty small! package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.routing.ResourceLoader.Companion.Directory import org.http4k.routing.static import org.http4k.server.Undertow import org.http4k.server.asServer fun `static file server` () = static ( Directory ()) . asServer ( Undertow ()) . start () fun main () { `static file server` (). use { // by default, static servers will only serve known file types, // or those registered on construction println ( JavaHttpClient ()( Request ( GET , \"http://localhost:8000/version.json\" ))) } }","title":"8. Serve static files from disk"},{"location":"blog/nanoservices/#9_build_your_own_chaosmonkey","text":"Requires: http4k-core , http4k-testing-chaos As per the [Principles of Chaos], this proxy adds Chaotic behaviour to a remote service, which is useful for modelling how a system might behave under various failure modes. Chaos can be dynamically injected via an OpenApi documented set of RPC endpoints. package blog.nanoservices import org.http4k.chaos.ChaosBehaviours.Latency import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency injection proxy (between 100ms-500ms)` () = ProxyHost ( Https ) . then ( JavaHttpClient ()) . withChaosApi ( ChaosEngine ( Latency ()). enable ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency injection proxy (between 100ms-500ms)` (). use { println ( JavaHttpClient ()( Request ( POST , \"http://localhost:8000/chaos/activate\" ))) println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" )). header ( \"X-http4k-chaos\" )) } }","title":"9. Build your own ChaosMonkey"},{"location":"blog/nanoservices/#10_build_a_remote_terminal","text":"Requires: http4k-core , http4k-server-netty Use Websockets to remote control a terminal!* Run the example and just type commands into the prompt to have them magicked to the server backend *Obviously this is, in general, a really (really) bad idea. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.lang.Runtime.getRuntime import java.util.Scanner @Suppress ( \"DEPRECATION\" ) fun `websocket terminal` () = { ws : Websocket -> ws . onMessage { val text = getRuntime (). exec ( it . bodyString ()) . inputStream . reader () . readText () ws . send ( WsMessage ( text )) } }. asServer ( Netty ()). start () fun main () { `websocket terminal` () val ws = WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )) ws . onMessage { println ( it . bodyString ()) } val scan = Scanner ( System . `in` ) while ( true ) { ws . send ( WsMessage ( scan . nextLine ())) } } Obviously we haven't thought of everything here. We'd love to hear your ideas about other clever uses of the http4k building blocks, or to take PRs to integrate them into the library for wider use. You can get in touch through GitHub or the usual channels . Principles of Chaos","title":"10. Build a remote terminal!"},{"location":"blog/regarding_jcenter/","text":"Reassurance to http4k users regarding JCenter shutdown \u00b6 feb 2021 / the http4k team \u00b6 It was announced this week that the JCenter artifact repository would be shutting down in May 2021 . As JCenter was a superset of the Maven Central repository, this obviously comes as disappointing and worrying news regarding the future of Open Source software distribution for the JVM. Many builds will undoubtedly break as a result of this move. The http4k project currently primarily builds and distributes our 50+ artifacts to Bintray and then syncs them to Maven Central automatically. As a result of this announcement, we have totally removed any dependency on JCenter from our builds and have verified that all of our dependencies resolve correctly without it. Hence we can say with absolute confidence that: As of v4.3.0.0, http4k users will be unaffected by the JCenter shutdown. http4k has always worked on the principle of being as lightweight as possible with respect to dependencies, and this situation has rather vindicated our position. Lots of Open Source projects will not be in such a fortunate position. We will be investigating alternatives to JCenter to keep our build pipelines working as efficiently as possible, but the artifacts will always be available in the Maven Central and this is where we will continue to source our dependencies from. On a more personal note, we would remind you that now would be an excellent opportunity to show appreciation for the entirely voluntary efforts of the http4k team and sponsor the project so we can keep on supporting our users. // the http4k team \u00b6","title":"JCenter Shutdown"},{"location":"blog/regarding_jcenter/#reassurance_to_http4k_users_regarding_jcenter_shutdown","text":"","title":"Reassurance to http4k users regarding JCenter shutdown"},{"location":"blog/regarding_jcenter/#feb_2021_the_http4k_team","text":"It was announced this week that the JCenter artifact repository would be shutting down in May 2021 . As JCenter was a superset of the Maven Central repository, this obviously comes as disappointing and worrying news regarding the future of Open Source software distribution for the JVM. Many builds will undoubtedly break as a result of this move. The http4k project currently primarily builds and distributes our 50+ artifacts to Bintray and then syncs them to Maven Central automatically. As a result of this announcement, we have totally removed any dependency on JCenter from our builds and have verified that all of our dependencies resolve correctly without it. Hence we can say with absolute confidence that: As of v4.3.0.0, http4k users will be unaffected by the JCenter shutdown. http4k has always worked on the principle of being as lightweight as possible with respect to dependencies, and this situation has rather vindicated our position. Lots of Open Source projects will not be in such a fortunate position. We will be investigating alternatives to JCenter to keep our build pipelines working as efficiently as possible, but the artifacts will always be available in the Maven Central and this is where we will continue to source our dependencies from. On a more personal note, we would remind you that now would be an excellent opportunity to show appreciation for the entirely voluntary efforts of the http4k team and sponsor the project so we can keep on supporting our users.","title":"feb 2021 / the http4k team"},{"location":"blog/regarding_jcenter/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/retrospective_v3/","text":"A retrospective on http4k v3 \u00b6 september 2020 / the http4k team \u00b6 It's been quite a long time since we released version 3 of http4k all the way back in November 2017. Wow - that's over 1000 days in fact! Still, that doesn't mean that we've been sitting on our hands over in http4k Towers - far from it, we've been busier than ever making sure that we'll remember 2020 for more than just hibernating away in a bunker. In fact, the current interesting situation did give us an idea for a pretty original piece of swag... The eagle-eyed amongst you may have noticed that the project branding has undergone a bit of cosmetic surgery - we thought we'd treat ourselves to a professional job as opposed to the one we knocked up on the cheap way back at the start of the project. We're planning to do an entire refit of the content over the next few months, hopefully to make everything a little easier to find and to provide a few more pointers about where to start with the library. Stay tuned... Anyway, I digress. We thought that we'd do a bit of a retrospective on our version 3 journey. But first off, here are some numbers about what has gone on in those 1000 days in the http4k codebase: over 2500 commits! 268 releases 257 PRs merged 139 issues fixed 71 new amazing contributors 28 new http4k library modules 5 newly supported HTTP client libraries 5 new server engines 4 new messaging format modules 4 new testing integrations 2 new serverless backends 2 new templating libraries supported 0 new dependencies added to the core module \ud83d\ude09 If you'd like to check out the old version in the GitHub time machine, here is how the code looked all that time ago. Community involvement \u00b6 One of the most important things to us when we were developing was to create a library that we loved using, and we're thrilled that our users are so positive about http4k as well. The community around the library is just the type that we envisaged - a friendly, helpful and collaborative space, and some of the very best code (be it new modules or and fixes) have come directly from there. Our favourite piece of feedback was this post to our Slack channel: \"I find the entire http4k project quite exemplary in both function and style/form. At first glance (long time ago) my impression was that it must be deficient because the code-base was too small and the style - simple and elegant -- to the point I was skeptical it would actually work in 'the real world'. I have verified to my satisfaction that I was totally wrong. The apparent simplicity is actually elegance -- a direct representative of the overall design architecture. So thank you -- not just for a great library but also an inspiration and example of excellent engineering we could all strive to follow as a model of something done well.\" - David A. Lee This post made us very happy and all of our efforts feel completely worthwhile. We hope you all feel the same. \ud83d\ude0a As for community reach, we've been in touch with http4k users from all corners of the planet from all kinds of different projects and industries - from COVID tracking apps (relevant!) to Academic Publishers (that one was us) to Investment and Challenger Banks (also us), to No-code platforms. If Twitter is to be believed, the library seems pretty popular in Japan, but no-one on the core team speaks Japanese so we don't actually know why! Globe-trotting \u00b6 In 2018, we were lucky enough to be invited to KotlinConf in Amsterdam to talk about the development of http4k, and this led to us presenting at a total of 10 international conferences, meetups and privately hosted company events spanning across 5 timezones to talk about the library and it's development story. As (apparent) ambassadors for how successful Kotlin can be in the serverside arena, so watch this space in the latter part of 2020 and beyond... On the radar \u00b6 Another high point for us was having http4k featured in the Thoughtworks Technology Radar , which is a quarterly publication highlighting tools, techniques and languages that the well-known consultancy have been using to successfully deliver projects across the globe. ThoughtWorks called out the test-driven nature of http4k, citing: \"Apart from its elegance and simplicity, we also like its emphasis on testability \u2014 given that the entities in the libraries are immutable and the routes in the app, as well as the app itself, are just functions, they're super easy to test.\" - TW TechRadar As Kotlin cements its reputation as a solid choice for serverside (and eventually everywhere!) development, and continues to receive backing from successful projects across the proprietary and open source ecosystems, we hope to be able to do our bit to nudge it ever further upwards in the \"most loved language\" tables. Performing to a crowd \u00b6 One of the most frequent questions that we get asked about http4k is \"how does it perform?\". We attempted to answer that question by entering our baby into the TechEmpower benchmarks, which is a suite of tests which pits each library against the rest of the pack in a set of real-world-esque scenarios to see how it performs including JSON serialisation, database access and HTML templating generation. Overall, we were thrilled (and continue to be) with the results of the benchmarks. Since the first round (16), http4k has been the best performing pure Kotlin web library across the contenders. The most important factor to us that there were no special tricks involved in the implementations - ie. the endpoints under test were written exactly as they would be on a \"real\" project and no custom tuning other than standard JVM options applied. In terms of the performance of the server backends, Apache HttpComponents (version 4) has been consistently the strongest performer in the previous benchmarks, although there have been performance improvements to the Netty backend implementations that we are hopeful in the upcoming round 20 might make it a real contender for the http4k \ud83d\udc51. Enter the platform! \u00b6 When we conceived http4k, it was a simple 40 line shim over the top of another Java-based framework, bashed together on a HackDay. Never did we realise that a few years later it would be a very popular library with over 40 modules! For this reason (and to save on typing), we introduced the http4k-bom module (learn more about BOM here , allowing users to use a single version of all http4k libraries and then just import the modules they need. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:3.259.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) } Cloudy-wowdy stuff \u00b6 Just as in every codebase there is a package called \"utils\", this also happens with libraries - useful code that doesn't quite fit anywhere else, yet you just always end up needing. For http4k, these utils were about the ancillary stuff that goes around an application to make it support 12-factor ideals such as configuration and relative primitives. We didn't want to put this stuff into the http4k-core module as we felt it wasn't absolutely necessary (and we wanted to continue to keep the binary size of the core module down). Thus, http4k-cloudnative was born! The major feature of this module is loading typesafe Environmental configuration of applications, using - what else... Yep - the reusing of the existing http4k lens system to cover configuring your apps, meaning that it's now impossible to make easy mistakes such as setting a 10-second timeout as 10-milliseconds! You can check out exactly how to use the feature in the docs . Testing modules \u00b6 It's no secret that the http4k team love testing - it's part of our core DNA and the simplicity of core design would be worthless unless we could test apps built with the library simply and effectively. To that end, some of the most exciting additions that we've added to the library have been in the arena of testing: Approval Testing is a technique for simplifying complex assertions that might otherwise be more effectively checked by eye. http4k-testing-approval provides the primitives and tooling for supporting this style of testing, Chaos Testing was made famous by Netflix for proving out how systems react when everything heads south. http4k-testing-chaos adds transparent, programmable failure-generation to any http4k app using only a simple Filter . Service Virtualization enables API test contracts to be encoded and then shipped, simplifying the process of proving that apps retain compatibility, http4k-testing-servirtium provides the basis for recording and replaying contracts from disk or from other sources such as GitHub. OpenAPI FTW \u00b6 One of the most popular and standout http4k features is the support for the OpenApi specification. Originally supporting Swagger 2 spec via the http4k-contract module, we rewrote the implementation to add support for much more complete (and consistent!) version 3 of specification in May 2019. The module will now generate fully compliant OpenAPI3 documentation, including full JSON Schema breakdowns for class models and taking advantage of Kotlin class features such as enums and nullability. Powered by the http4k lens API, this runtime system allows developers to avoid concerning themselves with tediously documenting API models which can easily go stale. Serverless turnabout \u00b6 The major http4k feature in version 3.0.0 was the addition of support for Serverless backends - namely the granddaddy of Serverless - AWS Lambda. And you know what they say about the first implementation of something? They say that it's probably wrong. Well, turns out they were right (again). When we got to introducing the second and third implementations of Serverless (Google Cloud Functions and OpenWhisk), we realised that the approach taken for AWS wasn't very dev friendly... it relied on reflection to solve the problem of loading the Lambda function class. This actually broke one of our own cardinal rules that we set for the http4k project: \"Absolutely no magic involved: No reflection. No annotations.\" - 5th Commandment of http4k So - we did what any good dev team would do and replaced the magic function loading mechanism with a more developer friendly API working by class extension. Fear not readers - the guilty parties have been appropriately punished, and it (probably) won't happen again. \ud83d\ude09 One other piece of interesting research which came out and somewhat vindicated the dependency-lite approach of http4k was Cold Start War , which performed a lot of experiments and concluded that: \"As expected, the dependencies slow the loading down. You should keep your Functions lean, otherwise, you will pay in seconds for every cold start.\" - Mikhail Shilkov For production deployments, we continue to recommend the use of a tool such as Proguard or R8 to massively reduce the size of packaged Serverless Function JAR file. The http4k serverless modules also ship with zero or minimal dependencies to avoid any transitive bloat that might occur. Going native \u00b6 Apart from Serverless, one of the most exciting things happening in JVM-land right now is the advent of native technologies such as Quarkus and GraalVM, giving the possibility of compiling apps direct to binaries - which give a massive performance boost. It's a young technology and often involves various amounts of trickery to get around limitations of the native system regarding areas such as reflection. Luckily for us (and you!), http4k operates on an anti-magic principle (see the 5th commandment above) and it was a nice surprise when it occurred that, with the correct server engine (Apache for the curious), http4k applications can be packaged out-of-the-box to this format with absolutely no modifications of aforementioned trickery. We look forward to further supporting these technologies as they develop, and will endeavour to provide custom modules to suppport teams who want to take advantage of them. Future echoes... \u00b6 \u201cAnd the open road rolled out in front of us.\u201d - Alexandra Bracken, In The Afterlight So what's next? Well, we've got a load of good stuff coming up for post version 4 of http4k, the rest of 2020 and beyond. As well as the website improvements, we've got a bunch of tools in the works to make the library more quick-start friendly, including the ability to autogenerate advanced http4k Clients and Server stubs from OpenApi specifications - both from the command-line and from the browser. We're also looking at improvements in the versioning scheme to better communicate breaking changes, and rolling out modules to enable better Serverless platform support. Whatever happens though, the focus of http4k will always be on providing a best-in-class Developer and Testing experience. We'd love to hear how we're doing, so please drop into the comm channels to get in touch. Here's to the future. Stay safe out there and we'll see you in it. // the http4k team \u00b6","title":"Retrospective on v3"},{"location":"blog/retrospective_v3/#a_retrospective_on_http4k_v3","text":"","title":"A retrospective on http4k v3"},{"location":"blog/retrospective_v3/#september_2020_the_http4k_team","text":"It's been quite a long time since we released version 3 of http4k all the way back in November 2017. Wow - that's over 1000 days in fact! Still, that doesn't mean that we've been sitting on our hands over in http4k Towers - far from it, we've been busier than ever making sure that we'll remember 2020 for more than just hibernating away in a bunker. In fact, the current interesting situation did give us an idea for a pretty original piece of swag... The eagle-eyed amongst you may have noticed that the project branding has undergone a bit of cosmetic surgery - we thought we'd treat ourselves to a professional job as opposed to the one we knocked up on the cheap way back at the start of the project. We're planning to do an entire refit of the content over the next few months, hopefully to make everything a little easier to find and to provide a few more pointers about where to start with the library. Stay tuned... Anyway, I digress. We thought that we'd do a bit of a retrospective on our version 3 journey. But first off, here are some numbers about what has gone on in those 1000 days in the http4k codebase: over 2500 commits! 268 releases 257 PRs merged 139 issues fixed 71 new amazing contributors 28 new http4k library modules 5 newly supported HTTP client libraries 5 new server engines 4 new messaging format modules 4 new testing integrations 2 new serverless backends 2 new templating libraries supported 0 new dependencies added to the core module \ud83d\ude09 If you'd like to check out the old version in the GitHub time machine, here is how the code looked all that time ago.","title":"september 2020 / the http4k team"},{"location":"blog/retrospective_v3/#community_involvement","text":"One of the most important things to us when we were developing was to create a library that we loved using, and we're thrilled that our users are so positive about http4k as well. The community around the library is just the type that we envisaged - a friendly, helpful and collaborative space, and some of the very best code (be it new modules or and fixes) have come directly from there. Our favourite piece of feedback was this post to our Slack channel: \"I find the entire http4k project quite exemplary in both function and style/form. At first glance (long time ago) my impression was that it must be deficient because the code-base was too small and the style - simple and elegant -- to the point I was skeptical it would actually work in 'the real world'. I have verified to my satisfaction that I was totally wrong. The apparent simplicity is actually elegance -- a direct representative of the overall design architecture. So thank you -- not just for a great library but also an inspiration and example of excellent engineering we could all strive to follow as a model of something done well.\" - David A. Lee This post made us very happy and all of our efforts feel completely worthwhile. We hope you all feel the same. \ud83d\ude0a As for community reach, we've been in touch with http4k users from all corners of the planet from all kinds of different projects and industries - from COVID tracking apps (relevant!) to Academic Publishers (that one was us) to Investment and Challenger Banks (also us), to No-code platforms. If Twitter is to be believed, the library seems pretty popular in Japan, but no-one on the core team speaks Japanese so we don't actually know why!","title":"Community involvement"},{"location":"blog/retrospective_v3/#globe-trotting","text":"In 2018, we were lucky enough to be invited to KotlinConf in Amsterdam to talk about the development of http4k, and this led to us presenting at a total of 10 international conferences, meetups and privately hosted company events spanning across 5 timezones to talk about the library and it's development story. As (apparent) ambassadors for how successful Kotlin can be in the serverside arena, so watch this space in the latter part of 2020 and beyond...","title":"Globe-trotting"},{"location":"blog/retrospective_v3/#on_the_radar","text":"Another high point for us was having http4k featured in the Thoughtworks Technology Radar , which is a quarterly publication highlighting tools, techniques and languages that the well-known consultancy have been using to successfully deliver projects across the globe. ThoughtWorks called out the test-driven nature of http4k, citing: \"Apart from its elegance and simplicity, we also like its emphasis on testability \u2014 given that the entities in the libraries are immutable and the routes in the app, as well as the app itself, are just functions, they're super easy to test.\" - TW TechRadar As Kotlin cements its reputation as a solid choice for serverside (and eventually everywhere!) development, and continues to receive backing from successful projects across the proprietary and open source ecosystems, we hope to be able to do our bit to nudge it ever further upwards in the \"most loved language\" tables.","title":"On the radar"},{"location":"blog/retrospective_v3/#performing_to_a_crowd","text":"One of the most frequent questions that we get asked about http4k is \"how does it perform?\". We attempted to answer that question by entering our baby into the TechEmpower benchmarks, which is a suite of tests which pits each library against the rest of the pack in a set of real-world-esque scenarios to see how it performs including JSON serialisation, database access and HTML templating generation. Overall, we were thrilled (and continue to be) with the results of the benchmarks. Since the first round (16), http4k has been the best performing pure Kotlin web library across the contenders. The most important factor to us that there were no special tricks involved in the implementations - ie. the endpoints under test were written exactly as they would be on a \"real\" project and no custom tuning other than standard JVM options applied. In terms of the performance of the server backends, Apache HttpComponents (version 4) has been consistently the strongest performer in the previous benchmarks, although there have been performance improvements to the Netty backend implementations that we are hopeful in the upcoming round 20 might make it a real contender for the http4k \ud83d\udc51.","title":"Performing to a crowd"},{"location":"blog/retrospective_v3/#enter_the_platform","text":"When we conceived http4k, it was a simple 40 line shim over the top of another Java-based framework, bashed together on a HackDay. Never did we realise that a few years later it would be a very popular library with over 40 modules! For this reason (and to save on typing), we introduced the http4k-bom module (learn more about BOM here , allowing users to use a single version of all http4k libraries and then just import the modules they need. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:3.259.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) }","title":"Enter the platform!"},{"location":"blog/retrospective_v3/#cloudy-wowdy_stuff","text":"Just as in every codebase there is a package called \"utils\", this also happens with libraries - useful code that doesn't quite fit anywhere else, yet you just always end up needing. For http4k, these utils were about the ancillary stuff that goes around an application to make it support 12-factor ideals such as configuration and relative primitives. We didn't want to put this stuff into the http4k-core module as we felt it wasn't absolutely necessary (and we wanted to continue to keep the binary size of the core module down). Thus, http4k-cloudnative was born! The major feature of this module is loading typesafe Environmental configuration of applications, using - what else... Yep - the reusing of the existing http4k lens system to cover configuring your apps, meaning that it's now impossible to make easy mistakes such as setting a 10-second timeout as 10-milliseconds! You can check out exactly how to use the feature in the docs .","title":"Cloudy-wowdy stuff"},{"location":"blog/retrospective_v3/#testing_modules","text":"It's no secret that the http4k team love testing - it's part of our core DNA and the simplicity of core design would be worthless unless we could test apps built with the library simply and effectively. To that end, some of the most exciting additions that we've added to the library have been in the arena of testing: Approval Testing is a technique for simplifying complex assertions that might otherwise be more effectively checked by eye. http4k-testing-approval provides the primitives and tooling for supporting this style of testing, Chaos Testing was made famous by Netflix for proving out how systems react when everything heads south. http4k-testing-chaos adds transparent, programmable failure-generation to any http4k app using only a simple Filter . Service Virtualization enables API test contracts to be encoded and then shipped, simplifying the process of proving that apps retain compatibility, http4k-testing-servirtium provides the basis for recording and replaying contracts from disk or from other sources such as GitHub.","title":"Testing modules"},{"location":"blog/retrospective_v3/#openapi_ftw","text":"One of the most popular and standout http4k features is the support for the OpenApi specification. Originally supporting Swagger 2 spec via the http4k-contract module, we rewrote the implementation to add support for much more complete (and consistent!) version 3 of specification in May 2019. The module will now generate fully compliant OpenAPI3 documentation, including full JSON Schema breakdowns for class models and taking advantage of Kotlin class features such as enums and nullability. Powered by the http4k lens API, this runtime system allows developers to avoid concerning themselves with tediously documenting API models which can easily go stale.","title":"OpenAPI FTW"},{"location":"blog/retrospective_v3/#serverless_turnabout","text":"The major http4k feature in version 3.0.0 was the addition of support for Serverless backends - namely the granddaddy of Serverless - AWS Lambda. And you know what they say about the first implementation of something? They say that it's probably wrong. Well, turns out they were right (again). When we got to introducing the second and third implementations of Serverless (Google Cloud Functions and OpenWhisk), we realised that the approach taken for AWS wasn't very dev friendly... it relied on reflection to solve the problem of loading the Lambda function class. This actually broke one of our own cardinal rules that we set for the http4k project: \"Absolutely no magic involved: No reflection. No annotations.\" - 5th Commandment of http4k So - we did what any good dev team would do and replaced the magic function loading mechanism with a more developer friendly API working by class extension. Fear not readers - the guilty parties have been appropriately punished, and it (probably) won't happen again. \ud83d\ude09 One other piece of interesting research which came out and somewhat vindicated the dependency-lite approach of http4k was Cold Start War , which performed a lot of experiments and concluded that: \"As expected, the dependencies slow the loading down. You should keep your Functions lean, otherwise, you will pay in seconds for every cold start.\" - Mikhail Shilkov For production deployments, we continue to recommend the use of a tool such as Proguard or R8 to massively reduce the size of packaged Serverless Function JAR file. The http4k serverless modules also ship with zero or minimal dependencies to avoid any transitive bloat that might occur.","title":"Serverless turnabout"},{"location":"blog/retrospective_v3/#going_native","text":"Apart from Serverless, one of the most exciting things happening in JVM-land right now is the advent of native technologies such as Quarkus and GraalVM, giving the possibility of compiling apps direct to binaries - which give a massive performance boost. It's a young technology and often involves various amounts of trickery to get around limitations of the native system regarding areas such as reflection. Luckily for us (and you!), http4k operates on an anti-magic principle (see the 5th commandment above) and it was a nice surprise when it occurred that, with the correct server engine (Apache for the curious), http4k applications can be packaged out-of-the-box to this format with absolutely no modifications of aforementioned trickery. We look forward to further supporting these technologies as they develop, and will endeavour to provide custom modules to suppport teams who want to take advantage of them.","title":"Going native"},{"location":"blog/retrospective_v3/#future_echoes","text":"\u201cAnd the open road rolled out in front of us.\u201d - Alexandra Bracken, In The Afterlight So what's next? Well, we've got a load of good stuff coming up for post version 4 of http4k, the rest of 2020 and beyond. As well as the website improvements, we've got a bunch of tools in the works to make the library more quick-start friendly, including the ability to autogenerate advanced http4k Clients and Server stubs from OpenApi specifications - both from the command-line and from the browser. We're also looking at improvements in the versioning scheme to better communicate breaking changes, and rolling out modules to enable better Serverless platform support. Whatever happens though, the focus of http4k will always be on providing a best-in-class Developer and Testing experience. We'd love to hear how we're doing, so please drop into the comm channels to get in touch. Here's to the future. Stay safe out there and we'll see you in it.","title":"Future echoes..."},{"location":"blog/retrospective_v3/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/toolbox/","text":"http4k Toolbox: Guns for show, knives for a pro \u00b6 november 2020 / @daviddenton \u00b6 Over the years of creating apps with http4k, we've collated several tools that we use to turbo charge our development activities. While working on v4 of the library, and with a little time on our hands, we decided to bring all of these tools together into a single place so that all of our users could get their benefits. These tools are mostly based around code generation of some sort or another and are mainly used to shortcut repetitive or mundane development tasks that you might encounter when developing and testing applications with http4k. For development, we made extensive use of the excellent http4k web library (have you heard of it? it's really rather good!), as well as KotlinPoet and Bunting4k , a new CLI flags library that we created for this task. A lot of the credit for the creation of the Toolbox has to go to the amazing Albert Latacz , who not only created large parts of the functionality, but also investigated build and deployment options. Web toolbox \u00b6 The Toolbox is now the main starting point for working with http4k The main concept behind each of the functions is to allow the user to preview any generated code before they download it. We have also set up automated pipelines to update the Toolbox whenever we do a release of http4k so it should always be up to date. It's worth noting that the Toolbox is also entirely stateless, so no user content is recorded in any form. The http4k Toolbox is available @ toolbox.http4k.org CLI \u00b6 The http4k team are massive fans of automation, so we also wanted to enable teams to use the functions of the Toolbox in a shell. Hence, we decided to also make a CLI binary for the Toolbox, which is available to install via both Brew and SDKMan . You can find instructions about how to install it on the Toolbox homepage . Toolbox functionality guide \u00b6 Where we introduce each of the functions of the Toolbox , with a rationale and a bit of background on each. Generating a project \u00b6 This is quite a common one for various libraries, but they tend to be quite basic - and we thought we could do better. The http4k Project Generator is an intelligent wizard which allows the user to select their backend, extra modules, and build system. It then crunches all the code from these choices together into a coherent project configuration (called a \"Stack\"), and provides a reusable link for regenerating the same project configuration again. The questionnaire is dynamically generated by providing options for all 17+ supported Server and Serverless backends, and the further 30+ http4k integration modules, including examples of the different types of tests that you can write for apps. We can also add other \"features\" to be mixed in to the project structure. As well as downloading the project, you can also browse the generated code in a browser. Try out the project generator here . OpenAPI3 Server, Client, & Models \u00b6 http4k already supports generating OpenAPI3 specification documents from your code, but one thing that you often come across is when you need to generate an HTTP server or Client for someone else's application which is documented with using the same format. Initially we did look at the existing OpenAPI generator projects available for other libraries, but they are mostly based around static templates, and once again we thought we could do something a little more sophisticated. The OpenAPI generator . accepts a JSON or YAML specification (or URL), and generates: Data Classes for JSON API model schemas - The message objects are generated using a \"best guess\" algorithm from the JSON Schema information in the specification document, mapping the base JSON types to their Kotlin equivalents. Template http4k Server implementation containing a set of generated API endpoint functions and Lens implementations for all defined HTTP contract parts (path, query etc), as well as auto-marshalled JSON request and response bodies. Security implementations (API Key, Basic, Bearer) are also generated. http4k API client implementation with functions generated for each API endpoint. Lens implementations are generated and HTTP contract parts (path, query etc) are marshalled both into and out of the Request and Response messages. As well as downloading the files as a ZIP archive, you can also browse the generated code in a browser. The OpenAPI generator is available here . Generating Data Classes from JSON, XML and YAML \u00b6 Most of the time when working with API models in http4k, you have example messages in the native format which need to be auto-marshalled into Kotlin using Jackson or one of the other supported format libraries. This results in a very boring exercise in conversion, so we wrote this converter that will generate Data Classes to support reading/writing of JSON, XML and YAML messages. For example, this JSON... { \"jsonRoot\" : { \"child\" : [ \"hello\" , \"there\" ], \"num\" : 123 } } ... results in these Data Classes: data class JsonRoot ( val child : List < String >? , val num : Number?) data class Base ( val jsonRoot : JsonRoot?) Convert your JSON/XML/Yaml here . HTTP -> HTTP message builder \u00b6 Occasionally you have the raw version of an HTTP message and want to create an identical http4k HTTP message object from it. This tool converts a raw message to the syntax. For example: POST /example/index.html?query1=abc&query2=def HTTP/1.1 Host: toolbox.http4k.org Accept: image/gif, image/jpeg, */* Content-Type: text/plain hello from http4k ... results in this HTTP message builder code: fun request (): Request = Request ( Method . POST , \"/example/index.html\" ) . query ( \"query1\" , \"abc\" ) . query ( \"query2\" , \"def\" ) . header ( \"Host\" , \"toolbox.http4k.org\" ) . header ( \"Accept\" , \"image/gif, image/jpeg, */*\" ) . header ( \"Content-Type\" , \"text/plain\" ) . body ( \"\"\"hello from http4k\"\"\" ) Build HTTP messages from raw HTTP here . IntelliJ Live Templates \u00b6 One of our most favourite power features of IntelliJ is Live Templates, which allow you to add code generation macros to your IDE and activate them with a shortcut + tab. For http4k development, we got bored with typing things longhand, so decided to create a bunch of templates and share them with our favourite people (our users!). Even better, anyone can use our shortcuts in their IDE by adding a read-only \"Settings Repository\". Install the http4k live templates (and save your fingers!) by following the instructions at the bottom of this page . Wrapping up \u00b6 We hope you'll find the various tools that we've built into the Toolbox useful to either get started with http4k or to make your life even more productive. As ever, let us know how we're doing, and if there are any other tools that you think might be helpful then we'd love to hear about them!","title":"Toolbox: Guns for show, knives for a pro"},{"location":"blog/toolbox/#http4k_toolbox_guns_for_show_knives_for_a_pro","text":"","title":"http4k Toolbox: Guns for show, knives for a pro"},{"location":"blog/toolbox/#november_2020_daviddenton","text":"Over the years of creating apps with http4k, we've collated several tools that we use to turbo charge our development activities. While working on v4 of the library, and with a little time on our hands, we decided to bring all of these tools together into a single place so that all of our users could get their benefits. These tools are mostly based around code generation of some sort or another and are mainly used to shortcut repetitive or mundane development tasks that you might encounter when developing and testing applications with http4k. For development, we made extensive use of the excellent http4k web library (have you heard of it? it's really rather good!), as well as KotlinPoet and Bunting4k , a new CLI flags library that we created for this task. A lot of the credit for the creation of the Toolbox has to go to the amazing Albert Latacz , who not only created large parts of the functionality, but also investigated build and deployment options.","title":"november 2020 / @daviddenton"},{"location":"blog/toolbox/#web_toolbox","text":"The Toolbox is now the main starting point for working with http4k The main concept behind each of the functions is to allow the user to preview any generated code before they download it. We have also set up automated pipelines to update the Toolbox whenever we do a release of http4k so it should always be up to date. It's worth noting that the Toolbox is also entirely stateless, so no user content is recorded in any form. The http4k Toolbox is available @ toolbox.http4k.org","title":"Web toolbox"},{"location":"blog/toolbox/#cli","text":"The http4k team are massive fans of automation, so we also wanted to enable teams to use the functions of the Toolbox in a shell. Hence, we decided to also make a CLI binary for the Toolbox, which is available to install via both Brew and SDKMan . You can find instructions about how to install it on the Toolbox homepage .","title":"CLI"},{"location":"blog/toolbox/#toolbox_functionality_guide","text":"Where we introduce each of the functions of the Toolbox , with a rationale and a bit of background on each.","title":"Toolbox functionality guide"},{"location":"blog/toolbox/#generating_a_project","text":"This is quite a common one for various libraries, but they tend to be quite basic - and we thought we could do better. The http4k Project Generator is an intelligent wizard which allows the user to select their backend, extra modules, and build system. It then crunches all the code from these choices together into a coherent project configuration (called a \"Stack\"), and provides a reusable link for regenerating the same project configuration again. The questionnaire is dynamically generated by providing options for all 17+ supported Server and Serverless backends, and the further 30+ http4k integration modules, including examples of the different types of tests that you can write for apps. We can also add other \"features\" to be mixed in to the project structure. As well as downloading the project, you can also browse the generated code in a browser. Try out the project generator here .","title":"Generating a project"},{"location":"blog/toolbox/#openapi3_server_client_models","text":"http4k already supports generating OpenAPI3 specification documents from your code, but one thing that you often come across is when you need to generate an HTTP server or Client for someone else's application which is documented with using the same format. Initially we did look at the existing OpenAPI generator projects available for other libraries, but they are mostly based around static templates, and once again we thought we could do something a little more sophisticated. The OpenAPI generator . accepts a JSON or YAML specification (or URL), and generates: Data Classes for JSON API model schemas - The message objects are generated using a \"best guess\" algorithm from the JSON Schema information in the specification document, mapping the base JSON types to their Kotlin equivalents. Template http4k Server implementation containing a set of generated API endpoint functions and Lens implementations for all defined HTTP contract parts (path, query etc), as well as auto-marshalled JSON request and response bodies. Security implementations (API Key, Basic, Bearer) are also generated. http4k API client implementation with functions generated for each API endpoint. Lens implementations are generated and HTTP contract parts (path, query etc) are marshalled both into and out of the Request and Response messages. As well as downloading the files as a ZIP archive, you can also browse the generated code in a browser. The OpenAPI generator is available here .","title":"OpenAPI3 Server, Client, & Models"},{"location":"blog/toolbox/#generating_data_classes_from_json_xml_and_yaml","text":"Most of the time when working with API models in http4k, you have example messages in the native format which need to be auto-marshalled into Kotlin using Jackson or one of the other supported format libraries. This results in a very boring exercise in conversion, so we wrote this converter that will generate Data Classes to support reading/writing of JSON, XML and YAML messages. For example, this JSON... { \"jsonRoot\" : { \"child\" : [ \"hello\" , \"there\" ], \"num\" : 123 } } ... results in these Data Classes: data class JsonRoot ( val child : List < String >? , val num : Number?) data class Base ( val jsonRoot : JsonRoot?) Convert your JSON/XML/Yaml here .","title":"Generating Data Classes from JSON, XML and YAML"},{"location":"blog/toolbox/#http_-_http_message_builder","text":"Occasionally you have the raw version of an HTTP message and want to create an identical http4k HTTP message object from it. This tool converts a raw message to the syntax. For example: POST /example/index.html?query1=abc&query2=def HTTP/1.1 Host: toolbox.http4k.org Accept: image/gif, image/jpeg, */* Content-Type: text/plain hello from http4k ... results in this HTTP message builder code: fun request (): Request = Request ( Method . POST , \"/example/index.html\" ) . query ( \"query1\" , \"abc\" ) . query ( \"query2\" , \"def\" ) . header ( \"Host\" , \"toolbox.http4k.org\" ) . header ( \"Accept\" , \"image/gif, image/jpeg, */*\" ) . header ( \"Content-Type\" , \"text/plain\" ) . body ( \"\"\"hello from http4k\"\"\" ) Build HTTP messages from raw HTTP here .","title":"HTTP -> HTTP message builder"},{"location":"blog/toolbox/#intellij_live_templates","text":"One of our most favourite power features of IntelliJ is Live Templates, which allow you to add code generation macros to your IDE and activate them with a shortcut + tab. For http4k development, we got bored with typing things longhand, so decided to create a bunch of templates and share them with our favourite people (our users!). Even better, anyone can use our shortcuts in their IDE by adding a read-only \"Settings Repository\". Install the http4k live templates (and save your fingers!) by following the instructions at the bottom of this page .","title":"IntelliJ Live Templates"},{"location":"blog/toolbox/#wrapping_up","text":"We hope you'll find the various tools that we've built into the Toolbox useful to either get started with http4k or to make your life even more productive. As ever, let us know how we're doing, and if there are any other tools that you think might be helpful then we'd love to hear about them!","title":"Wrapping up"},{"location":"blog/typesafe_configuration/","text":"Add typesafe 12-factor configuration to http4k apps with Environments \u00b6 november 2018 / @daviddenton \u00b6 Intro \u00b6 This post covers the various concerns around configuring HTTP apps, and introduces the http4k approach for addressing these when deploying applications into cloud-native environments, which leverages the Kotlin type system for maximum safely and code reuse. Concerns when configuring applications \u00b6 One of the tenets of operating applications according to the principles of 12factor , and especially in containerised cloud-native apps, is to inject all app configuration through the use of environmental variables. Additionally, when using more restrictive settings (such as utilising JVM security manager policies or through the use of container images which don't provide an OS baseline) it may not be possible to read files (such as YAML, JSON etc) from disk, which reinforces this approach. There are several particular relevant concerns that we need to address, but overall the effect that we are looking for is that any misconfiguration of the application will result in it failing to startup. For this reason we want to reify all values to check them as soon as possible in the application bootstrap phase. 1. Optionality \u00b6 Kotlin's type system guards us against missing values being injected - for instance the following code will throw a IllegalStateException due to a typo in the parameter name: package blog.typesafe_configuration.pre // export ANTIDISESTABLISHMENTARIANISM=opposition to the disestablishment of the Church of England val definition : String = System . getenv ( \"ANTIDISESTABLISHMENTARIAMISM\" ) However not all configuration values will be required. We can define that there are 3 distinct modes of optionality available for each parameter: Required: These values must be injected for each environment, with no default value defined. Most configurations such as hostnames should always use this form to maximise operational safety. Optional: These values can be supplied, but there is no default value. This category fits well with dynamic properties which could be data-driven (ie. not known at compile-time). Defaulted: These values can be supplied, but a fallback value (or chain of other config values) will be used if they are not. Missing values should produce a reasonable error and stop the app from starting. 2. Type coercion \u00b6 Most applications will require a variety of configuration primitive types, which may or may not map to the Java/Kotlin standard types, including: strings such as service URLs, log levels, or AWS role names numeric values such as ports or retry counts booleans such as debug switch or feature flags duration values for timeouts, backoff times But handling these raw types alone is not enough to guarantee safety - it is best to marshall the values into a suitable operational/domain type that can validate the input and avoid confusion. Kotlin gives us a simple way to do this using require as a guard: package blog.typesafe_configuration.pre class Port ( val value : Int ) { init { require (( 1. . 65535 ). contains ( value )) { \"Out of range Port: ' $ value '\" } } } // export PORT=8000 val port = Port ( System . getenv ( \"PORT\" ). toInt ()) Additionally to the above, it is important to represent those values in a form that cannot be misinterpreted. A good example of this is the passing of temporal values as integers - timeouts defined this way could be easily be parsed into the wrong time unit (seconds instead of milliseconds). Using a higher level primitive such as Duration will help us here: package blog.typesafe_configuration.pre import java.time.Duration data class Timeout ( val value : Duration ) { init { require ( ! value . isNegative ) { \"Cannot have negative timeout\" } } } // export TIMEOUT=PT30S val timeout = Timeout ( Duration . parse ( System . getenv ( \"TIMEOUT\" ))) Obviously, the above is still not very safe - and what's more, a coercion could now fail with one of 3 different exceptions depending on if the value was missing ( IllegalStateException ), unparsable ( DateTimeParseException ) or invalid ( IllegalArgumentException ). The conversion code from String -> Duration must also be repeated (or extracted) for each value that we wish to parse. 3. Multiplicity \u00b6 Configuration parameters may have one or many values and need to be converted safely from the injected string representation (usually comma-separated) and into their internally represented types at application startup: package blog.typesafe_configuration.pre class Host ( val value : String ) // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val hosts = System . getenv ( \"HOSTNAMES\" ). split ( \",\" ). map { Host ( it . trim ()) } Once again, the splitting code will need to be repeated for each config value, or extracted to a library function. 4. Security \u00b6 The configuration of a standard app will generally contain both sensitive and non-sensitive values. Sensitive such as application secrets, DB passwords or API keys should be handled in a way that avoid storing directly in memory in a readable format or long lived fashion, where they may be inadvertently inspected or outputted into a log file. Dangling code situations such as in the code below are common, and are asking for trouble... package blog.typesafe_configuration.pre import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import java.nio.ByteBuffer val s3 = OkHttp () fun readFile ( secretKey : String , bucketKey : String ): ByteBuffer { return s3 ( Request ( GET , \"https://mybucket.s3.amazonaws.com/ $ bucketKey \" )). body . payload } // export AWS_SECRET_KEY=someSuperSecretValueThatOpsReallyDoNotWantYouToKnow val file = readFile ( System . getenv ( \"AWS_SECRET_KEY\" ), \"myfile.txt\" ) 5. Configuration Context & Overriding \u00b6 We also want to avoid defining all values for all possible scenarios - for example in test cases, so the ability to overlay configuration sets on top of each other is useful. Although it is against the rules of 12-factor, it is sometimes convenient to source parameter values from a variety of contexts when running applications in non-cloud environments: System Environment variables Properties files JAR resources Local files Source code defined environmental configuration Implementing this kind of fallback logic manually, you'd end up with code like the below: package blog.typesafe_configuration.pre val name = System . getProperty ( \"USERNAME\" ) ?: System . getenv ( \"USERNAME\" ) ?: \"DEFAULT_USER\" The http4k approach... \u00b6 There are already many options for configurational libraries written in Kotlin, but http4k also provides an option in the http4k-cloudnative add-on module which leverages the power of the Lens system already built into the core library to provide a consistent experience to API users. In case you're new to Lenses, here's a recap... Lenses - a recap \u00b6 In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. A Lens is an stateless object responsible for either the one-way (or Bidirectional) transformation of It defines type parameters representing input IN and output OUT types and implements one (for a Lens ) or both (for a BiDiLens ) of the following interfaces: LensExtractor - takes a value of type IN and extracts a value of type OUT LensInjector - takes a value of type IN and a value of type OUT and returns a modified value of type IN with the value injected into it. package blog.typesafe_configuration.post interface LensExtractor < in IN , out OUT > : ( IN ) -> OUT { override operator fun invoke ( target : IN ): OUT } interface LensInjector < in IN , in OUT > { operator fun < R : OUT > invoke ( value : IN , target : R ): R } interface Lens < IN , OUT > : LensExtractor < IN , OUT > interface BiDiLens < IN , OUT > : Lens < IN , OUT > , LensInjector < IN , OUT > The creation of a Lens consists of 4 main concerns: targeting determines where the Lens expects to extract and inject the values from/to, which can consist of both an overall target and a name within that target. multiplicity handling which defines how many of a particular value we are attempting to handle. the transformation chain of function composition which forms a specification for converting one type to another. This is done in code using the map() method defined on the Lens. the optionality of a Lens denotes the behaviour if/when a value cannot be found in the target. To define a Lens instance through the http4k Lens API, we take an initial target specification, decide it's multiplicity , provide any transformations with map() , and finally reify the specification into a Lens instance by deciding it's optionality. It sounds involved, but it is consistent and the fluent API has been designed to make it simpler. By way of an example, here we define a bi-directional Lens for custom type Page , extracted from a querystring value and defaulting to Page 1. package blog.typesafe_configuration.post import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.lens.BiDiLens import org.http4k.lens.Query import org.http4k.lens.int data class Page ( val value : Int ) { init { require ( value > 1 ) { \"Page number must be positive\" } } } val lens : BiDiLens < Request , Page > = Query . int (). map ( :: Page , Page :: value ). defaulted ( \"pageNumber\" , Page ( 1 )) val pageNumber : Page = lens ( Request ( GET , \"http://abc/search?pageNumber=55\" )) val updatedRequest : Request = lens ( pageNumber , Request ( GET , \"http://abc/search\" )) In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. http4k Environments \u00b6 in http4k, an Environment object is a context which holds configuration values. It effectively behaves like a Map , in that it can be composed with other Environment objects to provide a consolidated view of all of it's component values. package blog.typesafe_configuration.post import org.http4k.config.Environment import java.io.File val systemEnv : Environment = Environment . ENV val jvmFlags : Environment = Environment . JVM_PROPERTIES val jar : Environment = Environment . fromResource ( \"jar.properties\" ) val filesystem : Environment = Environment . from ( File ( \"fs.properties\" )) val codeBased : Environment = Environment . from ( \"key1\" to \"value1\" , \"key2\" to \"value2\" ) val consolidated : Environment = jvmFlags overrides systemEnv overrides codeBased overrides filesystem overrides jar If you're using any of the other Kotlin-based configuration libraries, the above should look pretty familiar. The difference starts to become apparent when attempting to retrieve values from the Environment instance. This is done using EnviromentKey Lenses, which are an extension of the http4k Lens system that specifically targets Environment objects. package blog.typesafe_configuration.post import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.lens.Lens import org.http4k.lens.duration import org.http4k.lens.int import java.time.Duration data class Timeout ( val value : Duration ) data class Port ( val value : Int ) data class Host ( val value : String ) // export TIMEOUT=PT30S // export PORT=8080 // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val env = Environment . ENV val portLens : Lens < Environment , Port > = EnvironmentKey . int (). map ( :: Port ). defaulted ( \"PORT\" , Port ( 9000 )) val timeoutLens : Lens < Environment , Timeout?> = EnvironmentKey . duration (). map ( :: Timeout ). optional ( \"TIMEOUT\" ) val hostsLens : Lens < Environment , List < Host >> = EnvironmentKey . map ( :: Host ). multi . required ( \"HOSTNAMES\" ) val timeout : Timeout? = timeoutLens ( env ) val port : Port = portLens ( env ) val hosts : List < Host > = hostsLens ( env ) Handling failure \u00b6 When using the http4k Environment to define config, missing or values which cannot be deserialised all now cause a LensFailure to be thrown with a descriptive error message. As before, this results in the application failing to start, but as the exception if both consistent and explicit, diagnosing the problem becomes much simpler. Single-shot Secrets \u00b6 In order to avoid the accidental exposure of sensitive information such as passwords into the application runtime, a new type Secret has been introduced, which tries as much as possible to avoid exposing it's internal value as a readable String . The Secret class is designed to only have the string version of it's value read once, and only within a specific use() block, after which the underlying value is internally overwritten and further attempts to read it throw an IllegalStateException . The typical use-case for this block is to set-up a SQL Datasource or to create a Filter which adds authentication to all outbound requests, as in the example below: package blog.typesafe_configuration.post import org.http4k.client.OkHttp import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.lens.Lens import org.http4k.lens.secret // export USER_PASSWORD=12345 val accessToken : Lens < Environment , Secret > = EnvironmentKey . secret (). required ( \"USER_PASSWORD\" ) val secret : Secret = accessToken ( Environment . ENV ) val authFilter : Filter = secret . use { value : String -> ServerFilters . BearerAuth ( value ) } val authedHttp : HttpHandler = authFilter . then ( OkHttp ()) As with other supported primitives, Secret is available by default in all supported Lens Locations.","title":"Typesafe 12-factor config"},{"location":"blog/typesafe_configuration/#add_typesafe_12-factor_configuration_to_http4k_apps_with_environments","text":"","title":"Add typesafe 12-factor configuration to http4k apps with Environments"},{"location":"blog/typesafe_configuration/#november_2018_daviddenton","text":"","title":"november 2018 / @daviddenton"},{"location":"blog/typesafe_configuration/#intro","text":"This post covers the various concerns around configuring HTTP apps, and introduces the http4k approach for addressing these when deploying applications into cloud-native environments, which leverages the Kotlin type system for maximum safely and code reuse.","title":"Intro"},{"location":"blog/typesafe_configuration/#concerns_when_configuring_applications","text":"One of the tenets of operating applications according to the principles of 12factor , and especially in containerised cloud-native apps, is to inject all app configuration through the use of environmental variables. Additionally, when using more restrictive settings (such as utilising JVM security manager policies or through the use of container images which don't provide an OS baseline) it may not be possible to read files (such as YAML, JSON etc) from disk, which reinforces this approach. There are several particular relevant concerns that we need to address, but overall the effect that we are looking for is that any misconfiguration of the application will result in it failing to startup. For this reason we want to reify all values to check them as soon as possible in the application bootstrap phase.","title":"Concerns when configuring applications"},{"location":"blog/typesafe_configuration/#1_optionality","text":"Kotlin's type system guards us against missing values being injected - for instance the following code will throw a IllegalStateException due to a typo in the parameter name: package blog.typesafe_configuration.pre // export ANTIDISESTABLISHMENTARIANISM=opposition to the disestablishment of the Church of England val definition : String = System . getenv ( \"ANTIDISESTABLISHMENTARIAMISM\" ) However not all configuration values will be required. We can define that there are 3 distinct modes of optionality available for each parameter: Required: These values must be injected for each environment, with no default value defined. Most configurations such as hostnames should always use this form to maximise operational safety. Optional: These values can be supplied, but there is no default value. This category fits well with dynamic properties which could be data-driven (ie. not known at compile-time). Defaulted: These values can be supplied, but a fallback value (or chain of other config values) will be used if they are not. Missing values should produce a reasonable error and stop the app from starting.","title":"1. Optionality"},{"location":"blog/typesafe_configuration/#2_type_coercion","text":"Most applications will require a variety of configuration primitive types, which may or may not map to the Java/Kotlin standard types, including: strings such as service URLs, log levels, or AWS role names numeric values such as ports or retry counts booleans such as debug switch or feature flags duration values for timeouts, backoff times But handling these raw types alone is not enough to guarantee safety - it is best to marshall the values into a suitable operational/domain type that can validate the input and avoid confusion. Kotlin gives us a simple way to do this using require as a guard: package blog.typesafe_configuration.pre class Port ( val value : Int ) { init { require (( 1. . 65535 ). contains ( value )) { \"Out of range Port: ' $ value '\" } } } // export PORT=8000 val port = Port ( System . getenv ( \"PORT\" ). toInt ()) Additionally to the above, it is important to represent those values in a form that cannot be misinterpreted. A good example of this is the passing of temporal values as integers - timeouts defined this way could be easily be parsed into the wrong time unit (seconds instead of milliseconds). Using a higher level primitive such as Duration will help us here: package blog.typesafe_configuration.pre import java.time.Duration data class Timeout ( val value : Duration ) { init { require ( ! value . isNegative ) { \"Cannot have negative timeout\" } } } // export TIMEOUT=PT30S val timeout = Timeout ( Duration . parse ( System . getenv ( \"TIMEOUT\" ))) Obviously, the above is still not very safe - and what's more, a coercion could now fail with one of 3 different exceptions depending on if the value was missing ( IllegalStateException ), unparsable ( DateTimeParseException ) or invalid ( IllegalArgumentException ). The conversion code from String -> Duration must also be repeated (or extracted) for each value that we wish to parse.","title":"2. Type coercion"},{"location":"blog/typesafe_configuration/#3_multiplicity","text":"Configuration parameters may have one or many values and need to be converted safely from the injected string representation (usually comma-separated) and into their internally represented types at application startup: package blog.typesafe_configuration.pre class Host ( val value : String ) // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val hosts = System . getenv ( \"HOSTNAMES\" ). split ( \",\" ). map { Host ( it . trim ()) } Once again, the splitting code will need to be repeated for each config value, or extracted to a library function.","title":"3. Multiplicity"},{"location":"blog/typesafe_configuration/#4_security","text":"The configuration of a standard app will generally contain both sensitive and non-sensitive values. Sensitive such as application secrets, DB passwords or API keys should be handled in a way that avoid storing directly in memory in a readable format or long lived fashion, where they may be inadvertently inspected or outputted into a log file. Dangling code situations such as in the code below are common, and are asking for trouble... package blog.typesafe_configuration.pre import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import java.nio.ByteBuffer val s3 = OkHttp () fun readFile ( secretKey : String , bucketKey : String ): ByteBuffer { return s3 ( Request ( GET , \"https://mybucket.s3.amazonaws.com/ $ bucketKey \" )). body . payload } // export AWS_SECRET_KEY=someSuperSecretValueThatOpsReallyDoNotWantYouToKnow val file = readFile ( System . getenv ( \"AWS_SECRET_KEY\" ), \"myfile.txt\" )","title":"4. Security"},{"location":"blog/typesafe_configuration/#5_configuration_context_overriding","text":"We also want to avoid defining all values for all possible scenarios - for example in test cases, so the ability to overlay configuration sets on top of each other is useful. Although it is against the rules of 12-factor, it is sometimes convenient to source parameter values from a variety of contexts when running applications in non-cloud environments: System Environment variables Properties files JAR resources Local files Source code defined environmental configuration Implementing this kind of fallback logic manually, you'd end up with code like the below: package blog.typesafe_configuration.pre val name = System . getProperty ( \"USERNAME\" ) ?: System . getenv ( \"USERNAME\" ) ?: \"DEFAULT_USER\"","title":"5. Configuration Context & Overriding"},{"location":"blog/typesafe_configuration/#the_http4k_approach","text":"There are already many options for configurational libraries written in Kotlin, but http4k also provides an option in the http4k-cloudnative add-on module which leverages the power of the Lens system already built into the core library to provide a consistent experience to API users. In case you're new to Lenses, here's a recap...","title":"The http4k approach..."},{"location":"blog/typesafe_configuration/#lenses_-_a_recap","text":"In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. A Lens is an stateless object responsible for either the one-way (or Bidirectional) transformation of It defines type parameters representing input IN and output OUT types and implements one (for a Lens ) or both (for a BiDiLens ) of the following interfaces: LensExtractor - takes a value of type IN and extracts a value of type OUT LensInjector - takes a value of type IN and a value of type OUT and returns a modified value of type IN with the value injected into it. package blog.typesafe_configuration.post interface LensExtractor < in IN , out OUT > : ( IN ) -> OUT { override operator fun invoke ( target : IN ): OUT } interface LensInjector < in IN , in OUT > { operator fun < R : OUT > invoke ( value : IN , target : R ): R } interface Lens < IN , OUT > : LensExtractor < IN , OUT > interface BiDiLens < IN , OUT > : Lens < IN , OUT > , LensInjector < IN , OUT > The creation of a Lens consists of 4 main concerns: targeting determines where the Lens expects to extract and inject the values from/to, which can consist of both an overall target and a name within that target. multiplicity handling which defines how many of a particular value we are attempting to handle. the transformation chain of function composition which forms a specification for converting one type to another. This is done in code using the map() method defined on the Lens. the optionality of a Lens denotes the behaviour if/when a value cannot be found in the target. To define a Lens instance through the http4k Lens API, we take an initial target specification, decide it's multiplicity , provide any transformations with map() , and finally reify the specification into a Lens instance by deciding it's optionality. It sounds involved, but it is consistent and the fluent API has been designed to make it simpler. By way of an example, here we define a bi-directional Lens for custom type Page , extracted from a querystring value and defaulting to Page 1. package blog.typesafe_configuration.post import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.lens.BiDiLens import org.http4k.lens.Query import org.http4k.lens.int data class Page ( val value : Int ) { init { require ( value > 1 ) { \"Page number must be positive\" } } } val lens : BiDiLens < Request , Page > = Query . int (). map ( :: Page , Page :: value ). defaulted ( \"pageNumber\" , Page ( 1 )) val pageNumber : Page = lens ( Request ( GET , \"http://abc/search?pageNumber=55\" )) val updatedRequest : Request = lens ( pageNumber , Request ( GET , \"http://abc/search\" )) In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts.","title":"Lenses - a recap"},{"location":"blog/typesafe_configuration/#http4k_environments","text":"in http4k, an Environment object is a context which holds configuration values. It effectively behaves like a Map , in that it can be composed with other Environment objects to provide a consolidated view of all of it's component values. package blog.typesafe_configuration.post import org.http4k.config.Environment import java.io.File val systemEnv : Environment = Environment . ENV val jvmFlags : Environment = Environment . JVM_PROPERTIES val jar : Environment = Environment . fromResource ( \"jar.properties\" ) val filesystem : Environment = Environment . from ( File ( \"fs.properties\" )) val codeBased : Environment = Environment . from ( \"key1\" to \"value1\" , \"key2\" to \"value2\" ) val consolidated : Environment = jvmFlags overrides systemEnv overrides codeBased overrides filesystem overrides jar If you're using any of the other Kotlin-based configuration libraries, the above should look pretty familiar. The difference starts to become apparent when attempting to retrieve values from the Environment instance. This is done using EnviromentKey Lenses, which are an extension of the http4k Lens system that specifically targets Environment objects. package blog.typesafe_configuration.post import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.lens.Lens import org.http4k.lens.duration import org.http4k.lens.int import java.time.Duration data class Timeout ( val value : Duration ) data class Port ( val value : Int ) data class Host ( val value : String ) // export TIMEOUT=PT30S // export PORT=8080 // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val env = Environment . ENV val portLens : Lens < Environment , Port > = EnvironmentKey . int (). map ( :: Port ). defaulted ( \"PORT\" , Port ( 9000 )) val timeoutLens : Lens < Environment , Timeout?> = EnvironmentKey . duration (). map ( :: Timeout ). optional ( \"TIMEOUT\" ) val hostsLens : Lens < Environment , List < Host >> = EnvironmentKey . map ( :: Host ). multi . required ( \"HOSTNAMES\" ) val timeout : Timeout? = timeoutLens ( env ) val port : Port = portLens ( env ) val hosts : List < Host > = hostsLens ( env )","title":"http4k Environments"},{"location":"blog/typesafe_configuration/#handling_failure","text":"When using the http4k Environment to define config, missing or values which cannot be deserialised all now cause a LensFailure to be thrown with a descriptive error message. As before, this results in the application failing to start, but as the exception if both consistent and explicit, diagnosing the problem becomes much simpler.","title":"Handling failure"},{"location":"blog/typesafe_configuration/#single-shot_secrets","text":"In order to avoid the accidental exposure of sensitive information such as passwords into the application runtime, a new type Secret has been introduced, which tries as much as possible to avoid exposing it's internal value as a readable String . The Secret class is designed to only have the string version of it's value read once, and only within a specific use() block, after which the underlying value is internally overwritten and further attempts to read it throw an IllegalStateException . The typical use-case for this block is to set-up a SQL Datasource or to create a Filter which adds authentication to all outbound requests, as in the example below: package blog.typesafe_configuration.post import org.http4k.client.OkHttp import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.lens.Lens import org.http4k.lens.secret // export USER_PASSWORD=12345 val accessToken : Lens < Environment , Secret > = EnvironmentKey . secret (). required ( \"USER_PASSWORD\" ) val secret : Secret = accessToken ( Environment . ENV ) val authFilter : Filter = secret . use { value : String -> ServerFilters . BearerAuth ( value ) } val authedHttp : HttpHandler = authFilter . then ( OkHttp ()) As with other supported primitives, Secret is available by default in all supported Lens Locations.","title":"Single-shot Secrets"},{"location":"blog/typesafe_websockets/","text":"Websockets. But typesafe. And testable. Without the Server. \u00b6 december 2017 / @daviddenton \u00b6 Reaction to the last post introducing http4k was pretty good, and one of the most popular questions was: \"But what about Websockets\" ? The answer to that question at the time was an emphatic \"Not yet\" - because they didn't fit the \"Server as a Function\" model, and the team hadn't worked out a way to deliver them in a simple, offline testable* way. Well, a month is a long time, and we've been beavering away, so now we're thrilled to release Websockets for http4k , which are: Simple : using the same style of API as the rest of http4k, allowing the same dynamic path-based routing as is available for standard HttpHandlers . Typesafe : Marshall and unmarshall typed objects from Websocket Messages using the established Lens API. Testable : This is something that is massively important to us - and just like standard HttpHandlers, http4k Websockets are completely testable in a synchronous online or offline environment. No. Server. Required. Details schmeetails... \u00b6 Just as with HttpHandlers, the here are 2 basic function types which make up the core of the Websocket routing API: A WsHandler - represented as a typealias: (Request) -> WsConsumer? . This is responsible for matching an incoming HTTP upgrade request to a websocket. WsConsumer - represented as a typealias: (WebSocket) -> Unit . This function is called on connection and allow the API user to react to events coming from the connected Websocket by attaching listeners. Additionally, WsMessage objects are used for actual communication - ie. a message which is sent or received on a Websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. And just like the http4k HTTP message model, WsMessages are immutable data classes . An example server \u00b6 The example below shows how: Websockets can be dynamically routed Lens-based marshalling of Websocket message objects using Jackson. WsHandler can be combined with an HttpHandler to make a PolyHandler - an application which can serve many protocols. Conversion of the PolyHandler to a supporting Server can be done via the standard asServer() mechanism, or it can be kept offline for ultra-fast in-memory testing: package blog.typesafe_websockets import org.http4k.core.HttpHandler import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson.auto import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse // in json, this looks like: {\"value\": 123, \"currency: \"EUR\" } data class Money ( val value : Int , val currency : String ) fun main () { // we use the Lens API to convert between the WsMessage and the Money instance, and to // dynamically bind the \"name\" from the path val moneyLens = WsMessage . auto < Money > (). toLens () val nameLens = Path . of ( \"name\" ) // the routing API is virtually identical to the standard http4k http routing API. // on connection, the bound WsConsumer is called with the Websocket instance val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . onMessage { val received = moneyLens ( it ) ws . send ( moneyLens ( received )) } ws . onClose { println ( \"closed\" ) } ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) val http : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } // the poly-handler can serve both http and ws protocols. PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start (). block () } Alternatively, you can check out the Websocket enabled http4k demo: IRC clone in 30 lines of Kotlin . Testability \u00b6 As well as API simplicity, the http4k team are very passionate about testing, and it was very important that this could be done in an out-of-container fashion - ie. in memory and with no server being started. As such, it is possible to call testWsClient() on an WsHandler to provide a synchronous API for testing. Messages and other events can be \"sent\" to a connected websocket and responses will be received back in a completely predictable way from the application under test. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package blog.typesafe_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } } Fin \u00b6 Websocket support is now available for the Jetty server backend in http4k v3.2.0 . We plan to roll out support for other server-backends in due course. Have a play a let us know what you think... Footnotes \u00b6 * We had a bit of a search for \"unit testing websockets\", half through curiosity and half because we wanted to swipe other people's ideas for implementing it. But we came up with nothing - it seems like all the existing JVM HTTP libraries rely on running servers for testing websockets. We hope we're wrong - because the alternative makes us a little . If we are, then please let us know! \ud83d\ude1d","title":"Typesafe Websockets"},{"location":"blog/typesafe_websockets/#websockets_but_typesafe_and_testable_without_the_server","text":"","title":"Websockets. But typesafe. And testable. Without the Server."},{"location":"blog/typesafe_websockets/#december_2017_daviddenton","text":"Reaction to the last post introducing http4k was pretty good, and one of the most popular questions was: \"But what about Websockets\" ? The answer to that question at the time was an emphatic \"Not yet\" - because they didn't fit the \"Server as a Function\" model, and the team hadn't worked out a way to deliver them in a simple, offline testable* way. Well, a month is a long time, and we've been beavering away, so now we're thrilled to release Websockets for http4k , which are: Simple : using the same style of API as the rest of http4k, allowing the same dynamic path-based routing as is available for standard HttpHandlers . Typesafe : Marshall and unmarshall typed objects from Websocket Messages using the established Lens API. Testable : This is something that is massively important to us - and just like standard HttpHandlers, http4k Websockets are completely testable in a synchronous online or offline environment. No. Server. Required.","title":"december 2017 / @daviddenton"},{"location":"blog/typesafe_websockets/#details_schmeetails","text":"Just as with HttpHandlers, the here are 2 basic function types which make up the core of the Websocket routing API: A WsHandler - represented as a typealias: (Request) -> WsConsumer? . This is responsible for matching an incoming HTTP upgrade request to a websocket. WsConsumer - represented as a typealias: (WebSocket) -> Unit . This function is called on connection and allow the API user to react to events coming from the connected Websocket by attaching listeners. Additionally, WsMessage objects are used for actual communication - ie. a message which is sent or received on a Websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. And just like the http4k HTTP message model, WsMessages are immutable data classes .","title":"Details schmeetails..."},{"location":"blog/typesafe_websockets/#an_example_server","text":"The example below shows how: Websockets can be dynamically routed Lens-based marshalling of Websocket message objects using Jackson. WsHandler can be combined with an HttpHandler to make a PolyHandler - an application which can serve many protocols. Conversion of the PolyHandler to a supporting Server can be done via the standard asServer() mechanism, or it can be kept offline for ultra-fast in-memory testing: package blog.typesafe_websockets import org.http4k.core.HttpHandler import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson.auto import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse // in json, this looks like: {\"value\": 123, \"currency: \"EUR\" } data class Money ( val value : Int , val currency : String ) fun main () { // we use the Lens API to convert between the WsMessage and the Money instance, and to // dynamically bind the \"name\" from the path val moneyLens = WsMessage . auto < Money > (). toLens () val nameLens = Path . of ( \"name\" ) // the routing API is virtually identical to the standard http4k http routing API. // on connection, the bound WsConsumer is called with the Websocket instance val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . onMessage { val received = moneyLens ( it ) ws . send ( moneyLens ( received )) } ws . onClose { println ( \"closed\" ) } ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) val http : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } // the poly-handler can serve both http and ws protocols. PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start (). block () } Alternatively, you can check out the Websocket enabled http4k demo: IRC clone in 30 lines of Kotlin .","title":"An example server"},{"location":"blog/typesafe_websockets/#testability","text":"As well as API simplicity, the http4k team are very passionate about testing, and it was very important that this could be done in an out-of-container fashion - ie. in memory and with no server being started. As such, it is possible to call testWsClient() on an WsHandler to provide a synchronous API for testing. Messages and other events can be \"sent\" to a connected websocket and responses will be received back in a completely predictable way from the application under test. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package blog.typesafe_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"Testability"},{"location":"blog/typesafe_websockets/#fin","text":"Websocket support is now available for the Jetty server backend in http4k v3.2.0 . We plan to roll out support for other server-backends in due course. Have a play a let us know what you think...","title":"Fin"},{"location":"blog/typesafe_websockets/#footnotes","text":"* We had a bit of a search for \"unit testing websockets\", half through curiosity and half because we wanted to swipe other people's ideas for implementing it. But we came up with nothing - it seems like all the existing JVM HTTP libraries rely on running servers for testing websockets. We hope we're wrong - because the alternative makes us a little . If we are, then please let us know! \ud83d\ude1d","title":"Footnotes"},{"location":"changelog/","text":"Changelog This list is not intended to be all-encompassing - it will document major and breaking API changes with their rationale when appropriate: v5.28.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Include Vary header on CORS responses. H/T @ollieabbey http4k-multipart : [Fix Break] Multipart form files were all calling deleteOnExit() instead of being deleted when the Body is closed. Possible memory leak for long running processes. The fix MAY be a change of OS files-system usage if you are not closing your MultiPart form body. #H/T @oharaandrew314 for the report. v5.27.0.0 \u00b6 http4k- * : Upgrade some dependency versions including Kotlin to 2.0.10 http4k-core New HTTP status codes. H/T @torfinnberset http4k-core Added helper method for dealing with forms. H/T @tim-mortimer http4k-core [Fix] Close backing DiskLocation when MultipartForm closed. H/T @oharaandrew314 http4k-testing-kotest Fix haveSetCookie and haveCookie to work when cookie isn't present. H/T @bagguley v5.26.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Fix transformer is lost when adding name suffix to Approver H/T @ilya.aliaksandrovich v5.26.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Response caching extensions. H/T @ollieabbey http4k-config : [New Module!] Extraction of typesafe configuration module from http4k-cloudnative. http4k-cloudnative : [Breaking!] Repackaging of typesafe configuration module classes (org.http4k.cloudnative.env) to http4k-config (org.http4k.config). New imports are required. http4k-contract : Adds ApiKeySecurity that identifies a consumer and makes it available for later use. H/T @dhs3000 v5.25.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Breaking Fix] Incorrect lambda request context variable is passed - we now pass the incoming reqeust object instead of the converted http4k request. If you were using the LAMBDA_REQUEST_KEY, you can just use the request passed into the handler instead. v5.24.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Fix] AWS adapter throws on invalid URLs. http4k-testing-webdriver : [Fix] Base path replacement logic for same-dir-path and dot-path URLs. H/T jweidler v5.24.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Allow removal of all queries for a URI. H/T @dhs3000 http4k-format-kondor : Upgrade to new version of Kondor. H/T @uberto http4k-testing-strikt [Break] The upgrade to the latest version drops Java <17 support. If you are still using Java 8, you will need to stick with the previous version of this module. v5.23.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : [Approval test break] Addition of \"nullable\" field to every model property. This improves JSON output compatability with various tooling for generating types from the definitions. v5.22.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [Break] Move classes to alternative package to not clash with existing format objects. v5.21.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [New module] Support for KotlinX DataFrame. v5.21.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-multipart * : [Fix #1113] Disk-backed multipart form field data is now cleaned up when the body is closed, including the parent form directory. v5.21.0.0 \u00b6 http4k- * : Upgrade some dependency versions including Kotlin to v2 http4k-testing-chaos : [Breaking] Changed Trigger to be a fun interface instead of a typealias. Should be no-op or a simple fix to the type. http4k-core : [Possible Break] Renamed CachingFilters.Request/Response to CachingFilters.CacheRequest/CacheResponse . If you have imports then they may break and need to be updated. v5.20.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-chaos * : [Unlikely break]: remove Hamkrest dependency so that it does not appear randomly in your projects. If you were accidentally relying on this it will need to be re-added manually. v5.19.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.24 http4k-core : Add support for the timesource typealias () -> Instant where a Clock is used. H/T @kwydler v5.18.2.0 \u00b6 http4k-core : Add convenience methods to read bodies from HttpMessages as JSON/XML/CSV etc.. request.json() v5.18.1.0 \u00b6 http4k-core : Add convenience methods to set common headers to HTTP message. v5.18.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Behaviour break] We now do not set the legacy Expires header in CachingFilters. Modern caches should use the Cache-Control header instead (max-age). v5.17.0.0 \u00b6 http4k- * : Tweaks to make the K2 compiler happy http4k : Added convenience methods to set the body of an HTTP message. The works for both standard body types and with automarshallers. http4k-core : Fix request source in SunHttp. H/T @dkandalov http4k-contract : Added top-level MetadataRetrieval to schema objects. H/T @BBB http4k-format- * : [Unlikely break] rename with() functions on auto-marshallers to match content type, so you can now do req.json(myObj) and get the content type and body set in one go. Likewise for other content types v5.16.2.0 \u00b6 http4k- * : Upgrade some dependency versions. v5.16.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Add support for surrogate-key headers in EtagSupport. H/T @jason-annadani-springer v5.16.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : [Unlikely break] Rename typo in an ApprovalSource instance http4k-testing-approval : Addition of optional suffix to the approval file name, and added ApprovalTransformer for varying the compared content from the InputStream http4k-core : [Fix #1084] Route name without a beginning / works for everything except static resources. H/T @ArthurS1 v5.15.1.0 \u00b6 http4k-core : [Unlikely break] Change to Meta to remove default params http4k-testing-approval : Add ability to add a suffix to the approval file name. v5.15.0.0 \u00b6 http4k-core : [Unlikely break] Change to Meta to remove default params v5.14.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Make Lenses support metadata passed through the LensBuilder construction methods. H/T @BBB, @ivanmoore @jack-bolles http4k-testing-tracerbullet : Account for spans across traces with same spanId. H/T @IvanPavlov1995 v5.14.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov v5.14.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov v5.14.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Validator optimisation. H/T @dkandalov http4k-testing-webdriver : Adding a space between method name and URI when naming spans H/T @ReinholdsB http4k-testing-webdriver : Multipart forms in the webdriver, including sending files. H/T @gypsydave5 http4k-testing-webdriver : Fix bug in webdriver form submission + a method for relative Uri resolution. H/T @gypsydave5 v5.14.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.23 http4k- * : Static handlers serve an index.html file from a subdirectory. H/T @mbcltd v5.13.9.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Implement Kondor Schema creator. H/T @tamj0rd2 http4k-cloudnative : Read environment properties from yaml resources. H/T @dzappold http4k-webdriver : [Fix] Bug when submitting with inputs of type submit. H/T @gypsydave5 http4k-testing-approval : Allow adding a suffix to an approval test file name. H/T @becky-sequence v5.13.8.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-format-kondor : Expose converterFor method. H/T @tamj0rd2 v5.13.7.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Allow user to provide schema creation implementation. H/T @tamj0rd2 http4k-core : [Fix #1053]: Add BiDiLensSpec defaulted with factory method http4k-core : [Fix #1059]: Update kondor-json to 2.2.2. H/T @asadmanji v5.13.6.1 \u00b6 http4k-core : FollowRedirects also sets port on redirect. v5.13.6.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-serverless-lambda :[Fix #1057] Error when parsing AWS lambda event from S3 bucket http4k-testing-webdriver :[Fix #1050] Http4kWebDriver does not work on Windows due to path issues. H/T @cmh-dev http4k-core :[Fix #1055] Host header should contain host with port. H/T @obecker v5.13.5.0 \u00b6 http4k-client-core : Ensure consistent content-length behaviour across clients http4k-client-apache : Ensure consistent content-length behaviour across clients http4k-client-apache4 : Ensure consistent content-length behaviour across clients http4k-client-fuel : Ensure consistent content-length behaviour across clients http4k-client-jetty : Ensure consistent content-length behaviour across clients v5.13.4.1 \u00b6 http4k- * : Fix broken POM dependencies. v5.13.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Support for data4k progressive data models with field metadata via delegate properties v5.13.3.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-cloudnative * : Ability to override separator in Environment . v5.13.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract * : [Fix] Enums do not pick up custom prefixes in model naming. H/T @ashcor for the tip-off! http4k-opentelemetry * : [Fix] Fix to set HTTP_REQUEST_BODY_SIZE attribute in OpenTelemetryTracing. H/T @dkandalov http4k-contract * : Added Canonical model-namer. v5.13.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-client-helidon : [Fix #1037] Improve support for query parameters. H/T @franckrasolo v5.13.0.1 \u00b6 http4k-testing-tracerbullet : [Fix] Mermaid sequence diagram generation was constantly changing by default editorconfig files and people committing with different IDE settings http4k-server-jetty : [Fix #1023] Header values in quotes lose their quotes. H/T @efasel, @dhs3000 v5.13.0.0 \u00b6 http4k-format-jade4j : [Breaking] This module has been renamed due to the library Jade4J becoming Pug4J. Migration should be a no-op apart from switching the imported module, and renaming your templates from .jade to .pug. Please see Pug4j docs for anything else. http4k-format-pug4j : [New module] Replacement for Jade4j v5.12.2.1 \u00b6 http4k-webhooks : Move VerifyWebhookSignature filter to ServerFilters as it's not for HTTP clients! v5.12.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [New module!] Support for the Standard Webhooks format http4k-core [Fix #1022] For a request with matching if-none-match header the response lacks the etag header. H/T @efasel http4k-core [Fix #1030] Maven POM for http4k-format-jackson-xml is invalid: jackson-dataformat-xml is missing a version v5.12.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : Fix lens replacement of path parameter when there is a regular expression in the path segment http4k-format-jackson : Added lens support for deserialising data4k containers directly from HTTP message bodies (via Body.json(::JsonNodeDataContainer)).toLens() v5.12.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.22, and Jetty 12 (see below). http4k-server-jetty - [Breaking] Upgrade to Jetty 12. This is a major rewrite of the Jetty engine and the API surface is incompatible with v11. If you are using vanilla Jetty backend then this is a NoOp replacement, otherwise fallback to using the new Jetty11 module and then plan migration accordingly. Massive H/T to H/T @FredNordin for the implementation upgrade. http4k-server-jetty11 - [New Module!] Drop-in replacement module for custom Jetty11 users. Constructor is now called Jetty11() instead of Jetty() , so migration should be very simple. Other renames as required (using 11 ) to avoid API clashes in the http4k codebase. http4k-aws : [Breaking] Tweaks to the signature of AwsPreSignRequests . Use AwsRequestPreSigner instead. H/T @oharaandrew314 v5.11.1.0 \u00b6 http4k-aws : Pre-sign AWS requests with the new AwsPreSignRequests class. H/T @oharaandrew314 http4k-serverless-lambda : [Fix #1013] Support multi value query parameters in ApiGatewayV2LambdaFunction ( http4k-serverless/lambda) v5.11.0.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [Unlikely Break: Fix #1011] Jackson does not honour serialisation of Enums when they are used as Map keys. The fix MAY break JSON serialisation (which actually is a bug as the expected behaviour is for the Enums to use the predefined mapping). v5.10.7.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [Fix #1009] Extracting access token from non-standard AccessToken response fails v5.10.6.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : Make RouterDescription print-friendly v5.10.5.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-serverless-lambda : Add support for custom EventBridgeEvent format v5.10.4.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.21 v5.10.3.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract [Fix #1002]: Ability to use RequestContexts for providing a User Principal with Security. v5.10.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core- : [Fix] FollowRedirects now remove host header http4k-testing-webdriver- : Ability to inject clock into the Webdriver v5.10.1.0 \u00b6 http4k-testing-webdriver * : Allow the originalUri method of the OAuthRedirectionFilter to be configured when constructing an OAuthProvider H/T @mbcltd http4k-format- * : Add alternative syntax for Automarshalling injection/extraction of bodies into and out of HttpMessages v5.10.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.20 http4k-testing-webdriver * : Host header is populated in Http4kWebDriver H/T @mbcltd v5.9.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k-client-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k- * : [Breaking - dev only] http4k is now built with Java 21, although Java 8 is still targeted. v5.8.6.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : New filter to validate zipkin headers. H/T @time4tea v5.8.5.1 \u00b6 http4k- * : Fix maven dependencies marked as optional in various http4k modules v5.8.5.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Jetty. http4k-core : Rename Events.then() with Events.and() for clarity. v5.8.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda : Add support to multiple query/header values with the same key v5.8.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator * : Added HTMX emulation on Http4kWebDriver H/T @mbcltd v5.8.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core * : Added extension function ExecutionService.withRequestTracing() to propagate Zipkin traces across threads v5.8.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : BiDiLenses now implement LensInjectorExtractor v5.8.0.0 \u00b6 http4k-core : BiDiLenses now implement LensInjectorExtractor http4k-contract : [Unlikely break] NoRenderer now returns a 404 instead of an empty JSON document. v5.7.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-aws : Add support for AwsSdkAsyncClient. H/T @oharaandrew314 v5.7.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-testing-playwright : [New Module] Easily browser-test your http4k apps with this Playwright JUnit extension! H/T @dmcg for the inspiration. v5.7.3.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.10 http4k-htmx : Added hyperscript.js webjar to the distribution. v5.7.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-htmx : [New Module] Basic support for htmx development, including Webjar and custom lens types http4k-testing-webdriver : Improve support for radio buttons and radio groups in the http4k-testing-webdriver. H/T @mbcltd v5.7.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract * : [Fix #964] ContractRoute - inconsistent behavior on route matching. H/T @potfur for the investigation. v5.7.0.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-server-undertow : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite. http4k-server-jetty : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite. v5.6.5.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-testing-approval : Whitespace is now trimmed from end of approval file content. Improves compatibility with IntelliJ (as final line endings might be added automatically) http4k-testing-webdriver : [Fix #963] Submitting empty textarea form element in the webdriver causes a validation error v5.6.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-server-apache4 : Upgrade compromised commons-codec version. H/T @oharaandrew314 http4k-template-jte : [New module] JTE templating support v5.6.3.0 \u00b6 http4k-security-oauth :Add ability to override response mode in OAuthProvider. v5.6.2.1 \u00b6 http4k-core : [Fix] Extend URI now supports fragment parameters v5.6.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-security-oauth : [Fix] In-memory request tracking for FakeOAuthServer now supports full AuthRequest. v5.7.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-core : Add some filters for Serverless functions. v5.6.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added nonBlank() to set of standard BiDiMappings. H/T @dmcg http4k-core : [Unlikely break: Fix #956] Lenses don't really work for optional fields in HTML form parsing. Form-fields are now filtered for values which are not blank. This means that you may need to change form lenses to be optional and default to an empty string if they are missing. v5.5.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-cloudnative - Add support for enums in EnvironmentKey http4k-testing-tracerbullet : [Breaking] Fixed SequenceDiagrams for Mermaid to be formatted correctly. This may break approval files for any tests. v5.4.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-helidon : API changes from Helidon alpha to M1 http4k-server-helidon : API changes from Helidon alpha to M1 http4k-contract : [Fix #750] JacksonFieldMetadataRetrievalStrategy is incompatible with kotlinx.serialization @Serializable classes. H/T @krissrex http4k-realtime-core : [Fix #951] Add filters to initialise request context for SSE and WS. http4k-realtime-core : [Fix #885] Accept websocket subprotocols. Note that not all servers are currently supported http4k-server-jetty : [Fix #885] Support subprotocols for Websockets v5.4.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-aws : [Fix #656] AWS request signing issue for URLs with special characters. H/T @krissrex http4k-aws : [Fix #947] AWS request signing issue for duplicated headers and header values with multiple spaces. H/T @krissrex http4k-format-kondor-json : [Unlikely Break] Upgrade kondor-json to 2.0.0. H/T @FredNordin v5.3.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.0. http4k-core : Surface root error messages in Body lenses when failure occurs on deserialisation. http4k-template-thymeleaf : [Unlikely Break] Use HTML rendering mode and .html suffix by default for Thymeleaf templates. H/T @mikaelstaldal v5.2.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Fix #939] Override all Request and Response mutators for routed messages. H/T @kwydler v5.2.0.0 \u00b6 http4k-contract-jsonschema : [New module] Extracted this so we can reuse in non-OpenAPI scenarios http4k-contract : [Deprecation] Repackaging of JSON schema classes. Should only affect users if they have explicitly used/extended the standard behaviour. v5.1.2.1 \u00b6 http4k-serverless-lambda * : [Fix #936] AWS SQS null deserialization issues v5.1.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth * : Remove dependency on Kotlin-reflect by adding custom adapters. H/T v5.1.1.1 \u00b6 http4k-serverless-lambda * : [Fix #933] AWS SQS deserialization issue when md5OfMessageAttributes is null. H/T @oharaandrew314 v5.1.1.0 \u00b6 http4k-realtime-core * : Readd test client methods for SSE and WS v5.1.0.0 \u00b6 http4k-server-realtime-core * : [Breaking - Fix #931] Change Websocket and SSE interfaces to return WsResponse and SseResponse objects. This makes it easier to set response headers and control if a connection is made from the incoming request as it is no-longer hidden (it is exposed at the top level instead of being hidden in the SSE and Websocket objects). It also means that the interfaces for the protocols follow the same pattern. http4k-server-jetty * : As above http4k-server-undertow * : As above http4k-core * : [Fix #930] Update content-length header after GZipping it. H/T @bjornbugge v5.0.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : [Breaking] Remove all previous deprecations from all modules for v4. To upgrade cleanly, first upgrade to v4.48.0.0 and then re-upgrade to v5.0.0.0 . This will ensure that you only have to deal with Deprecations between the major versions. http4k-templates-dust : [Breaking] Nashorn is finally removed, so we are dropping support for this module. If you are on-pre Java 19 you can continue to use the old module version with no breaking changes. http4k- *: [Breaking] http4k is now built with Java 20. We are still compiling for older Java versions. New major versions will now be incoming with every major JDK release in order to track new and retired JVM features (6 month cycle). http4k-server-jetty : New Server Backend JettyLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard Jetty remains usable with any Java version. http4k-core : New Server Backend SunHttpLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard SunHttp remains usable with any Java version. http4k-server-helidon : [New Module] Helidon is a Loom-first rewrite of the popular server. Requires Java >= 19 to use. http4k-server-websocket : [New Module] A lightweight Websocket server built on TooTallNate/Java-Websocket . H/T @oharaandrew314 http4k-client-helidon : [New Module] An HTTP client build from the ground up to take advantage of project Loom. Requires Java >= 19 to use. http4k-format-kondor-json : [New Module] Support for KondorJson , the reflection-free JSON library. http4k-testing-tracerbullet : [New Module] TracerBullet allows you to hook into the http4k Events implementation to visually document your applications through testing. See example in reference guide. http4k-contract : Allow RouteMetaDsl to be marked as hidden H/T @oharaandrew314 v4.48.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.22. v4.47.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator Further simplifications of tracing algorithm v4.47.1.0 \u00b6 http4k-core Make it easy to propagate or update trace spans in ZipkinStorage http4k-incubator Further simplifications of tracing algorithm v4.46.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added status lookup by code. H/T @jhult http4k-core : [Unlikely break] Client request tracing now sets and resets the ThreadLocal containing the current Zipkin traces. Possible break if you were relying on Zipkin state in a downstream handler. This change will allow better in-memory testing as traces will be reported correctly inside the context of the filter. http4k-incubator : [Break] Changes to improve how we create Tracing trees, and this the signature of the Tracer to take EventNode which is a tree node. v4.45.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Fix #912 - CatchLensFailure filter now can pass the Request object into the receiver. H/T @mikaelstaldal http4k-server-format-moshi : Add support for Sets http4k-security-oauth : [Breaking] AccessTokens create method took an unnecessary duplicate parameter. To fix, just remove the authorizationCode parameter from your implementations and use the code from the tokenRequest v4.44.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : SameSite cookie is now lax when it comes to casing. v4.44.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Allow setting of compression level on GZip filter in both Streaming and Memory mode. To fix, simply change from GzipCompressionMode.Memory/Streaming to GzipCompressionMode.Memory()/Streaming() v4.43.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Fix] #901. Improve performance of GZip in streaming mode. v4.43.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-undertow : Upgrade websocket requests based on other common headers. H/T @endofhome http4k-security-oauth : [Breaking] Make full callback URI available as part AuthorizationCodeMissing error. Fixes #895 http4k-core : Static resources now return directives as well as content type on served assets. v4.42.1.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.42.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-kotest : [Possible break] Fix of this Kotest issue in new dependency release might lead to some surprising changes in behaviour of matchers for comparing JSON nodes v4.41.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract-ui-redoc : [New Module] Serve Redoc with the redocUiWebjar function. http4k-contract : [New Feature] Serve Redoc with the redocUiLite function. http4k-contract-ui-swagger : [Fix] #880. swaggerUiWebjar now works properly with a non-root path. Plus performance improvements. v4.41.3.0 \u00b6 http4k-incubator TracerBullet diagrams have more options for reporting errors. v4.41.2.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.20. http4k-incubator TracerBullet diagrams have added colours. v4.41.1.1 \u00b6 http4k-contract-ui-swagger Fix dependency from provided -> api v4.41.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-opentelemetry * :Fix #867. OpenTelemetry tracing uses bad default span naming. H/T @krissrex for the report. http4k-contract-ui-swagger : [New Module] Serve a customized Swagger UI via a bundled WebJar with the new swaggerUiWebjar function. H/T @oharaandrew314 http4k-contract : Deprecate swaggerUI in favor of new swaggerUiLite function, which uses a new config format. H/T @oharaandrew314 v4.41.0.0 \u00b6 http4k-core * : [Unlikely break] Fix creation of UriTemplate when it starts/ends with multiple slashes. This shouldn't cause any problems that we know about, but we are bumping the breaking version number just in case. v4.40.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-apache * : Fix #866 - ApacheClient does not handle SocketException. v4.40.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : TracerBullet now renders results of tests by default. Use RenderingMode to switch off this default behaviour. v4.40.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [Possible clash/break] Upgrade to v2.0 of SnakeYaml (CVE fixes etc) may break dependencies which previously used v1.3X.X. It is safe to pin your SnakeYaml version to 1.3X.X if there is a clash with other libraries in your stack. v4.39.0.0 \u00b6 http4k-contract : [Breaking] Support for HTTP webhooks and callbacks in OpenApi3 models. Note that the Swagger UIs do not support OA 3.1.0 yet so we have limited the OA version number to 3.0.0. v4.38.0.1 \u00b6 http4k-core : [Fix] Header parsing to split correctly. v4.38.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.10. http4k-core : [Unlikely break] Header values now trim leading space (as per RFC) http4k-incubator : Added D2 support for tracing diagrams. http4k-testing-approval : Make tests line-ending-agnostic. H/T @oharaandrew314 http4k-format- * : Various tweaks to modules to standardise behaviour. H/T @oharaandrew314 v4.37.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.0. http4k-core : [Fix] #846 - Status.hashCode is inconsistent with Status.equals. http4k-contract : Add new endpoint security type: OpenIdConnectSecurity . H/T @oharaandrew314 http4k-contract : swaggerUi now supports Oauth2 redirects. H/T @oharaandrew314 v4.36.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-multipart : Add lensing of Multipart form fields using JSON and Automarshalling http4k-server-jetty : Add support for serving SSE. H/T @FredNordin http4k-contract : [Breaking Fix] Fix #842 - Map OpenAPI implementation adds all properties as required H/T @BBB v4.35.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Allow access-control-max-age header to be set from cors policy. H/T @moddular http4k-contract : [Fix] Or security renderer was not rendering properly when the component parts are themselves composite securities. v4.35.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : Trace diagram improvements for PUML, Mermaid and Markdown. v4.35.2.0 \u00b6 http4k-incubator : More diagram tweaking. v4.35.1.0 \u00b6 http4k-incubator : Tweak of some diagramming. v4.35.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-failsafe : [New Module!] Failsafe is a lightweight, zero-dependency library for handling failures. H/T @FredNordin http4k-incubator : [Breaking] Rewrite of infrastructure for generating tracing diagrams, including new interfaces and support for rendering to various formats. Initial support for PUML and Mermaid. v4.34.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-undertow : Remove extra dependencies which aren't needed. http4k-contract : fix Path value resolution it starts with same string as the prefix URL segment. H/T @tkint v4.34.3.1 \u00b6 http4k- * : Fix #827 - Requests with unknown HTTP method result in uncaught exceptions v4.34.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support for arrays of enums in OA3. v4.34.2.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.34.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-template-rocker : [New module] Compile-time templating with Rocker! v4.34.0.4 \u00b6 http4k-contract- : Fix errant import which broke multipart Openapi V3 spec. v4.34.0.3 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Re-re-fix. v4.34.0.2 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Refix. v4.34.0.1 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. v4.34.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.21. http4k-format- * : Added auto() methods to arbitrary lenses (so Query, Header, FormField etc..) http4k-core : [Unlikely break] reverseProxy() now takes the authority into account instead of just the hostname from the request. This should only impact you if you are doing reverse proxy operational on client side and using localhost without a port as a proxy. To fix - simply add the port to your proxying setup and all should be good. http4k-contract * : Fix: Remove duplicate content type header. v4.33.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-ktor * : Fix: Remove duplicate content type header. v4.33.2.1 \u00b6 http4k-contract : Fix OpenApi rendering for enums when there isn't reflection. v4.33.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-resilience4j-jetty : Fix #804 - CircuitBreaker counts error twice, once as an error and once as a success http4k-client-okhttp : Added websocket client. H/T @FredNordin. http4k-format-argo : Fix problem with duplicate keys when creating objects. http4k-security-oauth : Ability to add scopes to the OAuth refresh token. H/T @p10r v4.33.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-jetty : Added websocket client. H/T @FredNordin. http4k-format-moshi : Add facility to use lightweight metadata adapter instead of Kotlin reflect. H/T @oharaandrew314 v4.33.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Handlebars. http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda v4.32.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Move Jakarta Servlet code from Jetty as is now shared. http4k-contract : Add UserCredentialsOAuthSecurity . This allows the OpenApi spec to define a Resource Owner Password Credentials grant. It also includes a shortcut to load the principal into a RequestContextLens . H/T @oharaandrew314 http4k-core : Add StringBiDiMappings.csv to map between string and list, with a configurable delimiter and element mapping. H/T @oharaandrew314 http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda v4.32.3.0 \u00b6 http4k- * : Upgrade some dependency versions including CVE fix for Undertow backend. v4.32.2.0 \u00b6 http4k-core : Add StringBidDiMapping.basicCredentials to easily convert between Credentials and basic auth. H/T oharaandrew314 http4k-core : Add Header.AUTHORIZATION_BASIC lens to easily get and set basic Credentials for a message. H/T oharaandrew314 http4k-contract : BasicAuthSecurity now supports a RequestContextLens to store the principal. H/T oharaandrew314 v4.32.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Added ability to make Automarshallers strict. v4.32.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.20. http4k-testing-webdriver : [Unlikely Break] Upgrade has removed deprecated method. v4.31.0.0 \u00b6 http4k-core : [Unlikely Break] Added ZipkinTraceStorage , defaulting to ThreadLocal implementation. This allows centralised storage of trace information in non-standard threading environments (eg. coroutines). v4.30.10.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : YAML is now a recognised content type. v4.30.9.0 \u00b6 http4k-cloudevents : Add custom lenses to retrieve data from a cloud event and an extension function to set it. v4.30.8.0 \u00b6 http4k-cloudevents : Add Jackson.cloudEventsFormat() so we can use custom formats in cloud events lenses v4.30.7.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #779: SunHttp does not blow up if you add a ll value. v4.30.6.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.30.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-websocket : Fix #775 - WebsocketClient.nonBlocking cannot receive messages in binary mode. H/T oharaandrew314 v4.30.4.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.30.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : RefreshingOAuthToken does not blow up when no expiry returned by server. v4.30.2.1 \u00b6 http4k-format-moshi-yaml : [Fix] Re-fix YAML defaults for over greedy boolean values (regression caused by upgrade to SnakeYaml). v4.30.2.0 \u00b6 http4k-security-oauth : Make FakeOAuthServer more configurable, and removed the need for passing in an auth-code generator. v4.30.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Converted AccessToken to be an interface, and internalised a lens which shouldn't have been used by anyone. To fix uses of accessTokenResponseBody , replace with Body.auto().toLens() , importing from OAuthServerMoshi. v4.29.1.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.29.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Slight changes to CSRF generator interface. Should be easy to fix. v4.28.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : Internal refactoring v4.28.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-okhttp : Handle previously escapable HTTP client timeout case. http4k-contract : Added Swagger UI helper route. H/T @oharaandrew314 v4.28.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Undertow backend. http4k-contract : [Unlikely break] Remove direct dependency on kotlin-reflect JAR, as it is brought in my http4k-format-jackson anyway. This builds ok but we have bumped the version number just to be sure. H/T @oharaandrew314 for the inspiration. http4k-format-core : Add ContentNegotiator and auto versions to be plugged into http4k-format-* modules. H/T @oharaandrew314 http4k-core : Add cors exposed headers property. H/T @oharaandrew314 v4.27.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Upgrade Moshi to introduce a JSON node model, thus converting Moshi to be an AutoMarshallingJson. This should open the door to us eventually allowing Moshi to be used in http4k-contracts (and OpenAPI). Massive H/T to @oharaandrew314 for the work that went into this. v4.27.3.0 \u00b6 http4k-contract : OpenApi3 Operation Ids now replace '-' with '_', as '-' interfere with generation of OpenAPI clients. v4.27.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-graphql : Add GraphQL explorer for http4k-graphql. H/T @arnabkd http4k-realtime-core : Add helper for test Websocket. H/T @oharaandrew314 http4k-resilience4j * : Fix #745: ResilienceFilters.CircuitBreak counts an error twice: once as successful, once as error. v4.27.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added support for Web Linking header standard http4k-multipart : Fix multipart upload failure if charset is included in content type. H/T @wickwirew http4k-server-jetty : Remove usage of deprecated status description API. H/T @@makowalski + @mandyvuong v4.27.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.0 v4.26.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Unlikely break] Remove dependency on kotlin stdlib JDK 8 as we don't need it to compile. If this causes a problem, simply re-add api(Kotlin.stdlib.jdk8) to your project dependency list. http4k- * : Fix #744 - Provided dependencies included as runtime in http4k versions > 4.19.1.0. v4.25.16.2 \u00b6 http4k-core : Fix query parameter parsing when value contained = . H/T @overfullstack http4k-security-digest : Fix digest challenge parsing when nonce contained = . H/T @oharaandrew314 v4.25.16.1 \u00b6 http4k-contract : [Revert fix] - File field is described as \"string\" instead of \"file\" in OA3 specification. v4.25.16.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Deprecate eTag filter in favour of ETagSupport. http4k-contract : [Fix] - File field is described as \"string\" instead of \"file\" in OA3 specification. v4.25.15.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #738 - Calculating eTag ate body. http4k-core : Caching filters now replace headers instead of adding them. http4k-server-jetty : Change constructor to use supported shutdown mode. H/T @jshiell v4.25.14.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Refreshing Credentials Provider now does not block if there is more than half of the expiring time left. http4k-core : Fix #735 - use whole message body for etag hash. H/T @aSemy http4k-metrics-micrometer - Enable publishPercentileHistogram for Micrometer request timer H/T @jakubjanecek v4.25.13.0 \u00b6 http4k-server- *: Add support for graceful shutdown (available to most server implementations) H/T @nlochschmidt http4k-core : Simplify hex decoding H/T @dzappold v4.25.12.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml- *: Replace default YAML boolean resolver to be less greedy. v4.25.11.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.21. http4k-core : Fix #728 - No way to set the request timeout when using the JavaHttpClient. H/T @gmulders http4k-oauth-security : Add missing Moshi adapter v4.25.10.1 \u00b6 http4k-core : Fix ServerFilters.BasicAuth handling of passwords containing colons H/T @robd v4.25.10.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Expand out values4k lens option http4k-core : Allow cookie values to be returned unquoted H/T @2x2xplz http4k-format- * : Throw a lens failure if a valid locale was not parsed H/T @oharaandrew314 http4k-opentelemetry : Fix #726 - OpenTelemetry: t.localizedMessage can't be null v4.25.9.0 \u00b6 http4k- * : Upgrade some dependency versions, including Ktor to v2.0.0 http4k-format-jackson-csv * : [New module] H/T @oharaandrew314 for the contribution. http4k-core : New standard mappings for Time primitives. H/T @oharaandrew314 v4.25.8.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.20. http4k-core : Enable overridable behaviour for CatchAll filter. H/T @dcmg http4k-multipart : Add disk cache path to MultipartFormBody.from() parameters. H/T @rny v4.25.7.0 \u00b6 http4k-client-fuel : [New module] An http4k client based on Fuel with both sync and async support. v4.25.6.0 \u00b6 http4k- * : Upgrade some dependency versions, including Jackson to overcome CVE-2020-36518. v4.25.5.2 \u00b6 http4k-contract : Don't output required fields into OpenAPI if there are none. v4.25.5.1 \u00b6 http4k-contract : Small tweak to internal API v4.25.5.0 \u00b6 http4k-contract : Add format OpenApi hints to Arrays and Maps v4.25.4.1 \u00b6 http4k-contract : Remove println from AutoJsonToJsonSchema. Doh! v4.25.4.0 \u00b6 http4k-format- *: Correctly identify integer and number JSON types. This has a knock on effect in OpenApi specifications. v4.25.3.0 \u00b6 http4k-serverless-tencent : Downgrade events library as is insecure. v4.25.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy). v4.25.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy). v4.25.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth [Breaking]: Rename OauthCallbackError to OAuthCallbackError v4.24.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-webdriver * : [Breaking] Upgrade of webdriver to V4 has changed the APIS. The custom By implementation is no longer required so you can use the inbuilt Selenium version instead. The disabledCssSelector By implementation has been removed, although you can simply replicate this using the existing CSS selector model. v4.23.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support OA3 meta fields on properties if they are populated by a custom annotation. http4k-graphql : [Possible breaks] Due to upgrade of underlying graphql lib. v4.22.0.1 \u00b6 http4k-security-oauth : Fix error messages for oauth callback failures v4.22.0.0 \u00b6 http4k-security-oauth [Breaking]: apiBase path is now preserved when building auth and token uris http4k-security-oauth [Breaking]: provide reason when an oauth callback fails http4k-security-oauth [Breaking]: allow id token consumer to fail authentication flow v4.21.1.1 \u00b6 http4k-contract : OpenApi3 - Expose new prefix-overriding in OpenApi definitions. v4.21.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : OpenApi3 - Ability to provide prefixes for all models in a tree. This allows you to have multiple versions of a single model in the specification (at the cost of duplicated schema models). v4.21.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] All metrics now include path when labelled and routed. For consistency, . in path names are now convert to underscores as well. Regexes are also removed from paths on both client and server. H/T @hektorKS http4k-testing-strikt : Fix #709 - Strikt assertion builder for Uri.path H/T @michaelbannister v4.20.2.0 \u00b6 http4k-contract : OpenApi3 - Don't add required field if no fields are required! v4.20.1.0 \u00b6 http4k-contract : Fix #706: Form \"multi\" lens's do not render an items field in contracts. http4k-testing-chaos : ChaoticHttpHandler disables Chaos API when reflection not available. v4.20.0.0 \u00b6 http4k-core : Fix #704: Filters are recreated on every request/ H/T @hektorKS http4k-core : Fix #702: TrafficFilters.ReplayFrom doesn't correctly read from Replay. http4k-server-netty : Fix #703: Netty: null cannot be cast to non-null type java.net.InetSocketAddress http4k-client-apache-async : Remove usage of deprecated API http4k-client-jetty : Remove usage of deprecated API http4k-testing-webdriver : Remove usage of deprecated (internal) API http4k- * : Upgrade some dependency versions. v4.19.5.0 \u00b6 http4k-client-websocket : Apply a timeout when creating a blocking client websocket connection v4.19.4.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.19.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-opentelemetry : Fixes #697: Upgraded OpenTelemetry version to 1.11.0 H/T @jenarros v4.19.2.0 \u00b6 http4k-server-jetty : Replace conscrypt with internal java for ALPN server v4.19.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added ContentDispositionAttachment server filter. H/T @jenarros http4k-core : Fix path conversion for static routing handlers with trailing. H/T @jenarros http4k-contract : Support non-JSON schema types in request definitions. v4.19.0.0 \u00b6 http4k-core : [Potential Break] Fix #693 Cookie implementation uses LocalDateTime val which is implicitly turned into GMT time for cookie. Break is that Cookies now run from Instant instead of LocalDateTime. Thanks to @maedjyuk-ghoti for alerting us to chase down this 5y+ standing bug! http4k-security-oauth : Fixes to the InsecureCookieBasedOAuthPersistence to make it more user-friendly. http4k-server-netty : Keep-alive for Netty when not streaming. H/T @jakubjanecek for the contrib! v4.18.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fixed URLPathSegment encoding/decoding based on RFC 3986. H/T @jenarros for the thoughtful and through contribution! v4.17.9.0 \u00b6 http4k-core : Added mapping for enum() in lenses. v4.17.8.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #687 - OpenApiV3 object serialization. H/T @lawkai v4.17.7.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : Fix for serialising Maps with null values (the key should still be rendered!) http4k-format-moshi-yaml : Remove accidental stack trace dump. v4.17.6.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [New module] YAML marshalling with zero-reflection is now possible due to a combination of Moshi and SnakeYaml http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path. http4k-format-jackson-xml : Add autoBody for ConfigurableJacksonXml. H/T @oharaandrew314 v4.17.5.0 \u00b6 http4k- * : Upgrade some dependency versions, including ForkHandles to 2.0.0.0. http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path. v4.17.4.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.10 http4k-incubator : Playing with TracerBullets... a generic interface for building TraceTrees from lists of MetadataEvents. v4.17.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : Fix #679. Approval tests delete actual when passing. v4.17.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Added Servers to OpenApi renderer. H/T @zsambek and @MarcusDunn for making it happen. v4.17.1.0 \u00b6 http4k-core : Make timeouts configurable for Java8HttpClient . v4.17.0.0 \u00b6 http4k- * : [Careful] Upgrade some dependency versions, including Kotlin to 1.6.0. http4k- * : [Breaking] Removal of all previously deprecated methods and types. To ensure you get the smoothest experience, please upgrade to v4.16.3.0 first, deal with the replacements and then upgrade to 4.17.0.0 v4.16.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : Check the content type after the content is checked. v4.16.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support for top-level enums in schema. http4k-contract : Support for enums in Header/Query/Paths. Finally! v4.16.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Upgrade build process to Kotlin. H/T @franckrasolo v4.16.0.0 \u00b6 http4k-contract : [Breaking] Added API-level tags to the contract rendering. http4k-serverless-lambda : Fix encoding of body to work with gzip filter v4.15.0.0 \u00b6 http4k-contract : [Break] BearerAuthSecurity is now more typesafe when taking a lens. v4.14.1.4 \u00b6 http4k-serverless-lambda : More fixing of deserialisation of SNS events. v4.14.1.3 \u00b6 http4k-serverless-lambda : Fix deserialisation of SNS events. v4.14.1.2 \u00b6 http4k-contract : Fix #667 - Jackson annotations being missed in FieldRetrieval. http4k-undertow : Server now handles HTTP requests gracefully when there is no HTTP handler set. v4.14.1.1 \u00b6 http4k-core : ChaoticHttpHandler is now event better behaved when chaos is not enabled and respects routing templates when applying. http4k-core : Fix #665 - OpenAPI json is incorrect when multi string query lens with defaulted values is used. H/T @suyash192 v4.14.1.0 \u00b6 http4k-core : ChaoticHttpHandler is now better behaved when chaos is not enabled. v4.14.0.0 \u00b6 http4k-core : Tidying up HttpEvents http4k-graphql : [Break] Handle null variables in calls. v4.13.4.0 \u00b6 http4k-core : Added convenience HttpEvents v4.13.3.0 \u00b6 http4k-core : ServerFilter request tracing now reinstates previous trace on exit instead of clearing it. v4.13.2.0 \u00b6 http4k-core : Make MetadataEvent a data class. v4.13.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Rename AsyncHttpClient to AsyncHttpHandler (deprecation). http4k-contract : Fix #664 - Introduce OpenAPIJackson to not serialize nulls by default into OpenAPI specs. If you use your own Jackson instance, you can replicate this behaviour by using .setSerializationInclusion(NON_NULL) on your custom ObjectMapper implementation. v4.13.0.0 \u00b6 http4k-core : Reverse Proxy available in both routing and non-routing version. Use reverseProxy() or reverseProxyRouting() accordingly v4.12.3.1 \u00b6 http4k-core : Reverse Proxy router now falls back to URI host when Host header missing. v4.12.3.0 \u00b6 http4k-security-oauth : Nicer OAuth client filters. v4.12.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #657 Use jackson to serialize enum models for OpenApi. v4.12.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix TrafficFilters.RecordTo eating body stream when used on a server. v4.12.0.1 \u00b6 http4k- * : Fix #652 - AWS event format adapters have fields with wrong cases. v4.12.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.5.30. http4k-testing-chaos : [Break] Behaviour is now an abstract class instead of a typealias. Super simple to fix. :) v4.11.0.1 \u00b6 http4k-graphql : Fix - Downgrade graphql-java and fix Graphql reference example. 4.11.0.0 contained an incompatible version of graphql-java for generate use. H/T @razvn for spotting and fixing. :) v4.11.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-jackson : Fix #646 - Boolean field can escape lens check without throwing MissingKotlinParameterException. http4k-aws : Set the query parameter to empty string if it's value is null, instead of \"null\". H/T @raelg for the PR. http4k-contract : [Possible (small) Break] Fix #644 - Lazy init contract without path params: Type mismatch. Contract routes with 0 parameters are now able to be constructed lazily - which has added (for consistency) a secondary to(fn: () -> HttpHandler) function to the construction DSL. This may cause overload ambiguity when routes are defined withtout the request input parameter. To fix, un-ambiguate you bindings! (eg. to { Response(OK) } becomes to { _ -> Response(OK) } ). H/T @dbacinski for the investigation. v4.10.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #638 - Revert changes to make Uri incompatible with req -> next.invoke(request.header(\"foo\", \"bar\")); http4k-testing-kotest - Possible Break : DUE TO KOTLIN 1.4.10. Remove a haveBody matcher which uses Matcher directly, because of a bug in Kotest: https://github.com/kotest/kotest/issues/1727 http4k-format-jackson - Possible Break : DUE TO KOTLIN 1.4.10. Inline classes do not deserialise properly. See: https://github.com/FasterXML/jackson-module-kotlin/issues/356 v3.261.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Remove some example code which was mistakenly added to some main src dirs. No impact on anything other than JAR size. http4k-aws * : Add pluggable Amazon SDK client, allowing you to plug an HttpHandler into the Amazon SDK. v3.260.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Unlikely break : Added some nicer naming and examples for when people are calling http4k via Java code. http4k-core : Fixed SunHttp server backend not setting content length, and hence responses are always chunked. v3.259.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-netty : Fix #141 Http4k-netty performs really badly on all benchmarks. Massive H/T adam-arold! http4k-server-ratpack : Tweak to SO_BACKLOG size (1000). v3.258.0 \u00b6 http4k-testing-kotest : [New module] A set of matchers for use with the kotest library. H/T @nlochschmidt for the PR. http4k- * : Upgrade some dependency versions. v3.257.0 \u00b6 http4k-serverless-* : Making the Serverless APIs consistent between flavours by ensuring that all Serverless functions act by class extension and not reflection based approach. Deprecated old approach. Hopefully this is simpler.. :) v3.256.1 \u00b6 http4k-core : Fix #470. Path.of cannot decode path parameter values containing %/ v3.256.0 \u00b6 http4k-security-oauth : Add ability to handle form encoded responses in OAuth responses. v3.255.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : - Fixed up Maven dependencies so that they are not exporting compileOnly libraries into POMs. http4k-security-oauth : Remove \"user\" from default list of GitHub scopes as it gives you write access to the profile. New default is empty (just public data). http4k-core : Improve defaults of SunHttp server. H/T @nlochschmidt for the PR. http4k-contract : Add description to OpenApi schema fields using Jackson annotations. H/T @env0der for the PR. v3.254.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added hostDemux() routing for when you want to select an HttpHandler based on the Host header. v3.253.0 \u00b6 http4k-core : Replaced implementation of JavaHttpClient with one from Java standard library. Should you not yet have access to the Java 11 SDK, we renamed the old implementation to Java8HttpClient . Note that some headers that are added by default by the old Java8 implementation will no longer be added. http4k-core, Breaking : Change Body.binary() lens to use an InputStream instead of a raw Body . To fix, just provide the InputStream by calling Body.stream() or similar. http4k-client-websocket, Unlikely break : Allow API users to pass in their own Draft object for custom protocols. If broken, simple fix is to just use named arguments in the construction call to the client. http4k- * : Upgrade some dependency versions. v3.252.0 \u00b6 http4k-server-apache, http4k-client-apache, http4k-client-apache-async, Breaking : Updated to Apache HTTP 5.X.X. H/T to @jshiell. Note that the underlying Apache APIs have changed in the v5 release. For the clients, this should only break if you have customised the underlying HTTP CloseableHttpClient that is passed to the constructor of the http4k client. If you have, we have you covered with.... http4k-server-apache4, http4k-client-apache4, http4k-client-apache4-async : New modules to maintain previous integration with Apache HTTP 4.X.X. Intended to reduce the impact on projects that are not ready to move to v5 yet. In these compatibility modules, renamed ApacheClient -> Apache4Client and ApacheAsyncClient to Apache4AsyncClient - which is the only change that should be required in end user code. http4k-serverless-openwhisk : Fixes to support binary content types and overcome issues with the request/response format of the OW Java runtime. http4k-core : Added some Filters for base64 encoding and decoding responses. http4k- * : Upgrade some dependency versions. v3.251.0 \u00b6 http4k-core : Added support for multiple \"cookie\" headers. H/T @jshiell http4k-serverless-openwhisk : New serverless module! http4k-serverless-*, Breaking : - Repackage some functions to org.http4k.serverless package. Just change the package names to fix. v3.250.0 \u00b6 http4k-core : Add Request.source to provide extra information about the request origin (address/port/scheme). H/T @kam1sh and @jshiell for the contributions. http4k-security-oauth : Add OAuth provider configuration for Facebook. H/T @knyttl for the PR. http4k-server-netty : Implement KeepAlive. H/T @carbotaniuman for the PR. http4k-bom : New Bill-Of-Materials module! http4k- * : Upgrade some dependency versions. v3.249.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-netty : Add support for response streaming. H/T @carbotaniuman for the PR. http4k-serverless-gcf : New serverless module! H/T @ssijak for the PR. v3.248.0 \u00b6 http4k-server-ratpack : New backend module! http4k-format-jackson-yaml : [New module] http4k- * : Upgrade some dependency versions. http4k-cloudnative : - Fix #418 - Fix separator propagation when adding values to an existing MapEnvironment. H/T @jshiell http4k-contract : - Add support for securing the API description endpoint. H/T @goodhoko for the PR. http4k-client-websocket : Added auto-reconnection support on blocking WsClient. H/T @alphaho for the PR. http4k-format-* : Rename/deprecate asXYZString(Any) -> asFormatString(Any) in all modules v3.247.0 \u00b6 http4k-server-ktornetty : New backend module! H/T @albertlatacz for the contribution! http4k- * : Upgrade some dependency versions. http4k-security-oauth : Fix #414 BasicAuth server filter to not throw an exception on invalid base64 input. H/T @Sebruck for the fix. v3.246.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-template-pebble : Fix #411 - Non-root pebble templates when using CachingClasspath from a compiled JAR. H/T @alyphen v3.245.1 \u00b6 http4k-server-ktorcio : Fix #410 - KtorCIO does not stop properly. v3.245.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Factored out Http4kServletAdapter to allow usage of the Servlet API outside of creating a Servlet instance. http4k-*, Breaking (prevent API abuse) : Restricted generic with() method actual http4k types. Usage outside our API should not use this method. http4k-contract : Fix #404 - Rework of some FieldRetrieval classes to remove duplication and to support PropertyNamingStrategies set at the global level v3.244.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : Fix #397 - Fixed up Maven dependencies so that they are not bringing in runtime libraries. http4k-core : - Add enum StringBiDiMapping #395 - H/T @goodhoko v3.243.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.72 . http4k-security-oauth : A strategy can now be passed into AuthRequestWithRequestAuthRequestExtractor to determine how to combine AuthRequest and RequestObject H/T @tom v3.242.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-servirtium : Improve error diagnostics. H/T @vchekan for the PR. http4k-*, Unlikely Break : Change Router to return RouterMatch instead of nullable HttpHandler . This allows us to support METHOD_NOT_ALLOWED (405) if we match a path but not a verb instead of just NOT_FOUND (404). This should break custom ro H/T @jshiell for the PR. v3.241.0 \u00b6 http4k-security-oauth, Breaking : client_id along with the corresponding TokenRequest is passed into access and refresh token generators so additional validation can take place H/T @tom v3.240.0 \u00b6 http4k- * : Upgrade Kotlin to 1.3.71 . http4k-testing-servirtium : Switch OkHttp client for Apache. http4k-server-jetty : Made some classes non-internal so they can be easily reused for custom ServerConfig implementations. v3.239.0 \u00b6 http4k-client-websocket, Breaking : Added extra onError handler when creating a non-blocking websocket. http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.70. v3.238.0 \u00b6 http4k-security-oauth : Early work on supporting refresh tokens. H/T @tom v3.237.0 \u00b6 http4k-core : Fix #377. Added replaceHeaders() method. Thanks to @bastman for the idea. http4k-contract : Fix nullability of references in OpenApi3 v3.236.0 \u00b6 http4k-testing-servirtium : Don't pass recording handler into non-test methods as a resolved parameter. v3.235.0 \u00b6 http4k-testing-chaos, Break/Rename : ChaosEngine is now exposed when configuring API. Renamed withChaosEngine() to withChaosApi() , replaced toggle() and update() with enable()/disable() v3.234.0 \u00b6 http4k-testing-chaos, Break : Tweaked API make it simpler to use the ChaosEngine via programmatically (as opposed to REST). http4k-testing-servirtium, Tiny break : Tweaks to InteractionOptions to make working with Servirtium tests a bit nicer. v3.233.0 \u00b6 http4k-testing-servirtium : Upgrade ServirtiumServer to use OkHttp instead of JavaHttpClient (due to streaming restrictions on MiTM). http4k-testing-servirtium, Break : Rename Github to GitHub . v3.232.0 \u00b6 http4k-format-kotlinx-serialization : New JSON module! H/T @joscha-alisch for the PR. :) http4k-testing-servirtium : Work around Kotlin @JvmOverloads problem in ServirtiumServer. http4k- * : Upgrade some dependency versions. v3.231.0 \u00b6 http4k-testing-servirtium : Making API a bit more Java-compatible friendly. Ability to vary the Server implementation. http4k-server-jetty : Fix #362 - Websocket disconnect early causes lateinit reference race condition. H/T @fintara for the report/fix. v3.230.0 \u00b6 http4k-aws : Improved efficiency of building AWS credentials (replace String.format). http4k-testing-servirtium : Making API a bit more Java-compatible friendly. http4k- * : Upgrade some dependency versions. v3.229.0 \u00b6 http4k-security-oauth : Allowing for custom authenticate methods when fetching access tokens H/T @tom v3.228.0 \u00b6 http4k-testing-servirtium, Breaking : API is still in beta, so moving to a more composed approach which will increase reuse and allow for running Servirtium infra without a dependency on http4k or Junit. Added loading from GitHub. :) http4k-security-oauth, Breaking : Audience on request object is now a list to support multiple audiences. H/T @tom http4k-security-oauth : Nonce is now also passed through on RequestJwts, so it can be added to request jwts. H/T @tom v3.227.0 \u00b6 http4k-core : Implmement #340. Support SameSite cookies. H/T @danielwellman for the contribution. http4k-format-jackson : Made JacksonJsonPropertyAnnotated Kotlin 1.4 safe (call to superclass might return null). H/T @pyos for spotting this. v3.226.0 \u00b6 http4k-testing-servirtium : Moved Servirtium code to new module - was previously [http4k-incubator]. v3.225.0 \u00b6 http4k-incubator : Rewrote Servirtium code to support manipulations. v3.224.0 \u00b6 http4k-security-oauth : Fix issue where AuthRequestWithRequestAuthRequestExtractor doesn't take into account scopes not being nullable correctly. H/T @tom v3.223.0 \u00b6 http4k-security-oauth : Adding expiry to RequestObject . H/T @tom http4k-security-oauth : Fixing issue where unknown fields cause extracting RequestObject from a jwt, fails due to unknown fields. H/T @tom v3.222.0 \u00b6 http4k-security-oauth, Breaking : Error responses in the authorise endpoint now take into account values from the request parameter, this will require a validator for that jwt be implemented. H/T @tom http4k-security-oauth, Breaking : State is now its own type, and not just a string, so it can be validated. H/T @tom http4k-security-oauth, Breaking : redirectUri on AuthRequest is now nullable as it might come on a request jwt, this is validated to be always be present downstream. H/T @tom http4k-security-oauth : Allow parsing of request jwt. H/T @tom http4k-security-oauth : Adding RequestObject to AuthRequest . H/T @tom http4k-security-oauth : Adding AuthRequestWithRequestAuthRequestExtractor that will extract the request from the jwt, assuming the validator is implemented which can be used instead of just using AuthRequestFromQueryParameters if support for parsing a request jwt is required. H/T @tom v3.221.0 \u00b6 http4k-*, Unlikely break from Java only : Make all custom http4k exceptions extend RuntimeException. This helps with Java compatibility so things like LensFailure inside Java Lambdas don't require catching (as they are caught/dealt with by other bits of http4k automatically) v3.220.0 \u00b6 http4k-moshi, Behaviour break : Fix #353 Don't fail by default on unknown properties. This is the expected default behaviour for all JSON implementations. H/T cnusp for the report. v3.219.0 \u00b6 http4k-incubator : Next iteration of Servirtium JUnit extensions. Improved API to support multiple storage engines. v3.218.0 \u00b6 http4k-incubator : Next iteration of Servirtium JUnit extensions. Correct indexing of interactions. http4k-security-oauth : Authorisation rendering will now taking into account 'response_mode' of either query or fragment in responses and no longer just use the default fo the 'response_type'. H/T @tom http4k-security-oauth, Breaking : Error responses in the authorise endpoint will actually redirect back to ' redirect_uri' assuming the validator correctly validates both the 'client_id' and 'redirect_uri' to be valid. H/T @tom v3.217.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : Next iteration of Servirtium JUnit extensions. Only check content which is in the contract when replaying. v3.216.0 \u00b6 http4k-core, Breaking : Removed clashing Events then() from deprecated (meaning it cannot be used as there is also another then() in that package). Use the one in org.http4k.events instead. http4k-security-oauth : Adding nonce to AuthorizationCodeDetails H/T @tom v3.215.0 \u00b6 http4k-core : GZip client filters now send correct accept-encoding header. @jshiell http4k-core : New AcceptGZip client filter allows handling of remote GZip without compressing client requests. @jshiell v3.214.0 \u00b6 http4k-core : Fix #344 H/T Streaming GZip encoder loses data. @jshiell v3.213.0 \u00b6 http4k-security-oauth : Fixing wrong AuthRequestExtractor passed to AuthRequestTrackingFilter. H/T @tom v3.212.0 \u00b6 http4k-security-oauth : allowing additional properties to be stored on auth request, if using additional extractors H/T @tom v3.211.0 \u00b6 http4k-core : Fixes for #338 - Gzip filters send content-encoding of gzip even when body is empty. H/T @jshiell http4k-security-oauth, Break : OIDC callback urls using the ResponseType 'code id_token' will now have the parameters returned as a fragment not a query as per 3.3.2.5 of the OpenID Connect Core 1.0 spec H/T @tom http4k-security-oauth, Break : Initial support of nonce in OIDC requests H/T @tom v3.210.0 \u00b6 http4k-core : Support for GZipping response streams. H/T @jshiell http4k-security-oauth : Adding expires_in to token endpoint response. H/T @tom v3.209.0 \u00b6 http4k- * : Added Status to auto-marshalling JSON mappings. http4k-security-oauth : Adding token_type to token endpoint response, and strip out nulls in response. H/T @tom v3.208.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : PR #333. Copy zipkin traces across threads. H/T @jshiell for the PR. http4k-testing-approval : Close Readers when reading from them. http4k-incubator : Next iteration of Servirtium JUnit extensions for recording and replaying. v3.207.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-incubator : Added first cut of Servirtium classes for recording and replaying traffic. Needs validating in the wild http4k-format-jackson : Fix #320. http4k-format-jackson incompatible with jackson-module-kotlin 2.10.1 v3.206.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #323. Doc generation does not work with multipart lenses. http4k-format-jackson : Fix #313. Jackson serialization is not working properly with polymorphic types stored in a collection. H/T @alphaho for the PR :) http4k-core, Break : Renamed value on ParamMeta to description . v3.205.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.61 http4k-security-oauth : allowing setting scopes on AccessToken creation so they are set on the response. H/T @tom v3.204.0 \u00b6 http4k-core, http4k-aws : - increase efficiency of Hex implementation for trace ids and HMAC. H/T @time4tea http4k-cloudnative : Reimplemented Environment to be more efficient. H/T @time4tea for noticing this. v3.203.0 \u00b6 http4k-security-oauth : On generating tokens allowing for the client id to be based on the result of validation rather than just the form parameters of the request. To support client assertions. H/T @tom v3.202.0 \u00b6 http4k-security-oauth : Adding new errors to support issues with client assertions. H/T @tom v3.201.0 \u00b6 http4k-security-oauth : Allowing a scope to be set on AccessToken. Allowing for more low level validation of Authorise and Token Requests, by implementing org.http4k.security.oauth.server.AuthoriseRequestValidator and org.http4k.security.oauth.server.accesstoken.AccessTokenRequestAuthentication respectively. H/T @tom v3.200.0 \u00b6 http4k-contract : Support multiple request bodies in OpenApi v3 v3.199.1 \u00b6 http4k-format-jackson : Fix #313 Part 2 - Revert default behaviour for collections of polymorphic types, but is now overridable by using autoBody() instead of auto() . Reopened #313. v3.199.0 \u00b6 http4k-format-jackson, Breaking : Fix #313 - ConfigurableJackson.autoBody implementation would not work with collections of polymorphic types. This fix has the effect of blowing up auto-json behaviour when classes are defined inside functions (causing nasty java.lang.reflect.GenericSignatureFormatError: Signature Parse error exceptions). To remedy, just move inlined classes outside of the functions that they are defined in. H/T @alphaho for the PR. http4k- * : Update some dependency versions v3.198.0 \u00b6 http4k-core, Breaking : Reworking of ContentType to support multiple directives. directive field is now directives , so just add the extra 's' to fix :) http4k-security-oauth : Moar options on OAuthProviderConfig . H/T @tom v3.197.0 \u00b6 http4k- * : Update some dependency versions, including Kotlin to 1.3.60 . http4k-core : Make Query value optional when setting on a Request . http4k-core, Breaking : Fix #316. Optional Query lens handling is more accurate. See issue for details of change in behaviour. v3.196.0 \u00b6 http4k- * : Update some dependency versions. http4k-format-jackson, http4k-format-gson : Add support for auto marshalling Throwable in a sensible way. http4k-cloudnative : Renamed badly named UpstreamRequestFailed to RemoteRequestFailed . Improved error handling. v3.195.1 \u00b6 http4k-cloudnative : Fix adding value to overridden environment when using set() . H/T @jippeholwerda for the PR v3.195.0 \u00b6 http4k-security-oauth : Tweak to handle Content-Type comparisons (with and without directive). H/T @jippeholwerda for the PR http4k-multipart] - [Breaking : Added support for setting custom headers in Multipart form fields and files. This has removed the String as the default field type (it is now MultipartFormField . Calls to create lenses using MultipartFormField will now require MultipartFormField.string() instead. v3.194.0 \u00b6 http4k-contract : Useful tweaks to the contracts API v3.193.1 \u00b6 [http4k-cloudnative] Fix #304 - map get() does not respect fallback values in overridden environment. v3.193.0 \u00b6 http4k-contract : Marking endpoints as deprecated in OpenApi3 v3.192.0 \u00b6 http4k-template-jade4j : [New module] H/T @RichyHBM for the contribution! :) v3.191.0 \u00b6 http4k-contract : Better support for overriding of raw map definition id in JSON schema generation v3.190.0 \u00b6 http4k-core : Added method to (immutably) modify status on Response . H/T @brandon-atkinson for the suggestion http4k-core : Added composite object support to lens system, allowing creation of simple lenses which draw from several different values (of the same location only - e.g Query/EnvironmentKey) http4k-contract : Support for overriding the entity definition id in JSON schema generation http4k- * : Update some dependency versions. v3.189.0 \u00b6 http4k-server-netty : Fix reported port in Netty . H/T @fantayeneh for the PR :) http4k-security-oauth : Add validateScopes() to ClientValidator . H/T @tom v3.188.0 \u00b6 http4k-contract : Support multiple-response models in OpenApi2 and 3. Note that this currently is unsupported in the OpenApi UI due to a bug (which doesn't display the schema for the response correctly). However, the JSON schema is generated correctly in these cases. http4k- * : Update some dependency versions. v3.187.0 \u00b6 http4k- * : Update some dependency versions, and changes to various APIs involved (Jackson and Resilience4J) http4k-core : - Add YearMonth support to standard JSON mappings http4k-format-jackson, http4k-format-gson, Possible break : - Moved reified NODE.asA() method from JsonLibAutoMarshallingJson down onto the instances of the Json ( ConfigurableJackson / ConfigurableGson ). This is so that we can handle generified classes such as lists and maps correctly. (As per the problems fixed in 3.181.0) v3.186.0 \u00b6 http4k-core : - Rollback a couple of places which were using Java9+ APIs (for no good reason). v3.185.0 \u00b6 http4k-contract : Improvements to rendering enums as their own objects in JSON Schema. v3.184.0 \u00b6 http4k-contract : Add Cookies options to contract DSL v3.183.0 \u00b6 http4k-serverless-lambda : Add ability to access Lambda context. H/T @ivoanjo for the PR. http4k-contract : Fix rendering of OrSecurity when there are more than 2 parts. v3.182.0 \u00b6 http4k-core : Rename EventsFilter to EventFilter because sanity. http4k-format-jackson, http4k-format-gson : Reintroduce autoBody() method v3.181.0 \u00b6 http4k-core : Added base events implementations for StructuredLogging. http4k-core, Repackage : Events classes are now in org.http4k.events . http4k-core, Breaking : EventCategory is no longer a field of Event . To fix, just remove override from your Event classes. http4k-format-jackson, http4k-format-gson : Fixed problem when attempting to deserialise generic Lists. v3.180.0 \u00b6 http4k- * : Update various dependencies. http4k-testing-hamcrest : Improve messages of Hamkrest matchers. H/T @albertlatacz http4k-cloudnative : Fix #291 - Readiness check result when there are > 2 checks may not report the correct result. H/T @alfi http4k-security-oauth, Possibly breaking : Making client_secret optional in AuthorizationCodeAccessTokenRequest to support non client_secret flows. H/T @tom v3.179.1 \u00b6 http4k-client-okhttp : Include status description in Response object. v3.179.0 \u00b6 http4k-contract : Added OpenApiExtension interface, which allows the definition of extensions that will modify the OpenApi specification JSON. H/T @rgladwell for the inspiration. http4k-contract : Support composite security models using or() and and() . Once again, H/T @rgladwell :) v3.178.0 \u00b6 http4k-security-oauth, Possibly breaking : Request is passed as a parameter to the ClientValidator. Just pass it in! :) H/T @tom http4k-contract, Behaviour change : When specified, individual route security now replaces global security (this is as the security model in the OpenApi spec is specified) as opposed to both being applied. v3.177.0 \u00b6 http4k-security-oauth, Possibly breaking : More support for OIDC, adding state to AuthorizationCodeDetails, and passing it into createForAccessToken on IdTokens. H/T @tom v3.176.0 \u00b6 http4k-security-oauth : More support for OIDC. H/T @tom v3.175.0 \u00b6 http4k- * : Update various dependencies, including Kotlin to 1.3.50. http4k-security-oauth : Some support for OIDC. H/T @tom v3.174.0 \u00b6 http4k- * : Update various dependencies, including Jackson for a CVE. v3.173.0 \u00b6 http4k-core : Fix #273 - parentSpanId trace incorrectly populated when no previous traces http4k-contract, Unlikely Break : Remodelled how Security is rendered, so it's possible that this may break slightly for customer implementations http4k-contract : Added support for Implicit OAuth flow, with suport for custom googleCloudEndpoints Security. H/T @rgladwell v3.172.0 \u00b6 http4k-core : Added uni-directional serialization/deserialization options to JSON lib auto-conversion configuration. v3.171.0 \u00b6 http4k-core, Break (mitigation) : Replaced default resource loader location for singlePageApp() to /public instead of root - this is for safety of NOT serving the root of the classpath by default. v3.170.0 \u00b6 http4k-core : Add a warning when static() is used with no package path, thus exposing the contents of the classpath remotely. v3.169.0 \u00b6 http4k- * : Update various dependencies. v3.168.0 \u00b6 http4k-contract : Collect LensFailure causes into a single place when validating. v3.167.0 \u00b6 http4k-contract, Possibly Break : Open out ErrorResponseRenderer interface to take LensFailure instead of the individual failures when rendering badResponse() . To fix, simply wrap the list of failures into a LensFailure. v3.166.1 \u00b6 http4k-core : Tweak singlePageApp() routing handler, to correctly apply filters when fallback page is used. v3.166.0 \u00b6 http4k-core : Added singlePageApp() routing handler, which matches both static content or falls back to the root path index file v3.165.0 \u00b6 http4k-contract : Fix invalid OpenApi2 when root and base path match. H/T @rgladwell http4k-contract : ContractRoute is now an HttpHandler , so no need to wrap contract routes in a contract {} to test them. H/T @rgladwell for the inspiration. http4k-contract : Support Host/baseUri values in OpenApi2. H/T @rgladwell http4k-contract : Optionally add description route to route list H/T @rgladwell v3.164.0 \u00b6 http4k- * : Update various dependencies, including Kotlin to 1.3.41. http4k-testing-approval : Upgrade of HTML library from above may have an effect on output of HTML approval tests. http4k-contract : Support for more Jackson annotations in JSON Schema rendering. H/T @tom for the PR contributing this. v3.163.0 \u00b6 http4k-testing-chaos : Add detail to Chaos OpenApi interface. v3.162.0 \u00b6 http4k-testing-chaos : Add detail to Chaos OpenApi interface. v3.161.0 \u00b6 http4k-cloudnative : Added Forbidden request exception to HandleUpstreamRequestFailed. v3.160.1 \u00b6 http4k-testing-chaos : Countdown chaos trigger fixed. v3.160.0 \u00b6 http4k-testing-chaos : Slight fix to avoid consuming stream body when setting chaos. v3.159.0 \u00b6 http4k- * : Update various dependencies. http4k-client-okhttp : Updated OkHttp to v4.0.0 (Kotlin edition). http4k-contract : Tweak to JSON Schema rendering to handle recursive objects better. v3.158.1 \u00b6 http4k-server-netty : Fix #260 - cannot set multiple response headers with same name http4k-server-undertow : Fix #260 - cannot set multiple response headers with same name v3.158.0 \u00b6 http4k-contract : POSSIBLE BEHAVIOUR CHANGE DUE TO BUG: Fix #259 - Contract blocks do not produce 400s if an external CatchAll is provided. This may have an effect on how errors are generated (a 400 is produced instead of the previous 500 from the CatchAll). v3.157.1 \u00b6 http4k-security-oauth : Fix broken deprecation annotation. v3.157.0 \u00b6 http4k-security-oauth : Default to JSON format response in Access Token response http4k-security-oauth : Renamed a couple of classes (AccessTokenContainer -> AccessToken), and removed isValid method from AuthorizationCodes because it doesn't make sense for this to be on the OAuthServer. v3.156.0 \u00b6 http4k- * : Update Kotlin to 1.3.40 http4k-contract : Support OAuthSecurity renderer. v3.155.2 \u00b6 http4k- * : Update various dependencies. http4k- * : Dokka improvements. Does not mitigate #196 as we run the main build on OpenJdk11. H/T @ivoanjo v3.155.1 \u00b6 DO NOT USE - broken v3.155.0 \u00b6 DO NOT USE - broken v3.154.1 \u00b6 http4k-multipart : Made the multipart header parser case-insensitive. H/T @tenniscp25 v3.154.0 \u00b6 http4k-contract : Add SchemaModelNamer to allow for custom JSON Schema model names. v3.153.0 \u00b6 http4k-contract : OperationIds are generated without illegal characters {} . v3.152.0 \u00b6 http4k-contract : Support non-string keys for \"text convertible\" values in maps for Auto-schema generation. v3.151.0 \u00b6 http4k-contract : Fixed Auto-schema generation to detect and remove duplicate items from list schemas. v3.150.0 \u00b6 http4k-security-oauth : Make authentication mechanism for grant types configurable. v3.149.0 \u00b6 http4k-security-oauth : Initial support for client_credentials grant type. v3.148.0 \u00b6 http4k-contract : Jackson property searching in OpenApi3 now searches superclasses. v3.147.0 \u00b6 http4k-contract : Support custom JsonProperty annotation for OpenAPi3 generation http4k-cloudnative : New exception type for unuathorised. H/T @tom v3.146.0 \u00b6 http4k-contract : Fix #228 - Support Map-based fields in OpenApi 3 Auto-schema generation as additionalProperties . H/T @noahbetzen-wk for the idea. v3.145.0 \u00b6 http4k-contract : Reimplement Auto-schema generation using reflection. Added test cases to use the OpenApi generator to create valid code-based OpenApi clients using the OpenApi generator. http4k-format-jackson : Removed reflective JSON schema creator, since it was not actually OA3 compliant. v3.144.0 \u00b6 http4k- * : Update various dependencies. http4k-contract : Improvements to better adhere to OA3 spec. http4k-security-oauth : Allow injecting OpenID's request parameter into the authorization request. http4k-security-oauth : Expose request to AuthRequestTracking. v3.143.1 \u00b6 http4k-core : Replace RequestContexts with reference to Store . H/T @amcghie http4k-contract : Added some missing deprecations. http4k-contract : Fix #243 - Nulls not allowed in OpenApi V3 JSON models. v3.143.0 \u00b6 http4k-contract : Fix #239 - OpenApi v3 schemas for raw lists blow up when rendering. http4k- * : Update various dependencies. v3.142.0 \u00b6 http4k-contract : Both OpenApi v2 and v3 are now supported, including automatic schema generation. Some classes for OpenApi2 have moved to a new package - Deprecations should provide most alternatives. See module docs for details. For OpenApi v3, optionally include http4k-format-jackson to get JSON schema models based on JVM objects. http4k-format-jackson : Added reflective JSON schema creator, to be used for generating named models from JVM objects. v3.141.0 \u00b6 http4k-core : - Fix #233 - MemoryBody blows up with \"java.nio.ReadOnlyBufferException\" http4k-core : - Tighten up security on Basic and Bearer auth server filters. H/T @andymoody http4k-security-oauth : - Add filter to check bearer token is valid access token. H/T @andymoody v3.140.0 \u00b6 http4k- * : Update dependencies (including Kotlin bump to 1.3.31) http4k-security-oauth : Handle user rejecting/failing authentication. H/T @andymoody v3.139.0 \u00b6 http4k-security-oauth : Allow access token generation to explicitly reject an authorization code already used. H/T @andymoody v3.138.1 \u00b6 http4k-security-oauth : Amend error responses from access token generation. H/T @andymoody v3.138.0 \u00b6 http4k-contracts : Tweaks to Security model for http4k-contracts . (Renamed) ApiKeySecurity is now a proper class, and added BasicAuthSecurity . You can now also override the security model on a per-route basis. http4k-contract : Added ability to set the Security on each individual contract route. This overrides any Security set on a contract-level basis. v3.137.1 \u00b6 http4k-serverless : Allow invocation of serverless functions locally. H/T @Charlyzzz http4k-core : Fix #226 - ResourceLoadingHandler not close stream v3.137.0 \u00b6 http4k-security-oauth : Rename AuthRequestPersistence to AuthRequestTracking v3.136.0 \u00b6 http4k-security-oauth : Allow the http request to be referenced when generating OAuth authorization codes. H/T @andymoody v3.135.0 \u00b6 http4k-core : Change mime.types location so it doesn't conflic with other libraries. H/T @benusher and @dgliosca http4k-testing-chaos : Added SnipRequestBody behaviour. http4k-core : (Small) Breaking Fixed location of some extension files to be relevant to the particular package that they are referencing. This will require reimporting the new location into your source if you were using the imports. v3.134.0 \u00b6 http4k-testing-approval : Made content-type aware approval tests check the content type after the content. This is friendlier for failing tests, as it is more important that the content is correct than the content-type (and often errors don't have content type set so you get an erroneous error message which masks the fact that the content was wrong). v3.133.0 \u00b6 http4k-cloudnative : HandleUpstreamRequestFailed client filter now takes a predicate (Response) -> Boolean instead of a boolean. This allows for more fine grained custom control of which Responses are acceptable. http4k- * : Upgrade deps, including Kotlin to 1.3.30 . http4k-contract : Fix #221 - Contract path fixed segments cannot contain slash characters. v3.132.0 \u00b6 http4k-format-jackson : Convert Jackson to use readValue instead of convertValue . This fixes some problems with type conversions. v3.131.0 \u00b6 http4k-core : (Possible) Break: Made lense implementations Query, Header etc clear previous values by default instead of appending. This leads to a more consistent behaviour. In order to be able to set multiple values on an object using a lense, use the multi form instead - eg. Header.required(\"foo\") -> Header.multi.required(\"foo\") . We envisage the impact of this change is limited as it's only Queries that generally can have multiple possible values, and in the vast majority of cases a replace rather than append is expected. v3.130.0 \u00b6 http4k-contract : Generify contract handling code to allow for custom HttpMessageMeta v3.129.0 \u00b6 (Slight) Break: Collapsed UpstreamRequestFailed exceptions to contain the status, and thus removing non-special cases like BadRequest and BadGateway . This makes them much easier to use in practice as users have access to the the status. To migrate, simply replace previous classes with UpstreamRequestFailed(Status.XYZ, message) . http4k-contract : Open up ContractRoute API to facilitate extension when defining a custom ContractRenderer . http4k- * : Upgrade deps. v3.128.0 \u00b6 http4k-core : Added base64 to the supported mappings for Query/Headers etc... http4k-testing-approval : Approver does not write actual output if there is none to write and there is no approved content v3.127.0 \u00b6 http4k-testing-approval : Improved Approver interface to more closely match the traditional assert approach - this results in a more discoverable/obvious API. http4k-testing-hamkrest : Added ability to create a Hamkrest matcher directly from the Approver instance to be combined with other relevant matchers. v3.126.0 \u00b6 http4k-testing-approval : Add support for XML and HTML approval tests. v3.125.0 \u00b6 Added http4k-testing-approval module, which is compatible with JUnit5 tests and integrates with the OkeyDoke approval testing files and IntelliJ plugin. H/T to @jshiell for the inspiration Gist containing the base Junit5 Extension. v3.124.0 \u00b6 http4k-security-oauth : Make authentication response available when creating AuthorizationCode. v3.123.0 \u00b6 http4k-security-oauth : Introduce OAuthServer to http4k-security-oauth to assist in the creation of authorization servers. v3.122.0 \u00b6 Generified GenerateXmlDataClasses filter, and added default implementations for http4k-format-jackson-xml and http4k-format-xml modules. (Rename) Break: GenerateXmlDataClasses filter in http4k-format-xml is now GsonGenerateXmlDataClasses Removed superfluous CatchLensFailure filter from http4k-contracts module. This is not required as lens failures are already handled by the main contract handler. v3.121.0 \u00b6 Moved Jackson XML support to new module http4k-format-jackson-xml . Note that this is for auto-marshalling of data-classes only and does not expose an XML DOM model. v3.120.0 \u00b6 Deprecated Body.view() lens construction in favour of a Body.viewModel() call which removes the implicitly called toLens() . This allows further mapping from one ViewModel type to another, and brings the view lens construction into line with the rest of the extension functions on Body . Add auto-marshalling XML support to http4k-format-jackson module. Upgrade deps. v3.119.0 \u00b6 Add UpstreamRequestFailed exceptions and HandleUpstreamRequestFailed filters to http4k-cloudnative . These allow apps to neatly deal with upstream failure in a sensible way. v3.118.0 \u00b6 Tweak contract() DSL to add remaining options for configuration. v3.117.0 \u00b6 Renamed ChaosControls (deprecated) to ChaosEngine . v3.116.0 \u00b6 Added new templating module http4k-templates-freemarker . H/T @amcghie for the PR implementing this http4k-contract has a new DSL for construction of the contract which replaces the old one (now deprecated). This is consistent with the meta DSL used to construct individual contract routes and avoids repetition of the old API. We attempted to implement the standard replace-with deprecation, but IntelliJ didn't like it (too complex maybe), so we've hard coded the warning instead which code which should work. Added PreFlightExtraction to contract module, which adds the ability to disable body-checking for contract routes. This will allow refining of routes or entire contracts to be more efficient. Upgrade deps. v3.115.1 \u00b6 Fix #217 - Cannot override the definitionId of a top-level array in OpenAPI Upgrade deps v3.115.0 \u00b6 Chaos now do not blat x-uri-template when used with a RoutingHttpHandler Simplified usage of Once chaos trigger. (Slight break) Consistentified (!) construction of Chaos Behaviours, Stages and Triggers. Replaced singletons with function calls. Eg. Always -> Always() v3.114.0 \u00b6 (Possible Break): Fix #215 - LensFailure does not always include target object. Only change to the API is that IN generic in Lenses is now bounded by IN : Any . This fix is a actually internally consistent as we could not always include the target otherwise (which is an Any? ). Trim leading and trailing whitespace from extracted EnvironmentKey values. Secret value is now only usable once via the use() function. Upgrade to various deps. Removed deprecations. v3.113.0 \u00b6 Added some common types for Environmental setup, and equivalent BiDiLens mappings Handle null response in Java Http client. H/T @FredNordin v3.112.2 \u00b6 Fix #212 - allow null values in HTTP contract definitions. This does mean we lose the type definition for that field, but we don't blow up silently (which was the previous behaviour). H/T @xhanin v3.112.1 \u00b6 Re-add Path.nonEmptyString() which was accidentally removed. v3.112.0 \u00b6 Add support for prohibiting String unmarshalling in JSON auto-marshalling configuration. HTTP Contracts now use the underlying ContractRenderer to produce the BadRequest and NotFound responses. Made OpenAPI open so that these responses can be customised. v3.111.0 \u00b6 Add support for JSON views in Jackson module. H/T @xhanin for the donkey work. v3.110.0 \u00b6 Breaking: slight rearrangement of RouteMeta receiving/returning methods to provide consistency when defining route contracts. v3.109.0 \u00b6 Moved the set of predefined String BiDiMapping instances to their own class. Bulked out the auto-mapping configuration options. v3.108.0 \u00b6 Upgrade to various deps. Extracted out new BiDiMapping type, which encapsulates string <-> type conversions and removes a boatload of duplications. These conversions are now used consistently across all the various places (Lenses, auto-mapping). Improved configurability of AutoMarshallingJson instances. v3.107.0 \u00b6 Upgrade to various deps. Fix #208 - Xml auto deserialisation incorrectly converting strings to numbers v3.106.1 \u00b6 Fix #207 - repeating prefixes in static routes are not handled correctly. H/T @ruXlab for the PR to fix. v3.106.0 \u00b6 Add http4k-server-ktorcio server backend. Note that whilst this module does allow http4k apps to plug into the Ktor-CIO engine, it does not provide fully front-to-back coroutine support. v3.105.0 \u00b6 Preventing FallbackCacheControl from duplicating existing headers. H/T @leandronunes85 Breaking: Make Body.length nullable instead of throwing exception when value is not available. H/T @zvozin v3.104.0 \u00b6 Upgrade to various deps. Add session token support to AWS filter, and \"credentials provider\" to allow for rotating AWS sessions. H/T @dhobbs. Breaking: Moved WsClient from org.http4k.testing to org.http4k.websocket . v3.103.2 \u00b6 Fix access-control-allow-origin returned when server supports multiple origins H/T @johnnorris v3.103.1 \u00b6 (Properly) Fix #198 - Rewrote OpenApi contract to ensure it stays fixed. H/T @reik-wargaming for the help in tracking this down. v3.103.0 \u00b6 \"Fix\" #198 - Breaking change made in http4k-contracts to clarify/deconfuse API. Hid body parameter in contract route meta DSL - it is now receiving() . Upgraded some dependencies, including Gradle to v5.0. Breaking: Resilience4j dependency upgrade causes a break when providing custom config. Simply insert the Config type generic to fix: e.g. RetryConfig.custom() -> RetryConfig.custom() v3.102.1 \u00b6 Fix #197 - Swagger spec for form fields had incorrect description. v3.102.0 \u00b6 Introduce interface for Environment v3.101.0 \u00b6 Upgrades to dependencies Improved Client-side HTTP status descriptions Lenses now support Durations out of the box Environments now support multi-value keys (comma separated) v3.100.0 \u00b6 Make Undertow API friendlier Fix to JsonReadinessCheckResultRenderer to actually implement the correct interface v3.99.0 \u00b6 Enhancement of http4k-cloudnative - now supports extra-health check routes, and provide way to load app configuration via Properties files. v3.98.0 \u00b6 Add filter allowing Gzipping based on an allowed set of content types. H/T @jshiell Change HttpHandler extending HttpClients to use object invoke() mechanism, as the individual clients have no visible API surface of their own. Introduced DualSyncAsyncHttpHandler interface. v3.97.0 \u00b6 Webdriver checkbox handling improved. H/T @gypsydave5 upgrade to various versions v3.96.0 \u00b6 upgrade to Kotlin 1.3.0 v3.95.1 \u00b6 Tweak to K8S port variables. v3.95.0 \u00b6 (Unlikely break): Change Http4kServer interface to return Unit from stop() . This affects all server implementations. Added DSL function for working with JSON objects (scopes JSON as this ). fun Json.invoke(Json.() -> T) New module http4k-cloudnative contains classes to help run http4k services inside cloud-native environments, including K8S. Upgrade some dependencies Deprecation: Moved Header.Common fields to main Header object. Extension properties should go there now. v3.94.1 \u00b6 Use UTC when checking cookie expiry v3.94.0 \u00b6 Deprecate String.toBody() Fix checkbox behaviour in webdriver ~v3.39.4~ v3.93.4 \u00b6 Use Jetty latest release version (rather than RC one) v3.39.3 \u00b6 Fix #189 - Uri toString now omits leading slash if the authority of a Uri is blank. This could be a potential break, but is actually more consistent as a Uri can currently be relative or absolute. v3.39.2 \u00b6 Extend SetBaseUriFrom to support query parameters v3.39.1 \u00b6 Added SetBaseUriFrom filter v3.39.0 \u00b6 (Possible breaking change): Json is now only generified by a single type parameter instead of 2. For most usages, this type would have been identical anyway, but the upgrade of Argo has finally allowed the removal of this dead generic. Simply replace Json with Json . Added Offset datetime types to all JSON auto-marshalling libraries Build logic for versioning is now in Kotlin. H/T @jmfayard for the PR Upgrade Kotlin, and various other dependencies v3.38.1 \u00b6 Fix withChaosControls URL pattern so that it matches sub-routes ok on original handler v3.38.0 \u00b6 Added BearerAuth and BasicAuth implementations which populate RequestContexts . Plus howto example :) v3.37.1 \u00b6 Fix #177 - Make RequestContexts thread-safe. v3.37.0 \u00b6 Upgrades to http4k-testing-webdriver . H/T @dickon for the PRs Added ProxyHost request filter which is useful for writing proxy-type apps. v3.36.1 \u00b6 Fix #168 - Fix rest of hamkrest matchers caused by generics mishap. Upgrade HTTP client dependency versions. v3.36.0 \u00b6 Added http4k-testing-chaos module, designed to enhance failure-mode testing for http4k apps. Massive H/T to @IgorPerikov for the PR which drove this module's creation. Added http4k-incubator module, for hosting developing projects and other code which might be promoted to top-level modules in the future. v3.35.2 \u00b6 Fix #167 - Reintroduce hasBody compatibility with common matchers such as containsString() Remove deprecations. v3.35.1 \u00b6 Fix #165 - AWS auth filter does not replace headers - it sets them (which breaks for request signing) Fix #164 - Webdriver internal state breaks when navigating to a full URL Fix #162 - SetHostFrom doesn't set 'Host' header correctly (missing port). H/T @elifarley v3.35.0 \u00b6 Added some regex matchers to http4k-testing-hamkrest . Added BearerAuth authentication Server and Client Filters - these work similarly to BasicAuth . Added option for defaulted() lenses to fall back to another supplied lens in the case of missing value. Thanks to @dmcg for the inspiration. :) v3.34.3 \u00b6 Fix #160 - JavaHttpClient does not copy body stream correctly onto URL connection. v3.34.2 \u00b6 Fix #159 - Contracts should not have Security applied to the description route by default. v3.34.1 \u00b6 Fix #158 - Static and contract routes filters are applied in the wrong order. v3.34.0 \u00b6 Add default SamplingDecision param to ZipkinTraces - defaults to always sample. Fix #150 - StaticRoutingHandler filters being called twice. Fix #151 - POTENTIAL BREAK: Rework of Status objects to fix equality against the Status constant vals when a description has been overridden. This involves the following potential breaking change: The Status class is no longer a data class to tighten up encapsulation - user calls to copy() will have to be replaced. v3.33.2 \u00b6 Raise SO_BACKLOG in Apache and Netty server implementations. Add PERMANENT_REDIRECT and UNPROCESSABLE_ENTITY Status object. v3.33.1 \u00b6 No change from 3.33.0. Previous version couldn't be made available to maven central. v3.33.0 \u00b6 Add convenient way to extract from as a Map from http message. H/T to @dmcg (this version is available in jcenter only) v3.32.1 \u00b6 Fix #142 - Pebble templates don't load from JAR files. v3.32.0 \u00b6 Add support for propagation of the Zipkin x-b3-sampled header v3.31.0 \u00b6 Changes to the Netty factory to enable running http4k on GraalVM. H/T @RichyHBM v3.30.0 \u00b6 Allow all server implementations to start on port 0 (ie. find a free port) and then report it back as a part of the Http4kServer interface v3.29.0 \u00b6 Make HTTP clients resilient to unknown host and connection refused exceptions Implemented #134 - Added default (de)serialization for common JDK primitives to all Auto-marshalling JSON modules - eg. date times and UUIDs v3.28.0 \u00b6 Fix #131 - Uri's created with paths that don't contain leading slashes. Added etag parser filter. H/T @dgliosca for the PR Fix #132 - Ensured that disableDefaultTyping is called in default Jackson implementation. This should be the default anyway, but has been added to ensure that we don't fall foul of CVE-2017-7525 and to surface awareness of this issue. v3.27.0 \u00b6 OpenAPI now provides example values in the generated schema. H/T @skewwhiffy for the PR. v3.26.6 \u00b6 Fix #126 - ResourceLoadingHandler can expose mapped resources into the root. <-- We think this is an important update, so please upgrade! v3.26.5 \u00b6 Fix #125 - ApacheServer implementation now sets content length if present. v3.26.4 \u00b6 Fix #123 - Multipart Body objects blow up when parsed after being debugged. As with all streams, care should be taken to not blow heap when internalising them for debugging purposes. v3.26.3 \u00b6 Debugging filter now supports ignoring Multipart streams. v3.26.2 \u00b6 Tweak: OpenAPI now doesn't return null values in the schema. v3.26.1 \u00b6 Fix #124 - headers in WebSocket upgrade request are incorrectly joined. v3.26.0 \u00b6 Removed supportedContentTypes field from OpenApi contract JSON, since this is a legacy field. v3.25.0 \u00b6 Added option to Undertow to enable HTTP2 from main ServerConfig v3.24.0 \u00b6 Upgrade various dependencies for Java 10 compatibility. H/T @tom Fix bug with repeated params in Websocket upgrade request. H/T @tom v3.23.1 \u00b6 Composite LensFailures now capture (at least) the first failing cause (probably the body parameter in the case of an http4k-contract module. v3.23.0 \u00b6 Fix #116 - Can provide a custom Response creation method for CatchLensFailure . H/T @elifarley for the inspiration! v3.22.4 \u00b6 Added singleton method for Json.array, since if you pass in a single JsonNode (Jackson), it accidentally iterates over the fields in the node instead of using the object as an entry in the array. Fix #115 - Only add content-length for methods that allow content in AwsAuth filter v3.22.3 \u00b6 Preserve routing information on request/response manipulation v3.22.2 \u00b6 http4k-security-oauth module added - with support for OAuth2 Authorization Grant flow Replaced classes reliant on javax.activation package, which allows Java 9+ to not require any external dependencies. \\o/ Fix #112 - ApacheClient incorrectly sets headers on GET requests (this breaks F5 load balancers). H/T @simojenki PR #110 - Websocket client timeouts are incorrectly translated as seconds instead of millis. HT @anorth Core JavaHttpClient does not support streaming due to limitations with HttpURLConnection v3.21.1 \u00b6 Fix #109 - Jackson treats integer values inconsistently, leading to matching errors when using hamkrest. v3.21.0 \u00b6 Fix #107 - Killed the x-uri-template header and fixed the ReportHttpTransaction to have access to the routingGroup . Altered ordering of filters in http4k-contract so that the route is identified before pre-filters and security are applied. This allows knowledge of the path to be accessible at the time of application of those filters. v3.20.0 \u00b6 Introduce JavaHttpClient to http4k-core . It provides a very basic http client without any other 3rd party dependencies. v3.19.0 \u00b6 PR #104 - Add optional time/date formatters to LensSpecs so you can choose you serialisation format. H/T @elifarley Fix #105 - Swagger API json file: duplicate key in \"definitions\". v3.18.1 \u00b6 Fixed PR #100 - URI template regex required extra escaping. This only affects Android deployments as IDE shows the regex escaping is redundant. H/T @privatwolke v3.18.0 \u00b6 Breaking: converted contract pre-security filter to be a post-security filter. This means that all standard filters are applied before the security later, which allows for logging and monitoring and context setup. The previous filter mechanic applied security first, which didn't allow for this. In the unlikely event that post-security filters still need to be applied, use the withPostSecurityFilter() function when building the contract. Docs for contract RouteMeta function parameters, and deprecated some unused functions (missed when we introduced the DSL). PR #99 - Contract routes now support up to 10 path segments. Thanks to @scap1784 for the PR! :) v3.17.1 \u00b6 Fix #97. Moshi does not fail when deserialise non-nullable fields correctly. Note that GSON still suffers from this problem v3.17.0 \u00b6 Added a pre-security filter option to contract creation, so that you can explicitly specify behaviour to occur before security kicks in. v3.16.0 \u00b6 Convert Security (from sealed class) and ApiKey to be interfaces. This allows users to implement their own security models. v3.15.0 \u00b6 Introduce HttpTransaction and new ReportHttpTransaction filter provide better generic API for reporting, along with the ability to label transactions for this purpose. Breaking: Rework the metrics request counter and timer Filter API. There is now a HttpTransactionLabeller for you to add as many labels as required to the transaction. Each of these labels will be used to tag the metric. v3.14.1 \u00b6 Fix #95 - Filters are now applied to \"route not found\" responses v3.14.0 \u00b6 Fix #93 - Apache server doesn't like content-length or transfer-encoding headers present in http4k response. Add ability to \"name\" input and output contract body definitions in an OpenAPI JSON doc. This applies to only the top level entity. If no override is passed, the objects are named according to their hashcode. 3.13.4 \u00b6 Fix #92 - cookie date should always use US locale v3.13.3 \u00b6 Further tweak to Netty. H/T @FredDeschenes v3.13.2 \u00b6 Fix #91 - large message handli ng in Netty v3.13.1 \u00b6 Upgrade to Kotlin 1.2.20 v3.13.0 \u00b6 Support for operationId in OpenApi route metadata. H/T @danschultz for the PR. Removed previously deprecated methods. v3.12.0 \u00b6 New client module http4k-client-jetty , which supports both sync and async models. v3.11.1 \u00b6 Fix #84. OPTIONS requests are not detected by contract routes. Added option to NOT authorise OPTIONS requests in ApiKey security filter. Added support for Async HTTP clients and added new AsyncHttpClient interface, which is obviously used for HTTP clients only**, and not server-side calls. :) New client module http4k-client-apache-async . New metrics gathering module http4k-metrics-micrometer . Big H/T to @kirderf for the PR. Added support for async to OkHttp client module. v3.10.0 \u00b6 P/R 81 - adding headers and timeout to websocket client. v3.9.0 \u00b6 Added compactify and prettify to Json implementations Added Json.hasBody Hamkrest matchers for comparing bodies. Note these are extension methods and need to be referenced/imported as such. v3.8.0 \u00b6 Added facility for non-blocking websocket client to react to onConnect event. This API is the same as the inbound, server-side API - ie. there are no explicit connection event handlers. H/T @tom for the idea. v3.7.0 \u00b6 P/R #13 Create extension methods for Response to add caching headers. H/T @k0zakinio. v3.6.1 \u00b6 Fix #78. Serialisation of raw lists using Moshi fails in the same way as the Jackson auto-conversions do. Added convenience methods to get around this. v3.6.0 \u00b6 Added http4k-format-moshi to support the Square auto-marshalling library. v3.5.1 \u00b6 Fix #76 - encoding of path segments to use URI encoding instead of URL form encoding. v3.5.0 \u00b6 Added support for multiple HotReload template directories in HandlebarsTemplates . H/T @TomShacham Fix #74 - Request tracing span/parentSpan set too early so was shared between outgoing requests. v3.4.0 \u00b6 New server backend http4k-server-apache . H/T @kirderf for the PR :) We now set the length of the incoming request body when it is available in the incoming request. v3.3.1 \u00b6 Handlebars now uses combination of Class and Template name to cache templates. v3.3.0 \u00b6 Facility to compose TemplateRenderers with then() to provide fallback behaviour. v3.2.3 \u00b6 PR #70: Header order equality for Request/Response - H/T @gypsydave5. v3.2.2 \u00b6 Switched out Status for WsStatus (with proper RFC code set) in Websockets. v3.2.1 \u00b6 Typesafe Websockets! Jetty now supports websockets, using the same style of API in the main http4k routing. (Possible) Breaking change: Because WsHandler (typealias) implements the same inbound interface as HttpHandler , you now cannot declare HttpHandlers without specifying the input type, so any \"anonymous\" handlers will not compile as a result. The required fix is very simple, but manual: `{ Response(OK) } should become { _:Request -> Response(OK) } v3.1.3 \u00b6 Fix Request.form() for streaming requests v3.1.2 \u00b6 Remove possibility of empty message for Path Lens failure. v3.1.1 \u00b6 New (better!) API for http4k-contract module. Old meta DSL has been deprecated. v3.0.1 \u00b6 Fix #63 - Apache Client Connect. timeout exception handling. v3.0.0 \u00b6 Added http4k-serverless-lambda module, allowing http4k applications to be deployed into AWS Lambda and then called from API Gateway. Effectively, the combination of these two services become just another Server back-end supported by the library. \\o/ v2.38.1 \u00b6 RequestContextKey now follow the standardised Lens structure of required, optional, defaulted, and can now be removed (set to null). Replace calls to RequestContextKey.of() with RequestContextKey.required() Removed previously deprecated values. See below for details on replacements. v2.37.0 \u00b6 Added http4k-resilience4j module, which adds Circuits, RateLimiters, Retrying and Bulkheading. Fix #60 (H/T @michaelhixson for the spot). v2.36.0 \u00b6 Added a couple of useful ServerFilters . Upgrade various dependency versions. Tidying of Multipart code. v2.35.1 \u00b6 Fix #57. Static handlers behave oddly when combined with an HTTP verb in the routing tree. v2.35.0 \u00b6 Fix #56. Altered behaviour of CatchLensFailure to NOT catch errors from unmarshalling Response objects. This was causing BAD_REQUEST to be incorrectly generated. Simplification of generics around LensSpecs. This should not be a breaking change, (there were 3 generics, now the MID has been removed so there are just 2) but could break if signatures are used explicitly. v2.34.0 \u00b6 Reordered generics in LensInjector to make sense. This should have no effect on most code-bases, but could break if signatures are used explicitly. Just flip the generic types to switch. v2.33.1 \u00b6 Added support for unsigned AWS requests, which enables streaming content to S3. v2.33.0 \u00b6 Added BodyMode.Request to configure streaming for clients. ResponseBodyMode is now BodyMode.Response (Breaking change. Fixable with simple find/replace). v2.32.0 \u00b6 Added ServerFilter.ProcessFiles filter to stream Multipart Files, convert them into references and replace inline in the Form. v2.31.4 \u00b6 Avoid realising StreamBody unless necessary, which could break common usages of streaming. v2.31.3 \u00b6 Tweaks to Server backends to improve efficiency. v2.31.2 \u00b6 Webdriver will keep only the final URI after redirects. v2.31.1 \u00b6 Increased granularity of Replay.DiskStream and ensure that traffic is returned in exact order on all OSes. Add support for redirects to Webdriver. v2.31.0 \u00b6 Multipart module tweaked to provide a more consistent API. Fix FollowRedirects for POST/PUT request. v2.30.0 \u00b6 Multipart form support through new module http4k-multipart . Deprecation: Replaced Swagger with OpenApi and deprecated the former (via typealias). Deprecation: Replaced FormValidator with Validator and deprecated the former (via typealias). v2.29.4 \u00b6 Refactor release. v2.29.3 \u00b6 Fix #50 - Webdriver does not normalise relative links correctly. v2.29.2 \u00b6 Http client modules now catch and convert Socket Timeout exceptions to HTTP 504s (with a custom message) v2.29.1 \u00b6 Tweaks to how recorded traffic is stored on disk. Thanks to @dkandalov for the PR around this. v2.29.0 \u00b6 Added TrafficFilters for recording and replaying HTTP traffic. See org.http4k.traffic package for details. v2.28.0 \u00b6 Added http4k-template-dust for Dust template engine support. Thanks to @npryce for the PR to add this. v2.27.2 \u00b6 Fix #44 - Use quotes around cookie values v2.27.1 \u00b6 Raise proper Exception (instead of LensFailure) when RequestContexts are not set up correctly, so we don't accidentally classify developer errors as BadRequests v2.27.0 \u00b6 Added facility to assign values into a RequestContext which is passed down the Filter chain. v2.26.3 \u00b6 Fix #44 - Request cookies should not be wrapped in quotes. v2.26.2 \u00b6 Fix #43 - AWS does not sign binary requests correctly. v2.26.1 \u00b6 Fix #41 - Sending binary body alters the size of the payload. v2.26.0 \u00b6 Added \"catch all\" routing option, which matches all methods to a handler. v2.25.4 \u00b6 Fix #40 - GZip filters now use content-encoding headers instead of transfer-encoding. v2.25.3 \u00b6 Fix #39 - ResponseBodyMode.Memory properly closes streams (breaks jetty + gzip). v2.25.2 \u00b6 Ensure that streams are closed properly when consuming from an upstream client. v2.25.1 \u00b6 Remove Apache client request streaming because it may not release connections properly. v2.25.0 \u00b6 Add streaming support to HTTP Server and Client modules. Remove CatchLensFailure ClientFilter as it will never be used. v2.24.0 \u00b6 Added CatchLensFailure for ClientFilters - which catches un-deserializable invalid responses from clients and generates a BAD_GATEWAY error. v2.23.4 \u00b6 Switch XML generation to Gson over Jackson because Jackson doesn't handle uppercase field names well. Switch native XML parsed type to Document over Node. v2.23.3 \u00b6 New algorithm for XML data class deserialisation, so un-deprecated XML methods. v2.23.2 \u00b6 Deprecated methods in XML support due to limitation with underlying Jackson implementation. v2.23.1 \u00b6 Fixed bug with GenerateXmlDataClasses filter v2.23.0 \u00b6 Renamed http4k-format-jackson-xml module to http4k-format-xml . Improved XML unmarshalling support. v2.22.1 \u00b6 Fixed 36: Form entry is too strict with content encoding. v2.22.0 \u00b6 Added http4k-format-jackson-xml module, with XML parsing support. Upgrade several dependencies v2.21.2 \u00b6 Fixed Hamkrest matchers to be on HttpMessage and not Http Request. v2.21.1 \u00b6 Default body Content Negotiation strategy changed to None v2.21.0 \u00b6 Converted Content-Negotiation strategy from an Enum to an interface, so that users can define their own strategies. We also now check encoding so there are 4-built in strategies to choose from: Strict, StrictNoDirective, NonStrict and None. v2.20.1 \u00b6 Fixed #31 - Matching of segments in URIs is done after URLs are decoded, which results in not capturing encoded slashes in the path segments. v2.20.0 \u00b6 Fixed #30 - CachingClasspath template ResourceLoader not working with non-root packages. v2.19.0 \u00b6 Fixed #29 - webdriver submission of text area. Http clients now use a new instance of the default for each instantiation. Previously there was a shared instance. Add regex body type for parsing values out of bodies, and \"None\" option for content negotiation. v2.18.3 \u00b6 Fix AWS request signing for requests containing empty path v2.18.2 \u00b6 Fix AWS request signing for requests containing path with special characters v2.18.1 \u00b6 Added support for newRequest() in new RouteBinder mechanic. v2.18.0 \u00b6 Add support for unlimited nesting for routes() blocks. Removed the raw Route object, which can be replaced with Router or RoutingHttpHandler where appropriate. As part of above, rejigged route setup logic. Deprecated old routing structure, so now \"/path\" to GET bind is \"/path\" bind GET to . To fix deprecation, simply switch the calls to \"to\" and \"bind\" in routing setup. Rename of bind() in http4k-contract to be bindContract() v2.17.2 \u00b6 Added missing eclectic HTTP method. :) v2.17.1 \u00b6 Added GZip filters to http4k-core to zip request and response bodies. v2.16.1 \u00b6 Improved messages for http4k-testing-hamkrest matchers. v2.16.0 \u00b6 Added http4k-testing-hamkrest which contains a set of Hamkrest matchers for Http4k objects. v2.15.0 \u00b6 More features for http4k-testing-webdriver . Cookie support added. v2.14.0 \u00b6 More features for http4k-testing-webdriver . We now support Form entry and submission. v2.13.0 \u00b6 More features for http4k-testing-webdriver . v2.12.0 \u00b6 Added http4k-testing-webdriver module, an ultralight Selenium WebDriver for http4k apps v2.11.3 \u00b6 Fix #26 - GenerateDataClasses does not recurse into nested object trees v2.11.2 \u00b6 Fix filter application on GroupRoutingHttpHandler to apply the filter when it is applied with then(RoutingHttpHandler() v2.11.1 \u00b6 Fix static routes not defaulting to index.html when in root context v2.11.0 \u00b6 Added SunHttp server implementation (for development use only) v2.10.1 \u00b6 Fix cookie parsing when value contains '=' v2.10.0 \u00b6 Add method to set form values in the request v2.9.0 \u00b6 Added PURGE HTTP method as it's used commonly by various caches. v2.8.1 \u00b6 Repackage AWS classes for consistency with rest of project v2.7.1 \u00b6 Alter AWS Auth filter creation. Now use ClientFilters.AwsAuth v2.7.0 \u00b6 Add AWS module v2.6.0 \u00b6 Newly created Zipkin traces are now populated onto incoming request in ServerFilters. v2.5.1 \u00b6 Slight tweak to GSON auto-marshalling to allow for use of raw Arrays with auto-marshalling v2.5.0 \u00b6 Add Thymeleaf templating support v2.4.0 \u00b6 Add Pebble templating support v2.3.0 \u00b6 Make Route a Router so we can nest them together. v2.2.1 \u00b6 Remove excess \"charset\" from headers in Undertow. v2.2.0 \u00b6 Rename by() to bind() in routing for clarity. v2.1.2 \u00b6 Fix for #24 - UriTemplate captures query parameters when the trailing path parameter is a regex. v2.1.0 \u00b6 Added GSON full-auto functions to convert arbitrary objects to/from JSON. v2.0.5 \u00b6 Fix #23. Contract now supports multi-part URL params (for hardcoded parts) v2.0.4 \u00b6 Fix #22. Uri template does not parse out correct path params when URL starts with a path part. v2.0.3 \u00b6 toString() implementations to aid debugging v2.0.1 \u00b6 Readded missing default parameter for newRequest() on RouteSpec v2.0.0 \u00b6 Breaking: Inversion of routing API. GET to \"/someUri\" is now \"/someUri\" to GET for consistency across the entire API. v1.33.1 \u00b6 Reimplementation of http4k-contract API to match main routing API. Contracts are now nestable. v1.32.2 \u00b6 Fix Filters being applied twice in ContractRoutingHttpHandler v1.32.1 \u00b6 More work on http4k-contract contract API v1.31.0 \u00b6 Rework http4k-contract routing to be mounted in the same way as other RoutingHttpHandlers v1.30.0 \u00b6 Filters are now applied consistently to all Routers v1.29.0 \u00b6 Tweak to DSL for defining StaticRouters v1.28.1 \u00b6 Fix for #18: FollowRedirect will now work if location header includes charset information. v1.28.0 \u00b6 New DSL for defining StaticRouters v1.27.0 \u00b6 Merged StaticContent and StaticRouter and repackage of contract API into other packages v1.26.2 \u00b6 Extend fix for #17 to request Cookie header. v1.26.1 \u00b6 Fix for #17. Cookie can now parse a cookie without attributes and ending in semicolon. v1.26.0 \u00b6 Added nestable Routers. Merging of Modules and Routers. Router is the new Module ! RouteModule is now ContractRouter , so rename in code will be required. v1.25.1 \u00b6 Fix for #15. OkHttp client handling of POSTs with no body. v1.25.0 \u00b6 Can add custom mime types to Static Content GenerateDataClasses is capable of more complex object graphs v1.24.0 \u00b6 Remove HttpHandler.asServer in favour of HttpHandler.startServer to avoid confusion. Introduce Status.description() . v1.23.0 \u00b6 Netty sets content-length header. v1.22.2 \u00b6 Fix for #12. Undertow not constructing response correctly. v1.22.0 \u00b6 New module with Undertow.io support http4k-server-undertow Jackson implementation now ignores unknown properties in incoming messages Netty implementation tidied up v1.21.1 \u00b6 Fix for #11. Netty implementation returns incorrect status codes. v1.21.0 \u00b6 Add synonym methods for Lenses to aid readability. We now have invoke(IN)/extract(IN) and `invoke(IN, TARGET) /inject(IN, TARGET) v1.20.0 \u00b6 http4k-contracts : Add option to change the route of the module description route v1.19.1 \u00b6 http4k-contracts : Fix for contract module description routes not being authenticated via security filter v1.19.0 \u00b6 http4k-contracts : Add Swagger module rendering with JSON schema models for messages. v1.18.0 \u00b6 Add nonEmptyString() lens type to all request parts. v1.17.0 \u00b6 General rework v1.16.0 \u00b6 Further work on Path Lenses. They are now fully supported and consistent for both simple and contract routing scenarios. v1.15.0 \u00b6 Path lenses are now bidirectional, so can be used to populate requests as well as bodies an headers etc. Routes can now create shell Requests for themselves, using route.newRequest() v1.14.0 \u00b6 Body is now non-nullable (use Body.EMPTY instead) Rename methods BodyLens API for consistency and clarity. required() is now toLens() . to()' binding method is now of(). v1.13.0 \u00b6 New client module: http4k-client-okhttp v1.12.0 \u00b6 Tidying v1.11.0 \u00b6 Added option for Body content-negotiation to be strict or non-strict (the default). Always be strict in what you send, relaxed in what you will accept. :) v1.10.0 \u00b6 Moved Credentials to org.http4k.core package. Add various filters, including SetHostFrom and CatchAll . v1.9.0 \u00b6 Added GenerateDataClasses so you can generate Kotlin data classes from JSON messages. v1.8.0 \u00b6 Added CORs support v1.7.0 \u00b6 Added auto() to Jackson, so you can auto convert body objects into and out of Requests/Responses v1.6.0 \u00b6 Added CachingFilters v1.5.0 \u00b6 Removed static factory methods for Request/Response. They were confusing/incomplete and users can easily recreate them via extension functions. Merge org.http4k.core.Body and org.http4k.lens.Body . Add Request/Response message parsers. v1.4.0 \u00b6 Turn Body into ByteBuffer wrapper rather than typealias. That should make .toString() behave as most people would expected. v1.3.0 \u00b6 Removed non-mandatory parameters from Request and Response constructors. This is aid API clarity. and force users to use the API methods for properly constructing the objects. Regex Lens added. v1.0.0 \u00b6 Initial major release.","title":"Changelog"},{"location":"changelog/#v52800","text":"http4k- * : Upgrade some dependency versions. http4k-core : Include Vary header on CORS responses. H/T @ollieabbey http4k-multipart : [Fix Break] Multipart form files were all calling deleteOnExit() instead of being deleted when the Body is closed. Possible memory leak for long running processes. The fix MAY be a change of OS files-system usage if you are not closing your MultiPart form body. #H/T @oharaandrew314 for the report.","title":"v5.28.0.0"},{"location":"changelog/#v52700","text":"http4k- * : Upgrade some dependency versions including Kotlin to 2.0.10 http4k-core New HTTP status codes. H/T @torfinnberset http4k-core Added helper method for dealing with forms. H/T @tim-mortimer http4k-core [Fix] Close backing DiskLocation when MultipartForm closed. H/T @oharaandrew314 http4k-testing-kotest Fix haveSetCookie and haveCookie to work when cookie isn't present. H/T @bagguley","title":"v5.27.0.0"},{"location":"changelog/#v52610","text":"http4k- * : Upgrade some dependency versions. http4k- * : Fix transformer is lost when adding name suffix to Approver H/T @ilya.aliaksandrovich","title":"v5.26.1.0"},{"location":"changelog/#v52600","text":"http4k- * : Upgrade some dependency versions. http4k-core : Response caching extensions. H/T @ollieabbey http4k-config : [New Module!] Extraction of typesafe configuration module from http4k-cloudnative. http4k-cloudnative : [Breaking!] Repackaging of typesafe configuration module classes (org.http4k.cloudnative.env) to http4k-config (org.http4k.config). New imports are required. http4k-contract : Adds ApiKeySecurity that identifies a consumer and makes it available for later use. H/T @dhs3000","title":"v5.26.0.0"},{"location":"changelog/#v52500","text":"http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Breaking Fix] Incorrect lambda request context variable is passed - we now pass the incoming reqeust object instead of the converted http4k request. If you were using the LAMBDA_REQUEST_KEY, you can just use the request passed into the handler instead.","title":"v5.25.0.0"},{"location":"changelog/#v52410","text":"http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Fix] AWS adapter throws on invalid URLs. http4k-testing-webdriver : [Fix] Base path replacement logic for same-dir-path and dot-path URLs. H/T jweidler","title":"v5.24.1.0"},{"location":"changelog/#v52400","text":"http4k- * : Upgrade some dependency versions. http4k-core : Allow removal of all queries for a URI. H/T @dhs3000 http4k-format-kondor : Upgrade to new version of Kondor. H/T @uberto http4k-testing-strikt [Break] The upgrade to the latest version drops Java <17 support. If you are still using Java 8, you will need to stick with the previous version of this module.","title":"v5.24.0.0"},{"location":"changelog/#v52300","text":"http4k- * : Upgrade some dependency versions. http4k-contract : [Approval test break] Addition of \"nullable\" field to every model property. This improves JSON output compatability with various tooling for generating types from the definitions.","title":"v5.23.0.0"},{"location":"changelog/#v52200","text":"http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [Break] Move classes to alternative package to not clash with existing format objects.","title":"v5.22.0.0"},{"location":"changelog/#v52120","text":"http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [New module] Support for KotlinX DataFrame.","title":"v5.21.2.0"},{"location":"changelog/#v52110","text":"http4k- * : Upgrade some dependency versions. http4k-multipart * : [Fix #1113] Disk-backed multipart form field data is now cleaned up when the body is closed, including the parent form directory.","title":"v5.21.1.0"},{"location":"changelog/#v52100","text":"http4k- * : Upgrade some dependency versions including Kotlin to v2 http4k-testing-chaos : [Breaking] Changed Trigger to be a fun interface instead of a typealias. Should be no-op or a simple fix to the type. http4k-core : [Possible Break] Renamed CachingFilters.Request/Response to CachingFilters.CacheRequest/CacheResponse . If you have imports then they may break and need to be updated.","title":"v5.21.0.0"},{"location":"changelog/#v52000","text":"http4k- * : Upgrade some dependency versions. http4k-testing-chaos * : [Unlikely break]: remove Hamkrest dependency so that it does not appear randomly in your projects. If you were accidentally relying on this it will need to be re-added manually.","title":"v5.20.0.0"},{"location":"changelog/#v51900","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.24 http4k-core : Add support for the timesource typealias () -> Instant where a Clock is used. H/T @kwydler","title":"v5.19.0.0"},{"location":"changelog/#v51820","text":"http4k-core : Add convenience methods to read bodies from HttpMessages as JSON/XML/CSV etc.. request.json()","title":"v5.18.2.0"},{"location":"changelog/#v51810","text":"http4k-core : Add convenience methods to set common headers to HTTP message.","title":"v5.18.1.0"},{"location":"changelog/#v51800","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Behaviour break] We now do not set the legacy Expires header in CachingFilters. Modern caches should use the Cache-Control header instead (max-age).","title":"v5.18.0.0"},{"location":"changelog/#v51700","text":"http4k- * : Tweaks to make the K2 compiler happy http4k : Added convenience methods to set the body of an HTTP message. The works for both standard body types and with automarshallers. http4k-core : Fix request source in SunHttp. H/T @dkandalov http4k-contract : Added top-level MetadataRetrieval to schema objects. H/T @BBB http4k-format- * : [Unlikely break] rename with() functions on auto-marshallers to match content type, so you can now do req.json(myObj) and get the content type and body set in one go. Likewise for other content types","title":"v5.17.0.0"},{"location":"changelog/#v51620","text":"http4k- * : Upgrade some dependency versions.","title":"v5.16.2.0"},{"location":"changelog/#v51610","text":"http4k- * : Upgrade some dependency versions. http4k-core : Add support for surrogate-key headers in EtagSupport. H/T @jason-annadani-springer","title":"v5.16.1.0"},{"location":"changelog/#v51600","text":"http4k- * : Upgrade some dependency versions. http4k-testing-approval : [Unlikely break] Rename typo in an ApprovalSource instance http4k-testing-approval : Addition of optional suffix to the approval file name, and added ApprovalTransformer for varying the compared content from the InputStream http4k-core : [Fix #1084] Route name without a beginning / works for everything except static resources. H/T @ArthurS1","title":"v5.16.0.0"},{"location":"changelog/#v51510","text":"http4k-core : [Unlikely break] Change to Meta to remove default params http4k-testing-approval : Add ability to add a suffix to the approval file name.","title":"v5.15.1.0"},{"location":"changelog/#v51500","text":"http4k-core : [Unlikely break] Change to Meta to remove default params","title":"v5.15.0.0"},{"location":"changelog/#v51450","text":"http4k- * : Upgrade some dependency versions. http4k-core : Make Lenses support metadata passed through the LensBuilder construction methods. H/T @BBB, @ivanmoore @jack-bolles http4k-testing-tracerbullet : Account for spans across traces with same spanId. H/T @IvanPavlov1995","title":"v5.14.5.0"},{"location":"changelog/#v51440","text":"http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov","title":"v5.14.4.0"},{"location":"changelog/#v51420","text":"http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov","title":"v5.14.2.0"},{"location":"changelog/#v51410","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Validator optimisation. H/T @dkandalov http4k-testing-webdriver : Adding a space between method name and URI when naming spans H/T @ReinholdsB http4k-testing-webdriver : Multipart forms in the webdriver, including sending files. H/T @gypsydave5 http4k-testing-webdriver : Fix bug in webdriver form submission + a method for relative Uri resolution. H/T @gypsydave5","title":"v5.14.1.0"},{"location":"changelog/#v51400","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.23 http4k- * : Static handlers serve an index.html file from a subdirectory. H/T @mbcltd","title":"v5.14.0.0"},{"location":"changelog/#v51390","text":"http4k- * : Upgrade some dependency versions http4k-contract : Implement Kondor Schema creator. H/T @tamj0rd2 http4k-cloudnative : Read environment properties from yaml resources. H/T @dzappold http4k-webdriver : [Fix] Bug when submitting with inputs of type submit. H/T @gypsydave5 http4k-testing-approval : Allow adding a suffix to an approval test file name. H/T @becky-sequence","title":"v5.13.9.0"},{"location":"changelog/#v51380","text":"http4k- * : Upgrade some dependency versions http4k-format-kondor : Expose converterFor method. H/T @tamj0rd2","title":"v5.13.8.0"},{"location":"changelog/#v51370","text":"http4k- * : Upgrade some dependency versions http4k-contract : Allow user to provide schema creation implementation. H/T @tamj0rd2 http4k-core : [Fix #1053]: Add BiDiLensSpec defaulted with factory method http4k-core : [Fix #1059]: Update kondor-json to 2.2.2. H/T @asadmanji","title":"v5.13.7.0"},{"location":"changelog/#v51361","text":"http4k-core : FollowRedirects also sets port on redirect.","title":"v5.13.6.1"},{"location":"changelog/#v51360","text":"http4k- * : Upgrade some dependency versions http4k-serverless-lambda :[Fix #1057] Error when parsing AWS lambda event from S3 bucket http4k-testing-webdriver :[Fix #1050] Http4kWebDriver does not work on Windows due to path issues. H/T @cmh-dev http4k-core :[Fix #1055] Host header should contain host with port. H/T @obecker","title":"v5.13.6.0"},{"location":"changelog/#v51350","text":"http4k-client-core : Ensure consistent content-length behaviour across clients http4k-client-apache : Ensure consistent content-length behaviour across clients http4k-client-apache4 : Ensure consistent content-length behaviour across clients http4k-client-fuel : Ensure consistent content-length behaviour across clients http4k-client-jetty : Ensure consistent content-length behaviour across clients","title":"v5.13.5.0"},{"location":"changelog/#v51341","text":"http4k- * : Fix broken POM dependencies.","title":"v5.13.4.1"},{"location":"changelog/#v51340","text":"http4k- * : Upgrade some dependency versions http4k-contract : Support for data4k progressive data models with field metadata via delegate properties","title":"v5.13.4.0"},{"location":"changelog/#v51330","text":"http4k- * : Upgrade some dependency versions http4k-cloudnative * : Ability to override separator in Environment .","title":"v5.13.3.0"},{"location":"changelog/#v51320","text":"http4k- * : Upgrade some dependency versions http4k-contract * : [Fix] Enums do not pick up custom prefixes in model naming. H/T @ashcor for the tip-off! http4k-opentelemetry * : [Fix] Fix to set HTTP_REQUEST_BODY_SIZE attribute in OpenTelemetryTracing. H/T @dkandalov http4k-contract * : Added Canonical model-namer.","title":"v5.13.2.0"},{"location":"changelog/#v51310","text":"http4k- * : Upgrade some dependency versions http4k-client-helidon : [Fix #1037] Improve support for query parameters. H/T @franckrasolo","title":"v5.13.1.0"},{"location":"changelog/#v51301","text":"http4k-testing-tracerbullet : [Fix] Mermaid sequence diagram generation was constantly changing by default editorconfig files and people committing with different IDE settings http4k-server-jetty : [Fix #1023] Header values in quotes lose their quotes. H/T @efasel, @dhs3000","title":"v5.13.0.1"},{"location":"changelog/#v51300","text":"http4k-format-jade4j : [Breaking] This module has been renamed due to the library Jade4J becoming Pug4J. Migration should be a no-op apart from switching the imported module, and renaming your templates from .jade to .pug. Please see Pug4j docs for anything else. http4k-format-pug4j : [New module] Replacement for Jade4j","title":"v5.13.0.0"},{"location":"changelog/#v51221","text":"http4k-webhooks : Move VerifyWebhookSignature filter to ServerFilters as it's not for HTTP clients!","title":"v5.12.2.1"},{"location":"changelog/#v51220","text":"http4k- * : Upgrade some dependency versions http4k-core : [New module!] Support for the Standard Webhooks format http4k-core [Fix #1022] For a request with matching if-none-match header the response lacks the etag header. H/T @efasel http4k-core [Fix #1030] Maven POM for http4k-format-jackson-xml is invalid: jackson-dataformat-xml is missing a version","title":"v5.12.2.0"},{"location":"changelog/#v51210","text":"http4k- * : Upgrade some dependency versions http4k-core : Fix lens replacement of path parameter when there is a regular expression in the path segment http4k-format-jackson : Added lens support for deserialising data4k containers directly from HTTP message bodies (via Body.json(::JsonNodeDataContainer)).toLens()","title":"v5.12.1.0"},{"location":"changelog/#v51200","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.22, and Jetty 12 (see below). http4k-server-jetty - [Breaking] Upgrade to Jetty 12. This is a major rewrite of the Jetty engine and the API surface is incompatible with v11. If you are using vanilla Jetty backend then this is a NoOp replacement, otherwise fallback to using the new Jetty11 module and then plan migration accordingly. Massive H/T to H/T @FredNordin for the implementation upgrade. http4k-server-jetty11 - [New Module!] Drop-in replacement module for custom Jetty11 users. Constructor is now called Jetty11() instead of Jetty() , so migration should be very simple. Other renames as required (using 11 ) to avoid API clashes in the http4k codebase. http4k-aws : [Breaking] Tweaks to the signature of AwsPreSignRequests . Use AwsRequestPreSigner instead. H/T @oharaandrew314","title":"v5.12.0.0"},{"location":"changelog/#v51110","text":"http4k-aws : Pre-sign AWS requests with the new AwsPreSignRequests class. H/T @oharaandrew314 http4k-serverless-lambda : [Fix #1013] Support multi value query parameters in ApiGatewayV2LambdaFunction ( http4k-serverless/lambda)","title":"v5.11.1.0"},{"location":"changelog/#v51100","text":"http4k- * : Upgrade some dependency versions http4k-core : [Unlikely Break: Fix #1011] Jackson does not honour serialisation of Enums when they are used as Map keys. The fix MAY break JSON serialisation (which actually is a bug as the expected behaviour is for the Enums to use the predefined mapping).","title":"v5.11.0.0"},{"location":"changelog/#v51070","text":"http4k- * : Upgrade some dependency versions http4k-core : [Fix #1009] Extracting access token from non-standard AccessToken response fails","title":"v5.10.7.0"},{"location":"changelog/#v51060","text":"http4k- * : Upgrade some dependency versions http4k-core : Make RouterDescription print-friendly","title":"v5.10.6.0"},{"location":"changelog/#v51050","text":"http4k- * : Upgrade some dependency versions http4k-serverless-lambda : Add support for custom EventBridgeEvent format","title":"v5.10.5.0"},{"location":"changelog/#v51040","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.21","title":"v5.10.4.0"},{"location":"changelog/#v51030","text":"http4k- * : Upgrade some dependency versions http4k-contract [Fix #1002]: Ability to use RequestContexts for providing a User Principal with Security.","title":"v5.10.3.0"},{"location":"changelog/#v51020","text":"http4k- * : Upgrade some dependency versions http4k-core- : [Fix] FollowRedirects now remove host header http4k-testing-webdriver- : Ability to inject clock into the Webdriver","title":"v5.10.2.0"},{"location":"changelog/#v51010","text":"http4k-testing-webdriver * : Allow the originalUri method of the OAuthRedirectionFilter to be configured when constructing an OAuthProvider H/T @mbcltd http4k-format- * : Add alternative syntax for Automarshalling injection/extraction of bodies into and out of HttpMessages","title":"v5.10.1.0"},{"location":"changelog/#v51000","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.20 http4k-testing-webdriver * : Host header is populated in Http4kWebDriver H/T @mbcltd","title":"v5.10.0.0"},{"location":"changelog/#v5900","text":"http4k- * : Upgrade some dependency versions. http4k-server-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k-client-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k- * : [Breaking - dev only] http4k is now built with Java 21, although Java 8 is still targeted.","title":"v5.9.0.0"},{"location":"changelog/#v5860","text":"http4k- * : Upgrade some dependency versions. http4k-core : New filter to validate zipkin headers. H/T @time4tea","title":"v5.8.6.0"},{"location":"changelog/#v5851","text":"http4k- * : Fix maven dependencies marked as optional in various http4k modules","title":"v5.8.5.1"},{"location":"changelog/#v5850","text":"http4k- * : Upgrade some dependency versions, including CVE fix for Jetty. http4k-core : Rename Events.then() with Events.and() for clarity.","title":"v5.8.5.0"},{"location":"changelog/#v5840","text":"http4k- * : Upgrade some dependency versions. http4k-serverless-lambda : Add support to multiple query/header values with the same key","title":"v5.8.4.0"},{"location":"changelog/#v5830","text":"http4k- * : Upgrade some dependency versions. http4k-incubator * : Added HTMX emulation on Http4kWebDriver H/T @mbcltd","title":"v5.8.3.0"},{"location":"changelog/#v5820","text":"http4k- * : Upgrade some dependency versions. http4k-core * : Added extension function ExecutionService.withRequestTracing() to propagate Zipkin traces across threads","title":"v5.8.2.0"},{"location":"changelog/#v5810","text":"http4k- * : Upgrade some dependency versions. http4k-core : BiDiLenses now implement LensInjectorExtractor","title":"v5.8.1.0"},{"location":"changelog/#v5800","text":"http4k-core : BiDiLenses now implement LensInjectorExtractor http4k-contract : [Unlikely break] NoRenderer now returns a 404 instead of an empty JSON document.","title":"v5.8.0.0"},{"location":"changelog/#v5750","text":"http4k- * : Upgrade some dependency versions. http4k-aws : Add support for AwsSdkAsyncClient. H/T @oharaandrew314","title":"v5.7.5.0"},{"location":"changelog/#v5740","text":"http4k- * : Upgrade some dependency versions http4k-testing-playwright : [New Module] Easily browser-test your http4k apps with this Playwright JUnit extension! H/T @dmcg for the inspiration.","title":"v5.7.4.0"},{"location":"changelog/#v5730","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.10 http4k-htmx : Added hyperscript.js webjar to the distribution.","title":"v5.7.3.0"},{"location":"changelog/#v5720","text":"http4k- * : Upgrade some dependency versions http4k-htmx : [New Module] Basic support for htmx development, including Webjar and custom lens types http4k-testing-webdriver : Improve support for radio buttons and radio groups in the http4k-testing-webdriver. H/T @mbcltd","title":"v5.7.2.0"},{"location":"changelog/#v5710","text":"http4k- * : Upgrade some dependency versions http4k-contract * : [Fix #964] ContractRoute - inconsistent behavior on route matching. H/T @potfur for the investigation.","title":"v5.7.1.0"},{"location":"changelog/#v5700","text":"http4k- * : Upgrade some dependency versions http4k-server-undertow : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite. http4k-server-jetty : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite.","title":"v5.7.0.0"},{"location":"changelog/#v5650","text":"http4k- * : Upgrade some dependency versions http4k-testing-approval : Whitespace is now trimmed from end of approval file content. Improves compatibility with IntelliJ (as final line endings might be added automatically) http4k-testing-webdriver : [Fix #963] Submitting empty textarea form element in the webdriver causes a validation error","title":"v5.6.5.0"},{"location":"changelog/#v5640","text":"http4k- * : Upgrade some dependency versions http4k-server-apache4 : Upgrade compromised commons-codec version. H/T @oharaandrew314 http4k-template-jte : [New module] JTE templating support","title":"v5.6.4.0"},{"location":"changelog/#v5630","text":"http4k-security-oauth :Add ability to override response mode in OAuthProvider.","title":"v5.6.3.0"},{"location":"changelog/#v5621","text":"http4k-core : [Fix] Extend URI now supports fragment parameters","title":"v5.6.2.1"},{"location":"changelog/#v5620","text":"http4k- * : Upgrade some dependency versions http4k-security-oauth : [Fix] In-memory request tracking for FakeOAuthServer now supports full AuthRequest.","title":"v5.6.2.0"},{"location":"changelog/#v5720_1","text":"http4k- * : Upgrade some dependency versions. http4k-serverless-core : Add some filters for Serverless functions.","title":"v5.7.2.0"},{"location":"changelog/#v5600","text":"http4k- * : Upgrade some dependency versions. http4k-core : Added nonBlank() to set of standard BiDiMappings. H/T @dmcg http4k-core : [Unlikely break: Fix #956] Lenses don't really work for optional fields in HTML form parsing. Form-fields are now filtered for values which are not blank. This means that you may need to change form lenses to be optional and default to an empty string if they are missing.","title":"v5.6.0.0"},{"location":"changelog/#v5500","text":"http4k- * : Upgrade some dependency versions. http4k-cloudnative - Add support for enums in EnvironmentKey http4k-testing-tracerbullet : [Breaking] Fixed SequenceDiagrams for Mermaid to be formatted correctly. This may break approval files for any tests.","title":"v5.5.0.0"},{"location":"changelog/#v5410","text":"http4k- * : Upgrade some dependency versions. http4k-client-helidon : API changes from Helidon alpha to M1 http4k-server-helidon : API changes from Helidon alpha to M1 http4k-contract : [Fix #750] JacksonFieldMetadataRetrievalStrategy is incompatible with kotlinx.serialization @Serializable classes. H/T @krissrex http4k-realtime-core : [Fix #951] Add filters to initialise request context for SSE and WS. http4k-realtime-core : [Fix #885] Accept websocket subprotocols. Note that not all servers are currently supported http4k-server-jetty : [Fix #885] Support subprotocols for Websockets","title":"v5.4.1.0"},{"location":"changelog/#v5400","text":"http4k- * : Upgrade some dependency versions. http4k-aws : [Fix #656] AWS request signing issue for URLs with special characters. H/T @krissrex http4k-aws : [Fix #947] AWS request signing issue for duplicated headers and header values with multiple spaces. H/T @krissrex http4k-format-kondor-json : [Unlikely Break] Upgrade kondor-json to 2.0.0. H/T @FredNordin","title":"v5.4.0.0"},{"location":"changelog/#v5300","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.0. http4k-core : Surface root error messages in Body lenses when failure occurs on deserialisation. http4k-template-thymeleaf : [Unlikely Break] Use HTML rendering mode and .html suffix by default for Thymeleaf templates. H/T @mikaelstaldal","title":"v5.3.0.0"},{"location":"changelog/#v5210","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Fix #939] Override all Request and Response mutators for routed messages. H/T @kwydler","title":"v5.2.1.0"},{"location":"changelog/#v5200","text":"http4k-contract-jsonschema : [New module] Extracted this so we can reuse in non-OpenAPI scenarios http4k-contract : [Deprecation] Repackaging of JSON schema classes. Should only affect users if they have explicitly used/extended the standard behaviour.","title":"v5.2.0.0"},{"location":"changelog/#v5121","text":"http4k-serverless-lambda * : [Fix #936] AWS SQS null deserialization issues","title":"v5.1.2.1"},{"location":"changelog/#v5120","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth * : Remove dependency on Kotlin-reflect by adding custom adapters. H/T","title":"v5.1.2.0"},{"location":"changelog/#v5111","text":"http4k-serverless-lambda * : [Fix #933] AWS SQS deserialization issue when md5OfMessageAttributes is null. H/T @oharaandrew314","title":"v5.1.1.1"},{"location":"changelog/#v5110","text":"http4k-realtime-core * : Readd test client methods for SSE and WS","title":"v5.1.1.0"},{"location":"changelog/#v5100","text":"http4k-server-realtime-core * : [Breaking - Fix #931] Change Websocket and SSE interfaces to return WsResponse and SseResponse objects. This makes it easier to set response headers and control if a connection is made from the incoming request as it is no-longer hidden (it is exposed at the top level instead of being hidden in the SSE and Websocket objects). It also means that the interfaces for the protocols follow the same pattern. http4k-server-jetty * : As above http4k-server-undertow * : As above http4k-core * : [Fix #930] Update content-length header after GZipping it. H/T @bjornbugge","title":"v5.1.0.0"},{"location":"changelog/#v5000","text":"http4k- * : Upgrade some dependency versions. http4k- * : [Breaking] Remove all previous deprecations from all modules for v4. To upgrade cleanly, first upgrade to v4.48.0.0 and then re-upgrade to v5.0.0.0 . This will ensure that you only have to deal with Deprecations between the major versions. http4k-templates-dust : [Breaking] Nashorn is finally removed, so we are dropping support for this module. If you are on-pre Java 19 you can continue to use the old module version with no breaking changes. http4k- *: [Breaking] http4k is now built with Java 20. We are still compiling for older Java versions. New major versions will now be incoming with every major JDK release in order to track new and retired JVM features (6 month cycle). http4k-server-jetty : New Server Backend JettyLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard Jetty remains usable with any Java version. http4k-core : New Server Backend SunHttpLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard SunHttp remains usable with any Java version. http4k-server-helidon : [New Module] Helidon is a Loom-first rewrite of the popular server. Requires Java >= 19 to use. http4k-server-websocket : [New Module] A lightweight Websocket server built on TooTallNate/Java-Websocket . H/T @oharaandrew314 http4k-client-helidon : [New Module] An HTTP client build from the ground up to take advantage of project Loom. Requires Java >= 19 to use. http4k-format-kondor-json : [New Module] Support for KondorJson , the reflection-free JSON library. http4k-testing-tracerbullet : [New Module] TracerBullet allows you to hook into the http4k Events implementation to visually document your applications through testing. See example in reference guide. http4k-contract : Allow RouteMetaDsl to be marked as hidden H/T @oharaandrew314","title":"v5.0.0.0"},{"location":"changelog/#v44800","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.22.","title":"v4.48.0.0"},{"location":"changelog/#v44720","text":"http4k- * : Upgrade some dependency versions. http4k-incubator Further simplifications of tracing algorithm","title":"v4.47.2.0"},{"location":"changelog/#v44710","text":"http4k-core Make it easy to propagate or update trace spans in ZipkinStorage http4k-incubator Further simplifications of tracing algorithm","title":"v4.47.1.0"},{"location":"changelog/#v44600","text":"http4k- * : Upgrade some dependency versions. http4k-core : Added status lookup by code. H/T @jhult http4k-core : [Unlikely break] Client request tracing now sets and resets the ThreadLocal containing the current Zipkin traces. Possible break if you were relying on Zipkin state in a downstream handler. This change will allow better in-memory testing as traces will be reported correctly inside the context of the filter. http4k-incubator : [Break] Changes to improve how we create Tracing trees, and this the signature of the Tracer to take EventNode which is a tree node.","title":"v4.46.0.0"},{"location":"changelog/#v44500","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Fix #912 - CatchLensFailure filter now can pass the Request object into the receiver. H/T @mikaelstaldal http4k-server-format-moshi : Add support for Sets http4k-security-oauth : [Breaking] AccessTokens create method took an unnecessary duplicate parameter. To fix, just remove the authorizationCode parameter from your implementations and use the code from the tokenRequest","title":"v4.45.0.0"},{"location":"changelog/#v44410","text":"http4k- * : Upgrade some dependency versions. http4k-core : SameSite cookie is now lax when it comes to casing.","title":"v4.44.1.0"},{"location":"changelog/#v44400","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Allow setting of compression level on GZip filter in both Streaming and Memory mode. To fix, simply change from GzipCompressionMode.Memory/Streaming to GzipCompressionMode.Memory()/Streaming()","title":"v4.44.0.0"},{"location":"changelog/#v44310","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Fix] #901. Improve performance of GZip in streaming mode.","title":"v4.43.1.0"},{"location":"changelog/#v44300","text":"http4k- * : Upgrade some dependency versions. http4k-server-undertow : Upgrade websocket requests based on other common headers. H/T @endofhome http4k-security-oauth : [Breaking] Make full callback URI available as part AuthorizationCodeMissing error. Fixes #895 http4k-core : Static resources now return directives as well as content type on served assets.","title":"v4.43.0.0"},{"location":"changelog/#v44210","text":"http4k- * : Upgrade some dependency versions.","title":"v4.42.1.0"},{"location":"changelog/#v44200","text":"http4k- * : Upgrade some dependency versions. http4k-testing-kotest : [Possible break] Fix of this Kotest issue in new dependency release might lead to some surprising changes in behaviour of matchers for comparing JSON nodes","title":"v4.42.0.0"},{"location":"changelog/#v44140","text":"http4k- * : Upgrade some dependency versions. http4k-contract-ui-redoc : [New Module] Serve Redoc with the redocUiWebjar function. http4k-contract : [New Feature] Serve Redoc with the redocUiLite function. http4k-contract-ui-swagger : [Fix] #880. swaggerUiWebjar now works properly with a non-root path. Plus performance improvements.","title":"v4.41.4.0"},{"location":"changelog/#v44130","text":"http4k-incubator TracerBullet diagrams have more options for reporting errors.","title":"v4.41.3.0"},{"location":"changelog/#v44120","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.20. http4k-incubator TracerBullet diagrams have added colours.","title":"v4.41.2.0"},{"location":"changelog/#v44111","text":"http4k-contract-ui-swagger Fix dependency from provided -> api","title":"v4.41.1.1"},{"location":"changelog/#v44110","text":"http4k- * : Upgrade some dependency versions. http4k-opentelemetry * :Fix #867. OpenTelemetry tracing uses bad default span naming. H/T @krissrex for the report. http4k-contract-ui-swagger : [New Module] Serve a customized Swagger UI via a bundled WebJar with the new swaggerUiWebjar function. H/T @oharaandrew314 http4k-contract : Deprecate swaggerUI in favor of new swaggerUiLite function, which uses a new config format. H/T @oharaandrew314","title":"v4.41.1.0"},{"location":"changelog/#v44100","text":"http4k-core * : [Unlikely break] Fix creation of UriTemplate when it starts/ends with multiple slashes. This shouldn't cause any problems that we know about, but we are bumping the breaking version number just in case.","title":"v4.41.0.0"},{"location":"changelog/#v44020","text":"http4k- * : Upgrade some dependency versions. http4k-client-apache * : Fix #866 - ApacheClient does not handle SocketException.","title":"v4.40.2.0"},{"location":"changelog/#v44010","text":"http4k- * : Upgrade some dependency versions. http4k-incubator : TracerBullet now renders results of tests by default. Use RenderingMode to switch off this default behaviour.","title":"v4.40.1.0"},{"location":"changelog/#v44000","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [Possible clash/break] Upgrade to v2.0 of SnakeYaml (CVE fixes etc) may break dependencies which previously used v1.3X.X. It is safe to pin your SnakeYaml version to 1.3X.X if there is a clash with other libraries in your stack.","title":"v4.40.0.0"},{"location":"changelog/#v43900","text":"http4k-contract : [Breaking] Support for HTTP webhooks and callbacks in OpenApi3 models. Note that the Swagger UIs do not support OA 3.1.0 yet so we have limited the OA version number to 3.0.0.","title":"v4.39.0.0"},{"location":"changelog/#v43801","text":"http4k-core : [Fix] Header parsing to split correctly.","title":"v4.38.0.1"},{"location":"changelog/#v43800","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.10. http4k-core : [Unlikely break] Header values now trim leading space (as per RFC) http4k-incubator : Added D2 support for tracing diagrams. http4k-testing-approval : Make tests line-ending-agnostic. H/T @oharaandrew314 http4k-format- * : Various tweaks to modules to standardise behaviour. H/T @oharaandrew314","title":"v4.38.0.0"},{"location":"changelog/#v43700","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.0. http4k-core : [Fix] #846 - Status.hashCode is inconsistent with Status.equals. http4k-contract : Add new endpoint security type: OpenIdConnectSecurity . H/T @oharaandrew314 http4k-contract : swaggerUi now supports Oauth2 redirects. H/T @oharaandrew314","title":"v4.37.0.0"},{"location":"changelog/#v43600","text":"http4k- * : Upgrade some dependency versions. http4k-multipart : Add lensing of Multipart form fields using JSON and Automarshalling http4k-server-jetty : Add support for serving SSE. H/T @FredNordin http4k-contract : [Breaking Fix] Fix #842 - Map OpenAPI implementation adds all properties as required H/T @BBB","title":"v4.36.0.0"},{"location":"changelog/#v43540","text":"http4k- * : Upgrade some dependency versions. http4k-core : Allow access-control-max-age header to be set from cors policy. H/T @moddular http4k-contract : [Fix] Or security renderer was not rendering properly when the component parts are themselves composite securities.","title":"v4.35.4.0"},{"location":"changelog/#v43530","text":"http4k- * : Upgrade some dependency versions. http4k-incubator : Trace diagram improvements for PUML, Mermaid and Markdown.","title":"v4.35.3.0"},{"location":"changelog/#v43520","text":"http4k-incubator : More diagram tweaking.","title":"v4.35.2.0"},{"location":"changelog/#v43510","text":"http4k-incubator : Tweak of some diagramming.","title":"v4.35.1.0"},{"location":"changelog/#v43500","text":"http4k- * : Upgrade some dependency versions. http4k-failsafe : [New Module!] Failsafe is a lightweight, zero-dependency library for handling failures. H/T @FredNordin http4k-incubator : [Breaking] Rewrite of infrastructure for generating tracing diagrams, including new interfaces and support for rendering to various formats. Initial support for PUML and Mermaid.","title":"v4.35.0.0"},{"location":"changelog/#v43440","text":"http4k- * : Upgrade some dependency versions. http4k-server-undertow : Remove extra dependencies which aren't needed. http4k-contract : fix Path value resolution it starts with same string as the prefix URL segment. H/T @tkint","title":"v4.34.4.0"},{"location":"changelog/#v43431","text":"http4k- * : Fix #827 - Requests with unknown HTTP method result in uncaught exceptions","title":"v4.34.3.1"},{"location":"changelog/#v43430","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Support for arrays of enums in OA3.","title":"v4.34.3.0"},{"location":"changelog/#v43420","text":"http4k- * : Upgrade some dependency versions.","title":"v4.34.2.0"},{"location":"changelog/#v43410","text":"http4k- * : Upgrade some dependency versions. http4k-template-rocker : [New module] Compile-time templating with Rocker!","title":"v4.34.1.0"},{"location":"changelog/#v43404","text":"http4k-contract- : Fix errant import which broke multipart Openapi V3 spec.","title":"v4.34.0.4"},{"location":"changelog/#v43403","text":"http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Re-re-fix.","title":"v4.34.0.3"},{"location":"changelog/#v43402","text":"http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Refix.","title":"v4.34.0.2"},{"location":"changelog/#v43401","text":"http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit.","title":"v4.34.0.1"},{"location":"changelog/#v43400","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.21. http4k-format- * : Added auto() methods to arbitrary lenses (so Query, Header, FormField etc..) http4k-core : [Unlikely break] reverseProxy() now takes the authority into account instead of just the hostname from the request. This should only impact you if you are doing reverse proxy operational on client side and using localhost without a port as a proxy. To fix - simply add the port to your proxying setup and all should be good. http4k-contract * : Fix: Remove duplicate content type header.","title":"v4.34.0.0"},{"location":"changelog/#v43330","text":"http4k- * : Upgrade some dependency versions. http4k-server-ktor * : Fix: Remove duplicate content type header.","title":"v4.33.3.0"},{"location":"changelog/#v43321","text":"http4k-contract : Fix OpenApi rendering for enums when there isn't reflection.","title":"v4.33.2.1"},{"location":"changelog/#v43320","text":"http4k- * : Upgrade some dependency versions. http4k-resilience4j-jetty : Fix #804 - CircuitBreaker counts error twice, once as an error and once as a success http4k-client-okhttp : Added websocket client. H/T @FredNordin. http4k-format-argo : Fix problem with duplicate keys when creating objects. http4k-security-oauth : Ability to add scopes to the OAuth refresh token. H/T @p10r","title":"v4.33.2.0"},{"location":"changelog/#v43310","text":"http4k- * : Upgrade some dependency versions. http4k-client-jetty : Added websocket client. H/T @FredNordin. http4k-format-moshi : Add facility to use lightweight metadata adapter instead of Kotlin reflect. H/T @oharaandrew314","title":"v4.33.1.0"},{"location":"changelog/#v43300","text":"http4k- * : Upgrade some dependency versions, including CVE fix for Handlebars. http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda","title":"v4.33.0.0"},{"location":"changelog/#v43240","text":"http4k- * : Upgrade some dependency versions. http4k-core : Move Jakarta Servlet code from Jetty as is now shared. http4k-contract : Add UserCredentialsOAuthSecurity . This allows the OpenApi spec to define a Resource Owner Password Credentials grant. It also includes a shortcut to load the principal into a RequestContextLens . H/T @oharaandrew314 http4k-core : Add StringBiDiMappings.csv to map between string and list, with a configurable delimiter and element mapping. H/T @oharaandrew314 http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda","title":"v4.32.4.0"},{"location":"changelog/#v43230","text":"http4k- * : Upgrade some dependency versions including CVE fix for Undertow backend.","title":"v4.32.3.0"},{"location":"changelog/#v43220","text":"http4k-core : Add StringBidDiMapping.basicCredentials to easily convert between Credentials and basic auth. H/T oharaandrew314 http4k-core : Add Header.AUTHORIZATION_BASIC lens to easily get and set basic Credentials for a message. H/T oharaandrew314 http4k-contract : BasicAuthSecurity now supports a RequestContextLens to store the principal. H/T oharaandrew314","title":"v4.32.2.0"},{"location":"changelog/#v43210","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Added ability to make Automarshallers strict.","title":"v4.32.1.0"},{"location":"changelog/#v43200","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.20. http4k-testing-webdriver : [Unlikely Break] Upgrade has removed deprecated method.","title":"v4.32.0.0"},{"location":"changelog/#v43100","text":"http4k-core : [Unlikely Break] Added ZipkinTraceStorage , defaulting to ThreadLocal implementation. This allows centralised storage of trace information in non-standard threading environments (eg. coroutines).","title":"v4.31.0.0"},{"location":"changelog/#v430100","text":"http4k- * : Upgrade some dependency versions. http4k-core : YAML is now a recognised content type.","title":"v4.30.10.0"},{"location":"changelog/#v43090","text":"http4k-cloudevents : Add custom lenses to retrieve data from a cloud event and an extension function to set it.","title":"v4.30.9.0"},{"location":"changelog/#v43080","text":"http4k-cloudevents : Add Jackson.cloudEventsFormat() so we can use custom formats in cloud events lenses","title":"v4.30.8.0"},{"location":"changelog/#v43070","text":"http4k- * : Upgrade some dependency versions. http4k-core : Fix #779: SunHttp does not blow up if you add a ll value.","title":"v4.30.7.0"},{"location":"changelog/#v43060","text":"http4k- * : Upgrade some dependency versions.","title":"v4.30.6.0"},{"location":"changelog/#v43050","text":"http4k- * : Upgrade some dependency versions. http4k-client-websocket : Fix #775 - WebsocketClient.nonBlocking cannot receive messages in binary mode. H/T oharaandrew314","title":"v4.30.5.0"},{"location":"changelog/#v43040","text":"http4k- * : Upgrade some dependency versions.","title":"v4.30.4.0"},{"location":"changelog/#v43030","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth : RefreshingOAuthToken does not blow up when no expiry returned by server.","title":"v4.30.3.0"},{"location":"changelog/#v43021","text":"http4k-format-moshi-yaml : [Fix] Re-fix YAML defaults for over greedy boolean values (regression caused by upgrade to SnakeYaml).","title":"v4.30.2.1"},{"location":"changelog/#v43020","text":"http4k-security-oauth : Make FakeOAuthServer more configurable, and removed the need for passing in an auth-code generator.","title":"v4.30.2.0"},{"location":"changelog/#v43000","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Converted AccessToken to be an interface, and internalised a lens which shouldn't have been used by anyone. To fix uses of accessTokenResponseBody , replace with Body.auto().toLens() , importing from OAuthServerMoshi.","title":"v4.30.0.0"},{"location":"changelog/#v42910","text":"http4k- * : Upgrade some dependency versions.","title":"v4.29.1.0"},{"location":"changelog/#v42900","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Slight changes to CSRF generator interface. Should be easy to fix.","title":"v4.29.0.0"},{"location":"changelog/#v42820","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth : Internal refactoring","title":"v4.28.2.0"},{"location":"changelog/#v42810","text":"http4k- * : Upgrade some dependency versions. http4k-client-okhttp : Handle previously escapable HTTP client timeout case. http4k-contract : Added Swagger UI helper route. H/T @oharaandrew314","title":"v4.28.1.0"},{"location":"changelog/#v42800","text":"http4k- * : Upgrade some dependency versions, including CVE fix for Undertow backend. http4k-contract : [Unlikely break] Remove direct dependency on kotlin-reflect JAR, as it is brought in my http4k-format-jackson anyway. This builds ok but we have bumped the version number just to be sure. H/T @oharaandrew314 for the inspiration. http4k-format-core : Add ContentNegotiator and auto versions to be plugged into http4k-format-* modules. H/T @oharaandrew314 http4k-core : Add cors exposed headers property. H/T @oharaandrew314","title":"v4.28.0.0"},{"location":"changelog/#v42740","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Upgrade Moshi to introduce a JSON node model, thus converting Moshi to be an AutoMarshallingJson. This should open the door to us eventually allowing Moshi to be used in http4k-contracts (and OpenAPI). Massive H/T to @oharaandrew314 for the work that went into this.","title":"v4.27.4.0"},{"location":"changelog/#v42730","text":"http4k-contract : OpenApi3 Operation Ids now replace '-' with '_', as '-' interfere with generation of OpenAPI clients.","title":"v4.27.3.0"},{"location":"changelog/#v42720","text":"http4k- * : Upgrade some dependency versions. http4k-graphql : Add GraphQL explorer for http4k-graphql. H/T @arnabkd http4k-realtime-core : Add helper for test Websocket. H/T @oharaandrew314 http4k-resilience4j * : Fix #745: ResilienceFilters.CircuitBreak counts an error twice: once as successful, once as error.","title":"v4.27.2.0"},{"location":"changelog/#v42710","text":"http4k- * : Upgrade some dependency versions. http4k-core : Added support for Web Linking header standard http4k-multipart : Fix multipart upload failure if charset is included in content type. H/T @wickwirew http4k-server-jetty : Remove usage of deprecated status description API. H/T @@makowalski + @mandyvuong","title":"v4.27.1.0"},{"location":"changelog/#v42700","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.0","title":"v4.27.0.0"},{"location":"changelog/#v42600","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Unlikely break] Remove dependency on kotlin stdlib JDK 8 as we don't need it to compile. If this causes a problem, simply re-add api(Kotlin.stdlib.jdk8) to your project dependency list. http4k- * : Fix #744 - Provided dependencies included as runtime in http4k versions > 4.19.1.0.","title":"v4.26.0.0"},{"location":"changelog/#v425162","text":"http4k-core : Fix query parameter parsing when value contained = . H/T @overfullstack http4k-security-digest : Fix digest challenge parsing when nonce contained = . H/T @oharaandrew314","title":"v4.25.16.2"},{"location":"changelog/#v425161","text":"http4k-contract : [Revert fix] - File field is described as \"string\" instead of \"file\" in OA3 specification.","title":"v4.25.16.1"},{"location":"changelog/#v425160","text":"http4k- * : Upgrade some dependency versions. http4k-core : Deprecate eTag filter in favour of ETagSupport. http4k-contract : [Fix] - File field is described as \"string\" instead of \"file\" in OA3 specification.","title":"v4.25.16.0"},{"location":"changelog/#v425150","text":"http4k- * : Upgrade some dependency versions. http4k-core : Fix #738 - Calculating eTag ate body. http4k-core : Caching filters now replace headers instead of adding them. http4k-server-jetty : Change constructor to use supported shutdown mode. H/T @jshiell","title":"v4.25.15.0"},{"location":"changelog/#v425140","text":"http4k- * : Upgrade some dependency versions. http4k-core : Refreshing Credentials Provider now does not block if there is more than half of the expiring time left. http4k-core : Fix #735 - use whole message body for etag hash. H/T @aSemy http4k-metrics-micrometer - Enable publishPercentileHistogram for Micrometer request timer H/T @jakubjanecek","title":"v4.25.14.0"},{"location":"changelog/#v425130","text":"http4k-server- *: Add support for graceful shutdown (available to most server implementations) H/T @nlochschmidt http4k-core : Simplify hex decoding H/T @dzappold","title":"v4.25.13.0"},{"location":"changelog/#v425120","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml- *: Replace default YAML boolean resolver to be less greedy.","title":"v4.25.12.0"},{"location":"changelog/#v425110","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.21. http4k-core : Fix #728 - No way to set the request timeout when using the JavaHttpClient. H/T @gmulders http4k-oauth-security : Add missing Moshi adapter","title":"v4.25.11.0"},{"location":"changelog/#v425101","text":"http4k-core : Fix ServerFilters.BasicAuth handling of passwords containing colons H/T @robd","title":"v4.25.10.1"},{"location":"changelog/#v425100","text":"http4k- * : Upgrade some dependency versions. http4k-core : Expand out values4k lens option http4k-core : Allow cookie values to be returned unquoted H/T @2x2xplz http4k-format- * : Throw a lens failure if a valid locale was not parsed H/T @oharaandrew314 http4k-opentelemetry : Fix #726 - OpenTelemetry: t.localizedMessage can't be null","title":"v4.25.10.0"},{"location":"changelog/#v42590","text":"http4k- * : Upgrade some dependency versions, including Ktor to v2.0.0 http4k-format-jackson-csv * : [New module] H/T @oharaandrew314 for the contribution. http4k-core : New standard mappings for Time primitives. H/T @oharaandrew314","title":"v4.25.9.0"},{"location":"changelog/#v42580","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.20. http4k-core : Enable overridable behaviour for CatchAll filter. H/T @dcmg http4k-multipart : Add disk cache path to MultipartFormBody.from() parameters. H/T @rny","title":"v4.25.8.0"},{"location":"changelog/#v42570","text":"http4k-client-fuel : [New module] An http4k client based on Fuel with both sync and async support.","title":"v4.25.7.0"},{"location":"changelog/#v42560","text":"http4k- * : Upgrade some dependency versions, including Jackson to overcome CVE-2020-36518.","title":"v4.25.6.0"},{"location":"changelog/#v42552","text":"http4k-contract : Don't output required fields into OpenAPI if there are none.","title":"v4.25.5.2"},{"location":"changelog/#v42551","text":"http4k-contract : Small tweak to internal API","title":"v4.25.5.1"},{"location":"changelog/#v42550","text":"http4k-contract : Add format OpenApi hints to Arrays and Maps","title":"v4.25.5.0"},{"location":"changelog/#v42541","text":"http4k-contract : Remove println from AutoJsonToJsonSchema. Doh!","title":"v4.25.4.1"},{"location":"changelog/#v42540","text":"http4k-format- *: Correctly identify integer and number JSON types. This has a knock on effect in OpenApi specifications.","title":"v4.25.4.0"},{"location":"changelog/#v42530","text":"http4k-serverless-tencent : Downgrade events library as is insecure.","title":"v4.25.3.0"},{"location":"changelog/#v42520","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy).","title":"v4.25.2.0"},{"location":"changelog/#v42510","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy).","title":"v4.25.1.0"},{"location":"changelog/#v42500","text":"http4k- * : Upgrade some dependency versions. http4k-security-oauth [Breaking]: Rename OauthCallbackError to OAuthCallbackError","title":"v4.25.0.0"},{"location":"changelog/#v42400","text":"http4k- * : Upgrade some dependency versions. http4k-testing-webdriver * : [Breaking] Upgrade of webdriver to V4 has changed the APIS. The custom By implementation is no longer required so you can use the inbuilt Selenium version instead. The disabledCssSelector By implementation has been removed, although you can simply replicate this using the existing CSS selector model.","title":"v4.24.0.0"},{"location":"changelog/#v42300","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Support OA3 meta fields on properties if they are populated by a custom annotation. http4k-graphql : [Possible breaks] Due to upgrade of underlying graphql lib.","title":"v4.23.0.0"},{"location":"changelog/#v42201","text":"http4k-security-oauth : Fix error messages for oauth callback failures","title":"v4.22.0.1"},{"location":"changelog/#v42200","text":"http4k-security-oauth [Breaking]: apiBase path is now preserved when building auth and token uris http4k-security-oauth [Breaking]: provide reason when an oauth callback fails http4k-security-oauth [Breaking]: allow id token consumer to fail authentication flow","title":"v4.22.0.0"},{"location":"changelog/#v42111","text":"http4k-contract : OpenApi3 - Expose new prefix-overriding in OpenApi definitions.","title":"v4.21.1.1"},{"location":"changelog/#v42110","text":"http4k- * : Upgrade some dependency versions. http4k-contract : OpenApi3 - Ability to provide prefixes for all models in a tree. This allows you to have multiple versions of a single model in the specification (at the cost of duplicated schema models).","title":"v4.21.1.0"},{"location":"changelog/#v42100","text":"http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] All metrics now include path when labelled and routed. For consistency, . in path names are now convert to underscores as well. Regexes are also removed from paths on both client and server. H/T @hektorKS http4k-testing-strikt : Fix #709 - Strikt assertion builder for Uri.path H/T @michaelbannister","title":"v4.21.0.0"},{"location":"changelog/#v42020","text":"http4k-contract : OpenApi3 - Don't add required field if no fields are required!","title":"v4.20.2.0"},{"location":"changelog/#v42010","text":"http4k-contract : Fix #706: Form \"multi\" lens's do not render an items field in contracts. http4k-testing-chaos : ChaoticHttpHandler disables Chaos API when reflection not available.","title":"v4.20.1.0"},{"location":"changelog/#v42000","text":"http4k-core : Fix #704: Filters are recreated on every request/ H/T @hektorKS http4k-core : Fix #702: TrafficFilters.ReplayFrom doesn't correctly read from Replay. http4k-server-netty : Fix #703: Netty: null cannot be cast to non-null type java.net.InetSocketAddress http4k-client-apache-async : Remove usage of deprecated API http4k-client-jetty : Remove usage of deprecated API http4k-testing-webdriver : Remove usage of deprecated (internal) API http4k- * : Upgrade some dependency versions.","title":"v4.20.0.0"},{"location":"changelog/#v41950","text":"http4k-client-websocket : Apply a timeout when creating a blocking client websocket connection","title":"v4.19.5.0"},{"location":"changelog/#v41940","text":"http4k- * : Upgrade some dependency versions.","title":"v4.19.4.0"},{"location":"changelog/#v41930","text":"http4k- * : Upgrade some dependency versions. http4k-opentelemetry : Fixes #697: Upgraded OpenTelemetry version to 1.11.0 H/T @jenarros","title":"v4.19.3.0"},{"location":"changelog/#v41920","text":"http4k-server-jetty : Replace conscrypt with internal java for ALPN server","title":"v4.19.2.0"},{"location":"changelog/#v41910","text":"http4k- * : Upgrade some dependency versions. http4k-core : Added ContentDispositionAttachment server filter. H/T @jenarros http4k-core : Fix path conversion for static routing handlers with trailing. H/T @jenarros http4k-contract : Support non-JSON schema types in request definitions.","title":"v4.19.1.0"},{"location":"changelog/#v41900","text":"http4k-core : [Potential Break] Fix #693 Cookie implementation uses LocalDateTime val which is implicitly turned into GMT time for cookie. Break is that Cookies now run from Instant instead of LocalDateTime. Thanks to @maedjyuk-ghoti for alerting us to chase down this 5y+ standing bug! http4k-security-oauth : Fixes to the InsecureCookieBasedOAuthPersistence to make it more user-friendly. http4k-server-netty : Keep-alive for Netty when not streaming. H/T @jakubjanecek for the contrib!","title":"v4.19.0.0"},{"location":"changelog/#v41800","text":"http4k- * : Upgrade some dependency versions. http4k-core : Fixed URLPathSegment encoding/decoding based on RFC 3986. H/T @jenarros for the thoughtful and through contribution!","title":"v4.18.0.0"},{"location":"changelog/#v41790","text":"http4k-core : Added mapping for enum() in lenses.","title":"v4.17.9.0"},{"location":"changelog/#v41780","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Fix #687 - OpenApiV3 object serialization. H/T @lawkai","title":"v4.17.8.0"},{"location":"changelog/#v41770","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : Fix for serialising Maps with null values (the key should still be rendered!) http4k-format-moshi-yaml : Remove accidental stack trace dump.","title":"v4.17.7.0"},{"location":"changelog/#v41760","text":"http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [New module] YAML marshalling with zero-reflection is now possible due to a combination of Moshi and SnakeYaml http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path. http4k-format-jackson-xml : Add autoBody for ConfigurableJacksonXml. H/T @oharaandrew314","title":"v4.17.6.0"},{"location":"changelog/#v41750","text":"http4k- * : Upgrade some dependency versions, including ForkHandles to 2.0.0.0. http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path.","title":"v4.17.5.0"},{"location":"changelog/#v41740","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.10 http4k-incubator : Playing with TracerBullets... a generic interface for building TraceTrees from lists of MetadataEvents.","title":"v4.17.4.0"},{"location":"changelog/#v41730","text":"http4k- * : Upgrade some dependency versions. http4k-testing-approval : Fix #679. Approval tests delete actual when passing.","title":"v4.17.3.0"},{"location":"changelog/#v41720","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Added Servers to OpenApi renderer. H/T @zsambek and @MarcusDunn for making it happen.","title":"v4.17.2.0"},{"location":"changelog/#v41710","text":"http4k-core : Make timeouts configurable for Java8HttpClient .","title":"v4.17.1.0"},{"location":"changelog/#v41700","text":"http4k- * : [Careful] Upgrade some dependency versions, including Kotlin to 1.6.0. http4k- * : [Breaking] Removal of all previously deprecated methods and types. To ensure you get the smoothest experience, please upgrade to v4.16.3.0 first, deal with the replacements and then upgrade to 4.17.0.0","title":"v4.17.0.0"},{"location":"changelog/#v41630","text":"http4k- * : Upgrade some dependency versions. http4k-testing-approval : Check the content type after the content is checked.","title":"v4.16.3.0"},{"location":"changelog/#v41620","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Support for top-level enums in schema. http4k-contract : Support for enums in Header/Query/Paths. Finally!","title":"v4.16.2.0"},{"location":"changelog/#v41610","text":"http4k- * : Upgrade some dependency versions. http4k- * : Upgrade build process to Kotlin. H/T @franckrasolo","title":"v4.16.1.0"},{"location":"changelog/#v41600","text":"http4k-contract : [Breaking] Added API-level tags to the contract rendering. http4k-serverless-lambda : Fix encoding of body to work with gzip filter","title":"v4.16.0.0"},{"location":"changelog/#v41500","text":"http4k-contract : [Break] BearerAuthSecurity is now more typesafe when taking a lens.","title":"v4.15.0.0"},{"location":"changelog/#v41414","text":"http4k-serverless-lambda : More fixing of deserialisation of SNS events.","title":"v4.14.1.4"},{"location":"changelog/#v41413","text":"http4k-serverless-lambda : Fix deserialisation of SNS events.","title":"v4.14.1.3"},{"location":"changelog/#v41412","text":"http4k-contract : Fix #667 - Jackson annotations being missed in FieldRetrieval. http4k-undertow : Server now handles HTTP requests gracefully when there is no HTTP handler set.","title":"v4.14.1.2"},{"location":"changelog/#v41411","text":"http4k-core : ChaoticHttpHandler is now event better behaved when chaos is not enabled and respects routing templates when applying. http4k-core : Fix #665 - OpenAPI json is incorrect when multi string query lens with defaulted values is used. H/T @suyash192","title":"v4.14.1.1"},{"location":"changelog/#v41410","text":"http4k-core : ChaoticHttpHandler is now better behaved when chaos is not enabled.","title":"v4.14.1.0"},{"location":"changelog/#v41400","text":"http4k-core : Tidying up HttpEvents http4k-graphql : [Break] Handle null variables in calls.","title":"v4.14.0.0"},{"location":"changelog/#v41340","text":"http4k-core : Added convenience HttpEvents","title":"v4.13.4.0"},{"location":"changelog/#v41330","text":"http4k-core : ServerFilter request tracing now reinstates previous trace on exit instead of clearing it.","title":"v4.13.3.0"},{"location":"changelog/#v41320","text":"http4k-core : Make MetadataEvent a data class.","title":"v4.13.2.0"},{"location":"changelog/#v41310","text":"http4k- * : Upgrade some dependency versions. http4k-core : Rename AsyncHttpClient to AsyncHttpHandler (deprecation). http4k-contract : Fix #664 - Introduce OpenAPIJackson to not serialize nulls by default into OpenAPI specs. If you use your own Jackson instance, you can replicate this behaviour by using .setSerializationInclusion(NON_NULL) on your custom ObjectMapper implementation.","title":"v4.13.1.0"},{"location":"changelog/#v41300","text":"http4k-core : Reverse Proxy available in both routing and non-routing version. Use reverseProxy() or reverseProxyRouting() accordingly","title":"v4.13.0.0"},{"location":"changelog/#v41231","text":"http4k-core : Reverse Proxy router now falls back to URI host when Host header missing.","title":"v4.12.3.1"},{"location":"changelog/#v41230","text":"http4k-security-oauth : Nicer OAuth client filters.","title":"v4.12.3.0"},{"location":"changelog/#v41220","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Fix #657 Use jackson to serialize enum models for OpenApi.","title":"v4.12.2.0"},{"location":"changelog/#v41210","text":"http4k- * : Upgrade some dependency versions. http4k-core : Fix TrafficFilters.RecordTo eating body stream when used on a server.","title":"v4.12.1.0"},{"location":"changelog/#v41201","text":"http4k- * : Fix #652 - AWS event format adapters have fields with wrong cases.","title":"v4.12.0.1"},{"location":"changelog/#v41200","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.5.30. http4k-testing-chaos : [Break] Behaviour is now an abstract class instead of a typealias. Super simple to fix. :)","title":"v4.12.0.0"},{"location":"changelog/#v41101","text":"http4k-graphql : Fix - Downgrade graphql-java and fix Graphql reference example. 4.11.0.0 contained an incompatible version of graphql-java for generate use. H/T @razvn for spotting and fixing. :)","title":"v4.11.0.1"},{"location":"changelog/#v41100","text":"http4k- * : Upgrade some dependency versions. http4k-format-jackson : Fix #646 - Boolean field can escape lens check without throwing MissingKotlinParameterException. http4k-aws : Set the query parameter to empty string if it's value is null, instead of \"null\". H/T @raelg for the PR. http4k-contract : [Possible (small) Break] Fix #644 - Lazy init contract without path params: Type mismatch. Contract routes with 0 parameters are now able to be constructed lazily - which has added (for consistency) a secondary to(fn: () -> HttpHandler) function to the construction DSL. This may cause overload ambiguity when routes are defined withtout the request input parameter. To fix, un-ambiguate you bindings! (eg. to { Response(OK) } becomes to { _ -> Response(OK) } ). H/T @dbacinski for the investigation.","title":"v4.11.0.0"},{"location":"changelog/#v41010","text":"http4k- * : Upgrade some dependency versions. http4k-core : Fix #638 - Revert changes to make Uri incompatible with req -> next.invoke(request.header(\"foo\", \"bar\")); http4k-testing-kotest - Possible Break : DUE TO KOTLIN 1.4.10. Remove a haveBody matcher which uses Matcher directly, because of a bug in Kotest: https://github.com/kotest/kotest/issues/1727 http4k-format-jackson - Possible Break : DUE TO KOTLIN 1.4.10. Inline classes do not deserialise properly. See: https://github.com/FasterXML/jackson-module-kotlin/issues/356","title":"v3.262.0"},{"location":"changelog/#v32610","text":"http4k- * : Upgrade some dependency versions. http4k- * : Remove some example code which was mistakenly added to some main src dirs. No impact on anything other than JAR size. http4k-aws * : Add pluggable Amazon SDK client, allowing you to plug an HttpHandler into the Amazon SDK.","title":"v3.261.0"},{"location":"changelog/#v32600","text":"http4k- * : Upgrade some dependency versions. http4k-*, Unlikely break : Added some nicer naming and examples for when people are calling http4k via Java code. http4k-core : Fixed SunHttp server backend not setting content length, and hence responses are always chunked.","title":"v3.260.0"},{"location":"changelog/#v32590","text":"http4k- * : Upgrade some dependency versions. http4k-server-netty : Fix #141 Http4k-netty performs really badly on all benchmarks. Massive H/T adam-arold! http4k-server-ratpack : Tweak to SO_BACKLOG size (1000).","title":"v3.259.0"},{"location":"changelog/#v32580","text":"http4k-testing-kotest : [New module] A set of matchers for use with the kotest library. H/T @nlochschmidt for the PR. http4k- * : Upgrade some dependency versions.","title":"v3.258.0"},{"location":"changelog/#v32570","text":"http4k-serverless-* : Making the Serverless APIs consistent between flavours by ensuring that all Serverless functions act by class extension and not reflection based approach. Deprecated old approach. Hopefully this is simpler.. :)","title":"v3.257.0"},{"location":"changelog/#v32561","text":"http4k-core : Fix #470. Path.of cannot decode path parameter values containing %/","title":"v3.256.1"},{"location":"changelog/#v32560","text":"http4k-security-oauth : Add ability to handle form encoded responses in OAuth responses.","title":"v3.256.0"},{"location":"changelog/#v32550","text":"http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : - Fixed up Maven dependencies so that they are not exporting compileOnly libraries into POMs. http4k-security-oauth : Remove \"user\" from default list of GitHub scopes as it gives you write access to the profile. New default is empty (just public data). http4k-core : Improve defaults of SunHttp server. H/T @nlochschmidt for the PR. http4k-contract : Add description to OpenApi schema fields using Jackson annotations. H/T @env0der for the PR.","title":"v3.255.0"},{"location":"changelog/#v32540","text":"http4k- * : Upgrade some dependency versions. http4k-core : Added hostDemux() routing for when you want to select an HttpHandler based on the Host header.","title":"v3.254.0"},{"location":"changelog/#v32530","text":"http4k-core : Replaced implementation of JavaHttpClient with one from Java standard library. Should you not yet have access to the Java 11 SDK, we renamed the old implementation to Java8HttpClient . Note that some headers that are added by default by the old Java8 implementation will no longer be added. http4k-core, Breaking : Change Body.binary() lens to use an InputStream instead of a raw Body . To fix, just provide the InputStream by calling Body.stream() or similar. http4k-client-websocket, Unlikely break : Allow API users to pass in their own Draft object for custom protocols. If broken, simple fix is to just use named arguments in the construction call to the client. http4k- * : Upgrade some dependency versions.","title":"v3.253.0"},{"location":"changelog/#v32520","text":"http4k-server-apache, http4k-client-apache, http4k-client-apache-async, Breaking : Updated to Apache HTTP 5.X.X. H/T to @jshiell. Note that the underlying Apache APIs have changed in the v5 release. For the clients, this should only break if you have customised the underlying HTTP CloseableHttpClient that is passed to the constructor of the http4k client. If you have, we have you covered with.... http4k-server-apache4, http4k-client-apache4, http4k-client-apache4-async : New modules to maintain previous integration with Apache HTTP 4.X.X. Intended to reduce the impact on projects that are not ready to move to v5 yet. In these compatibility modules, renamed ApacheClient -> Apache4Client and ApacheAsyncClient to Apache4AsyncClient - which is the only change that should be required in end user code. http4k-serverless-openwhisk : Fixes to support binary content types and overcome issues with the request/response format of the OW Java runtime. http4k-core : Added some Filters for base64 encoding and decoding responses. http4k- * : Upgrade some dependency versions.","title":"v3.252.0"},{"location":"changelog/#v32510","text":"http4k-core : Added support for multiple \"cookie\" headers. H/T @jshiell http4k-serverless-openwhisk : New serverless module! http4k-serverless-*, Breaking : - Repackage some functions to org.http4k.serverless package. Just change the package names to fix.","title":"v3.251.0"},{"location":"changelog/#v32500","text":"http4k-core : Add Request.source to provide extra information about the request origin (address/port/scheme). H/T @kam1sh and @jshiell for the contributions. http4k-security-oauth : Add OAuth provider configuration for Facebook. H/T @knyttl for the PR. http4k-server-netty : Implement KeepAlive. H/T @carbotaniuman for the PR. http4k-bom : New Bill-Of-Materials module! http4k- * : Upgrade some dependency versions.","title":"v3.250.0"},{"location":"changelog/#v32490","text":"http4k- * : Upgrade some dependency versions. http4k-server-netty : Add support for response streaming. H/T @carbotaniuman for the PR. http4k-serverless-gcf : New serverless module! H/T @ssijak for the PR.","title":"v3.249.0"},{"location":"changelog/#v32480","text":"http4k-server-ratpack : New backend module! http4k-format-jackson-yaml : [New module] http4k- * : Upgrade some dependency versions. http4k-cloudnative : - Fix #418 - Fix separator propagation when adding values to an existing MapEnvironment. H/T @jshiell http4k-contract : - Add support for securing the API description endpoint. H/T @goodhoko for the PR. http4k-client-websocket : Added auto-reconnection support on blocking WsClient. H/T @alphaho for the PR. http4k-format-* : Rename/deprecate asXYZString(Any) -> asFormatString(Any) in all modules","title":"v3.248.0"},{"location":"changelog/#v32470","text":"http4k-server-ktornetty : New backend module! H/T @albertlatacz for the contribution! http4k- * : Upgrade some dependency versions. http4k-security-oauth : Fix #414 BasicAuth server filter to not throw an exception on invalid base64 input. H/T @Sebruck for the fix.","title":"v3.247.0"},{"location":"changelog/#v32460","text":"http4k- * : Upgrade some dependency versions. http4k-template-pebble : Fix #411 - Non-root pebble templates when using CachingClasspath from a compiled JAR. H/T @alyphen","title":"v3.246.0"},{"location":"changelog/#v32451","text":"http4k-server-ktorcio : Fix #410 - KtorCIO does not stop properly.","title":"v3.245.1"},{"location":"changelog/#v32450","text":"http4k- * : Upgrade some dependency versions. http4k-core : Factored out Http4kServletAdapter to allow usage of the Servlet API outside of creating a Servlet instance. http4k-*, Breaking (prevent API abuse) : Restricted generic with() method actual http4k types. Usage outside our API should not use this method. http4k-contract : Fix #404 - Rework of some FieldRetrieval classes to remove duplication and to support PropertyNamingStrategies set at the global level","title":"v3.245.0"},{"location":"changelog/#v32440","text":"http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : Fix #397 - Fixed up Maven dependencies so that they are not bringing in runtime libraries. http4k-core : - Add enum StringBiDiMapping #395 - H/T @goodhoko","title":"v3.244.0"},{"location":"changelog/#v32430","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.72 . http4k-security-oauth : A strategy can now be passed into AuthRequestWithRequestAuthRequestExtractor to determine how to combine AuthRequest and RequestObject H/T @tom","title":"v3.243.0"},{"location":"changelog/#v32420","text":"http4k- * : Upgrade some dependency versions. http4k-testing-servirtium : Improve error diagnostics. H/T @vchekan for the PR. http4k-*, Unlikely Break : Change Router to return RouterMatch instead of nullable HttpHandler . This allows us to support METHOD_NOT_ALLOWED (405) if we match a path but not a verb instead of just NOT_FOUND (404). This should break custom ro H/T @jshiell for the PR.","title":"v3.242.0"},{"location":"changelog/#v32410","text":"http4k-security-oauth, Breaking : client_id along with the corresponding TokenRequest is passed into access and refresh token generators so additional validation can take place H/T @tom","title":"v3.241.0"},{"location":"changelog/#v32400","text":"http4k- * : Upgrade Kotlin to 1.3.71 . http4k-testing-servirtium : Switch OkHttp client for Apache. http4k-server-jetty : Made some classes non-internal so they can be easily reused for custom ServerConfig implementations.","title":"v3.240.0"},{"location":"changelog/#v32390","text":"http4k-client-websocket, Breaking : Added extra onError handler when creating a non-blocking websocket. http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.70.","title":"v3.239.0"},{"location":"changelog/#v32380","text":"http4k-security-oauth : Early work on supporting refresh tokens. H/T @tom","title":"v3.238.0"},{"location":"changelog/#v32370","text":"http4k-core : Fix #377. Added replaceHeaders() method. Thanks to @bastman for the idea. http4k-contract : Fix nullability of references in OpenApi3","title":"v3.237.0"},{"location":"changelog/#v32360","text":"http4k-testing-servirtium : Don't pass recording handler into non-test methods as a resolved parameter.","title":"v3.236.0"},{"location":"changelog/#v32350","text":"http4k-testing-chaos, Break/Rename : ChaosEngine is now exposed when configuring API. Renamed withChaosEngine() to withChaosApi() , replaced toggle() and update() with enable()/disable()","title":"v3.235.0"},{"location":"changelog/#v32340","text":"http4k-testing-chaos, Break : Tweaked API make it simpler to use the ChaosEngine via programmatically (as opposed to REST). http4k-testing-servirtium, Tiny break : Tweaks to InteractionOptions to make working with Servirtium tests a bit nicer.","title":"v3.234.0"},{"location":"changelog/#v32330","text":"http4k-testing-servirtium : Upgrade ServirtiumServer to use OkHttp instead of JavaHttpClient (due to streaming restrictions on MiTM). http4k-testing-servirtium, Break : Rename Github to GitHub .","title":"v3.233.0"},{"location":"changelog/#v32320","text":"http4k-format-kotlinx-serialization : New JSON module! H/T @joscha-alisch for the PR. :) http4k-testing-servirtium : Work around Kotlin @JvmOverloads problem in ServirtiumServer. http4k- * : Upgrade some dependency versions.","title":"v3.232.0"},{"location":"changelog/#v32310","text":"http4k-testing-servirtium : Making API a bit more Java-compatible friendly. Ability to vary the Server implementation. http4k-server-jetty : Fix #362 - Websocket disconnect early causes lateinit reference race condition. H/T @fintara for the report/fix.","title":"v3.231.0"},{"location":"changelog/#v32300","text":"http4k-aws : Improved efficiency of building AWS credentials (replace String.format). http4k-testing-servirtium : Making API a bit more Java-compatible friendly. http4k- * : Upgrade some dependency versions.","title":"v3.230.0"},{"location":"changelog/#v32290","text":"http4k-security-oauth : Allowing for custom authenticate methods when fetching access tokens H/T @tom","title":"v3.229.0"},{"location":"changelog/#v32280","text":"http4k-testing-servirtium, Breaking : API is still in beta, so moving to a more composed approach which will increase reuse and allow for running Servirtium infra without a dependency on http4k or Junit. Added loading from GitHub. :) http4k-security-oauth, Breaking : Audience on request object is now a list to support multiple audiences. H/T @tom http4k-security-oauth : Nonce is now also passed through on RequestJwts, so it can be added to request jwts. H/T @tom","title":"v3.228.0"},{"location":"changelog/#v32270","text":"http4k-core : Implmement #340. Support SameSite cookies. H/T @danielwellman for the contribution. http4k-format-jackson : Made JacksonJsonPropertyAnnotated Kotlin 1.4 safe (call to superclass might return null). H/T @pyos for spotting this.","title":"v3.227.0"},{"location":"changelog/#v32260","text":"http4k-testing-servirtium : Moved Servirtium code to new module - was previously [http4k-incubator].","title":"v3.226.0"},{"location":"changelog/#v32250","text":"http4k-incubator : Rewrote Servirtium code to support manipulations.","title":"v3.225.0"},{"location":"changelog/#v32240","text":"http4k-security-oauth : Fix issue where AuthRequestWithRequestAuthRequestExtractor doesn't take into account scopes not being nullable correctly. H/T @tom","title":"v3.224.0"},{"location":"changelog/#v32230","text":"http4k-security-oauth : Adding expiry to RequestObject . H/T @tom http4k-security-oauth : Fixing issue where unknown fields cause extracting RequestObject from a jwt, fails due to unknown fields. H/T @tom","title":"v3.223.0"},{"location":"changelog/#v32220","text":"http4k-security-oauth, Breaking : Error responses in the authorise endpoint now take into account values from the request parameter, this will require a validator for that jwt be implemented. H/T @tom http4k-security-oauth, Breaking : State is now its own type, and not just a string, so it can be validated. H/T @tom http4k-security-oauth, Breaking : redirectUri on AuthRequest is now nullable as it might come on a request jwt, this is validated to be always be present downstream. H/T @tom http4k-security-oauth : Allow parsing of request jwt. H/T @tom http4k-security-oauth : Adding RequestObject to AuthRequest . H/T @tom http4k-security-oauth : Adding AuthRequestWithRequestAuthRequestExtractor that will extract the request from the jwt, assuming the validator is implemented which can be used instead of just using AuthRequestFromQueryParameters if support for parsing a request jwt is required. H/T @tom","title":"v3.222.0"},{"location":"changelog/#v32210","text":"http4k-*, Unlikely break from Java only : Make all custom http4k exceptions extend RuntimeException. This helps with Java compatibility so things like LensFailure inside Java Lambdas don't require catching (as they are caught/dealt with by other bits of http4k automatically)","title":"v3.221.0"},{"location":"changelog/#v32200","text":"http4k-moshi, Behaviour break : Fix #353 Don't fail by default on unknown properties. This is the expected default behaviour for all JSON implementations. H/T cnusp for the report.","title":"v3.220.0"},{"location":"changelog/#v32190","text":"http4k-incubator : Next iteration of Servirtium JUnit extensions. Improved API to support multiple storage engines.","title":"v3.219.0"},{"location":"changelog/#v32180","text":"http4k-incubator : Next iteration of Servirtium JUnit extensions. Correct indexing of interactions. http4k-security-oauth : Authorisation rendering will now taking into account 'response_mode' of either query or fragment in responses and no longer just use the default fo the 'response_type'. H/T @tom http4k-security-oauth, Breaking : Error responses in the authorise endpoint will actually redirect back to ' redirect_uri' assuming the validator correctly validates both the 'client_id' and 'redirect_uri' to be valid. H/T @tom","title":"v3.218.0"},{"location":"changelog/#v32170","text":"http4k- * : Upgrade some dependency versions. http4k-incubator : Next iteration of Servirtium JUnit extensions. Only check content which is in the contract when replaying.","title":"v3.217.0"},{"location":"changelog/#v32160","text":"http4k-core, Breaking : Removed clashing Events then() from deprecated (meaning it cannot be used as there is also another then() in that package). Use the one in org.http4k.events instead. http4k-security-oauth : Adding nonce to AuthorizationCodeDetails H/T @tom","title":"v3.216.0"},{"location":"changelog/#v32150","text":"http4k-core : GZip client filters now send correct accept-encoding header. @jshiell http4k-core : New AcceptGZip client filter allows handling of remote GZip without compressing client requests. @jshiell","title":"v3.215.0"},{"location":"changelog/#v32140","text":"http4k-core : Fix #344 H/T Streaming GZip encoder loses data. @jshiell","title":"v3.214.0"},{"location":"changelog/#v32130","text":"http4k-security-oauth : Fixing wrong AuthRequestExtractor passed to AuthRequestTrackingFilter. H/T @tom","title":"v3.213.0"},{"location":"changelog/#v32120","text":"http4k-security-oauth : allowing additional properties to be stored on auth request, if using additional extractors H/T @tom","title":"v3.212.0"},{"location":"changelog/#v32110","text":"http4k-core : Fixes for #338 - Gzip filters send content-encoding of gzip even when body is empty. H/T @jshiell http4k-security-oauth, Break : OIDC callback urls using the ResponseType 'code id_token' will now have the parameters returned as a fragment not a query as per 3.3.2.5 of the OpenID Connect Core 1.0 spec H/T @tom http4k-security-oauth, Break : Initial support of nonce in OIDC requests H/T @tom","title":"v3.211.0"},{"location":"changelog/#v32100","text":"http4k-core : Support for GZipping response streams. H/T @jshiell http4k-security-oauth : Adding expires_in to token endpoint response. H/T @tom","title":"v3.210.0"},{"location":"changelog/#v32090","text":"http4k- * : Added Status to auto-marshalling JSON mappings. http4k-security-oauth : Adding token_type to token endpoint response, and strip out nulls in response. H/T @tom","title":"v3.209.0"},{"location":"changelog/#v32080","text":"http4k- * : Upgrade some dependency versions. http4k-core : PR #333. Copy zipkin traces across threads. H/T @jshiell for the PR. http4k-testing-approval : Close Readers when reading from them. http4k-incubator : Next iteration of Servirtium JUnit extensions for recording and replaying.","title":"v3.208.0"},{"location":"changelog/#v32070","text":"http4k- * : Upgrade some dependency versions http4k-incubator : Added first cut of Servirtium classes for recording and replaying traffic. Needs validating in the wild http4k-format-jackson : Fix #320. http4k-format-jackson incompatible with jackson-module-kotlin 2.10.1","title":"v3.207.0"},{"location":"changelog/#v32060","text":"http4k- * : Upgrade some dependency versions. http4k-contract : Fix #323. Doc generation does not work with multipart lenses. http4k-format-jackson : Fix #313. Jackson serialization is not working properly with polymorphic types stored in a collection. H/T @alphaho for the PR :) http4k-core, Break : Renamed value on ParamMeta to description .","title":"v3.206.0"},{"location":"changelog/#v32050","text":"http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.61 http4k-security-oauth : allowing setting scopes on AccessToken creation so they are set on the response. H/T @tom","title":"v3.205.0"},{"location":"changelog/#v32040","text":"http4k-core, http4k-aws : - increase efficiency of Hex implementation for trace ids and HMAC. H/T @time4tea http4k-cloudnative : Reimplemented Environment to be more efficient. H/T @time4tea for noticing this.","title":"v3.204.0"},{"location":"changelog/#v32030","text":"http4k-security-oauth : On generating tokens allowing for the client id to be based on the result of validation rather than just the form parameters of the request. To support client assertions. H/T @tom","title":"v3.203.0"},{"location":"changelog/#v32020","text":"http4k-security-oauth : Adding new errors to support issues with client assertions. H/T @tom","title":"v3.202.0"},{"location":"changelog/#v32010","text":"http4k-security-oauth : Allowing a scope to be set on AccessToken. Allowing for more low level validation of Authorise and Token Requests, by implementing org.http4k.security.oauth.server.AuthoriseRequestValidator and org.http4k.security.oauth.server.accesstoken.AccessTokenRequestAuthentication respectively. H/T @tom","title":"v3.201.0"},{"location":"changelog/#v32000","text":"http4k-contract : Support multiple request bodies in OpenApi v3","title":"v3.200.0"},{"location":"changelog/#v31991","text":"http4k-format-jackson : Fix #313 Part 2 - Revert default behaviour for collections of polymorphic types, but is now overridable by using autoBody() instead of auto() . Reopened #313.","title":"v3.199.1"},{"location":"changelog/#v31990","text":"http4k-format-jackson, Breaking : Fix #313 - ConfigurableJackson.autoBody implementation would not work with collections of polymorphic types. This fix has the effect of blowing up auto-json behaviour when classes are defined inside functions (causing nasty java.lang.reflect.GenericSignatureFormatError: Signature Parse error exceptions). To remedy, just move inlined classes outside of the functions that they are defined in. H/T @alphaho for the PR. http4k- * : Update some dependency versions","title":"v3.199.0"},{"location":"changelog/#v31980","text":"http4k-core, Breaking : Reworking of ContentType to support multiple directives. directive field is now directives , so just add the extra 's' to fix :) http4k-security-oauth : Moar options on OAuthProviderConfig . H/T @tom","title":"v3.198.0"},{"location":"changelog/#v31970","text":"http4k- * : Update some dependency versions, including Kotlin to 1.3.60 . http4k-core : Make Query value optional when setting on a Request . http4k-core, Breaking : Fix #316. Optional Query lens handling is more accurate. See issue for details of change in behaviour.","title":"v3.197.0"},{"location":"changelog/#v31960","text":"http4k- * : Update some dependency versions. http4k-format-jackson, http4k-format-gson : Add support for auto marshalling Throwable in a sensible way. http4k-cloudnative : Renamed badly named UpstreamRequestFailed to RemoteRequestFailed . Improved error handling.","title":"v3.196.0"},{"location":"changelog/#v31951","text":"http4k-cloudnative : Fix adding value to overridden environment when using set() . H/T @jippeholwerda for the PR","title":"v3.195.1"},{"location":"changelog/#v31950","text":"http4k-security-oauth : Tweak to handle Content-Type comparisons (with and without directive). H/T @jippeholwerda for the PR http4k-multipart] - [Breaking : Added support for setting custom headers in Multipart form fields and files. This has removed the String as the default field type (it is now MultipartFormField . Calls to create lenses using MultipartFormField will now require MultipartFormField.string() instead.","title":"v3.195.0"},{"location":"changelog/#v31940","text":"http4k-contract : Useful tweaks to the contracts API","title":"v3.194.0"},{"location":"changelog/#v31931","text":"[http4k-cloudnative] Fix #304 - map get() does not respect fallback values in overridden environment.","title":"v3.193.1"},{"location":"changelog/#v31930","text":"http4k-contract : Marking endpoints as deprecated in OpenApi3","title":"v3.193.0"},{"location":"changelog/#v31920","text":"http4k-template-jade4j : [New module] H/T @RichyHBM for the contribution! :)","title":"v3.192.0"},{"location":"changelog/#v31910","text":"http4k-contract : Better support for overriding of raw map definition id in JSON schema generation","title":"v3.191.0"},{"location":"changelog/#v31900","text":"http4k-core : Added method to (immutably) modify status on Response . H/T @brandon-atkinson for the suggestion http4k-core : Added composite object support to lens system, allowing creation of simple lenses which draw from several different values (of the same location only - e.g Query/EnvironmentKey) http4k-contract : Support for overriding the entity definition id in JSON schema generation http4k- * : Update some dependency versions.","title":"v3.190.0"},{"location":"changelog/#v31890","text":"http4k-server-netty : Fix reported port in Netty . H/T @fantayeneh for the PR :) http4k-security-oauth : Add validateScopes() to ClientValidator . H/T @tom","title":"v3.189.0"},{"location":"changelog/#v31880","text":"http4k-contract : Support multiple-response models in OpenApi2 and 3. Note that this currently is unsupported in the OpenApi UI due to a bug (which doesn't display the schema for the response correctly). However, the JSON schema is generated correctly in these cases. http4k- * : Update some dependency versions.","title":"v3.188.0"},{"location":"changelog/#v31870","text":"http4k- * : Update some dependency versions, and changes to various APIs involved (Jackson and Resilience4J) http4k-core : - Add YearMonth support to standard JSON mappings http4k-format-jackson, http4k-format-gson, Possible break : - Moved reified NODE.asA() method from JsonLibAutoMarshallingJson down onto the instances of the Json ( ConfigurableJackson / ConfigurableGson ). This is so that we can handle generified classes such as lists and maps correctly. (As per the problems fixed in 3.181.0)","title":"v3.187.0"},{"location":"changelog/#v31860","text":"http4k-core : - Rollback a couple of places which were using Java9+ APIs (for no good reason).","title":"v3.186.0"},{"location":"changelog/#v31850","text":"http4k-contract : Improvements to rendering enums as their own objects in JSON Schema.","title":"v3.185.0"},{"location":"changelog/#v31840","text":"http4k-contract : Add Cookies options to contract DSL","title":"v3.184.0"},{"location":"changelog/#v31830","text":"http4k-serverless-lambda : Add ability to access Lambda context. H/T @ivoanjo for the PR. http4k-contract : Fix rendering of OrSecurity when there are more than 2 parts.","title":"v3.183.0"},{"location":"changelog/#v31820","text":"http4k-core : Rename EventsFilter to EventFilter because sanity. http4k-format-jackson, http4k-format-gson : Reintroduce autoBody() method","title":"v3.182.0"},{"location":"changelog/#v31810","text":"http4k-core : Added base events implementations for StructuredLogging. http4k-core, Repackage : Events classes are now in org.http4k.events . http4k-core, Breaking : EventCategory is no longer a field of Event . To fix, just remove override from your Event classes. http4k-format-jackson, http4k-format-gson : Fixed problem when attempting to deserialise generic Lists.","title":"v3.181.0"},{"location":"changelog/#v31800","text":"http4k- * : Update various dependencies. http4k-testing-hamcrest : Improve messages of Hamkrest matchers. H/T @albertlatacz http4k-cloudnative : Fix #291 - Readiness check result when there are > 2 checks may not report the correct result. H/T @alfi http4k-security-oauth, Possibly breaking : Making client_secret optional in AuthorizationCodeAccessTokenRequest to support non client_secret flows. H/T @tom","title":"v3.180.0"},{"location":"changelog/#v31791","text":"http4k-client-okhttp : Include status description in Response object.","title":"v3.179.1"},{"location":"changelog/#v31790","text":"http4k-contract : Added OpenApiExtension interface, which allows the definition of extensions that will modify the OpenApi specification JSON. H/T @rgladwell for the inspiration. http4k-contract : Support composite security models using or() and and() . Once again, H/T @rgladwell :)","title":"v3.179.0"},{"location":"changelog/#v31780","text":"http4k-security-oauth, Possibly breaking : Request is passed as a parameter to the ClientValidator. Just pass it in! :) H/T @tom http4k-contract, Behaviour change : When specified, individual route security now replaces global security (this is as the security model in the OpenApi spec is specified) as opposed to both being applied.","title":"v3.178.0"},{"location":"changelog/#v31770","text":"http4k-security-oauth, Possibly breaking : More support for OIDC, adding state to AuthorizationCodeDetails, and passing it into createForAccessToken on IdTokens. H/T @tom","title":"v3.177.0"},{"location":"changelog/#v31760","text":"http4k-security-oauth : More support for OIDC. H/T @tom","title":"v3.176.0"},{"location":"changelog/#v31750","text":"http4k- * : Update various dependencies, including Kotlin to 1.3.50. http4k-security-oauth : Some support for OIDC. H/T @tom","title":"v3.175.0"},{"location":"changelog/#v31740","text":"http4k- * : Update various dependencies, including Jackson for a CVE.","title":"v3.174.0"},{"location":"changelog/#v31730","text":"http4k-core : Fix #273 - parentSpanId trace incorrectly populated when no previous traces http4k-contract, Unlikely Break : Remodelled how Security is rendered, so it's possible that this may break slightly for customer implementations http4k-contract : Added support for Implicit OAuth flow, with suport for custom googleCloudEndpoints Security. H/T @rgladwell","title":"v3.173.0"},{"location":"changelog/#v31720","text":"http4k-core : Added uni-directional serialization/deserialization options to JSON lib auto-conversion configuration.","title":"v3.172.0"},{"location":"changelog/#v31710","text":"http4k-core, Break (mitigation) : Replaced default resource loader location for singlePageApp() to /public instead of root - this is for safety of NOT serving the root of the classpath by default.","title":"v3.171.0"},{"location":"changelog/#v31700","text":"http4k-core : Add a warning when static() is used with no package path, thus exposing the contents of the classpath remotely.","title":"v3.170.0"},{"location":"changelog/#v31690","text":"http4k- * : Update various dependencies.","title":"v3.169.0"},{"location":"changelog/#v31680","text":"http4k-contract : Collect LensFailure causes into a single place when validating.","title":"v3.168.0"},{"location":"changelog/#v31670","text":"http4k-contract, Possibly Break : Open out ErrorResponseRenderer interface to take LensFailure instead of the individual failures when rendering badResponse() . To fix, simply wrap the list of failures into a LensFailure.","title":"v3.167.0"},{"location":"changelog/#v31661","text":"http4k-core : Tweak singlePageApp() routing handler, to correctly apply filters when fallback page is used.","title":"v3.166.1"},{"location":"changelog/#v31660","text":"http4k-core : Added singlePageApp() routing handler, which matches both static content or falls back to the root path index file","title":"v3.166.0"},{"location":"changelog/#v31650","text":"http4k-contract : Fix invalid OpenApi2 when root and base path match. H/T @rgladwell http4k-contract : ContractRoute is now an HttpHandler , so no need to wrap contract routes in a contract {} to test them. H/T @rgladwell for the inspiration. http4k-contract : Support Host/baseUri values in OpenApi2. H/T @rgladwell http4k-contract : Optionally add description route to route list H/T @rgladwell","title":"v3.165.0"},{"location":"changelog/#v31640","text":"http4k- * : Update various dependencies, including Kotlin to 1.3.41. http4k-testing-approval : Upgrade of HTML library from above may have an effect on output of HTML approval tests. http4k-contract : Support for more Jackson annotations in JSON Schema rendering. H/T @tom for the PR contributing this.","title":"v3.164.0"},{"location":"changelog/#v31630","text":"http4k-testing-chaos : Add detail to Chaos OpenApi interface.","title":"v3.163.0"},{"location":"changelog/#v31620","text":"http4k-testing-chaos : Add detail to Chaos OpenApi interface.","title":"v3.162.0"},{"location":"changelog/#v31610","text":"http4k-cloudnative : Added Forbidden request exception to HandleUpstreamRequestFailed.","title":"v3.161.0"},{"location":"changelog/#v31601","text":"http4k-testing-chaos : Countdown chaos trigger fixed.","title":"v3.160.1"},{"location":"changelog/#v31600","text":"http4k-testing-chaos : Slight fix to avoid consuming stream body when setting chaos.","title":"v3.160.0"},{"location":"changelog/#v31590","text":"http4k- * : Update various dependencies. http4k-client-okhttp : Updated OkHttp to v4.0.0 (Kotlin edition). http4k-contract : Tweak to JSON Schema rendering to handle recursive objects better.","title":"v3.159.0"},{"location":"changelog/#v31581","text":"http4k-server-netty : Fix #260 - cannot set multiple response headers with same name http4k-server-undertow : Fix #260 - cannot set multiple response headers with same name","title":"v3.158.1"},{"location":"changelog/#v31580","text":"http4k-contract : POSSIBLE BEHAVIOUR CHANGE DUE TO BUG: Fix #259 - Contract blocks do not produce 400s if an external CatchAll is provided. This may have an effect on how errors are generated (a 400 is produced instead of the previous 500 from the CatchAll).","title":"v3.158.0"},{"location":"changelog/#v31571","text":"http4k-security-oauth : Fix broken deprecation annotation.","title":"v3.157.1"},{"location":"changelog/#v31570","text":"http4k-security-oauth : Default to JSON format response in Access Token response http4k-security-oauth : Renamed a couple of classes (AccessTokenContainer -> AccessToken), and removed isValid method from AuthorizationCodes because it doesn't make sense for this to be on the OAuthServer.","title":"v3.157.0"},{"location":"changelog/#v31560","text":"http4k- * : Update Kotlin to 1.3.40 http4k-contract : Support OAuthSecurity renderer.","title":"v3.156.0"},{"location":"changelog/#v31552","text":"http4k- * : Update various dependencies. http4k- * : Dokka improvements. Does not mitigate #196 as we run the main build on OpenJdk11. H/T @ivoanjo","title":"v3.155.2"},{"location":"changelog/#v31551","text":"DO NOT USE - broken","title":"v3.155.1"},{"location":"changelog/#v31550","text":"DO NOT USE - broken","title":"v3.155.0"},{"location":"changelog/#v31541","text":"http4k-multipart : Made the multipart header parser case-insensitive. H/T @tenniscp25","title":"v3.154.1"},{"location":"changelog/#v31540","text":"http4k-contract : Add SchemaModelNamer to allow for custom JSON Schema model names.","title":"v3.154.0"},{"location":"changelog/#v31530","text":"http4k-contract : OperationIds are generated without illegal characters {} .","title":"v3.153.0"},{"location":"changelog/#v31520","text":"http4k-contract : Support non-string keys for \"text convertible\" values in maps for Auto-schema generation.","title":"v3.152.0"},{"location":"changelog/#v31510","text":"http4k-contract : Fixed Auto-schema generation to detect and remove duplicate items from list schemas.","title":"v3.151.0"},{"location":"changelog/#v31500","text":"http4k-security-oauth : Make authentication mechanism for grant types configurable.","title":"v3.150.0"},{"location":"changelog/#v31490","text":"http4k-security-oauth : Initial support for client_credentials grant type.","title":"v3.149.0"},{"location":"changelog/#v31480","text":"http4k-contract : Jackson property searching in OpenApi3 now searches superclasses.","title":"v3.148.0"},{"location":"changelog/#v31470","text":"http4k-contract : Support custom JsonProperty annotation for OpenAPi3 generation http4k-cloudnative : New exception type for unuathorised. H/T @tom","title":"v3.147.0"},{"location":"changelog/#v31460","text":"http4k-contract : Fix #228 - Support Map-based fields in OpenApi 3 Auto-schema generation as additionalProperties . H/T @noahbetzen-wk for the idea.","title":"v3.146.0"},{"location":"changelog/#v31450","text":"http4k-contract : Reimplement Auto-schema generation using reflection. Added test cases to use the OpenApi generator to create valid code-based OpenApi clients using the OpenApi generator. http4k-format-jackson : Removed reflective JSON schema creator, since it was not actually OA3 compliant.","title":"v3.145.0"},{"location":"changelog/#v31440","text":"http4k- * : Update various dependencies. http4k-contract : Improvements to better adhere to OA3 spec. http4k-security-oauth : Allow injecting OpenID's request parameter into the authorization request. http4k-security-oauth : Expose request to AuthRequestTracking.","title":"v3.144.0"},{"location":"changelog/#v31431","text":"http4k-core : Replace RequestContexts with reference to Store . H/T @amcghie http4k-contract : Added some missing deprecations. http4k-contract : Fix #243 - Nulls not allowed in OpenApi V3 JSON models.","title":"v3.143.1"},{"location":"changelog/#v31430","text":"http4k-contract : Fix #239 - OpenApi v3 schemas for raw lists blow up when rendering. http4k- * : Update various dependencies.","title":"v3.143.0"},{"location":"changelog/#v31420","text":"http4k-contract : Both OpenApi v2 and v3 are now supported, including automatic schema generation. Some classes for OpenApi2 have moved to a new package - Deprecations should provide most alternatives. See module docs for details. For OpenApi v3, optionally include http4k-format-jackson to get JSON schema models based on JVM objects. http4k-format-jackson : Added reflective JSON schema creator, to be used for generating named models from JVM objects.","title":"v3.142.0"},{"location":"changelog/#v31410","text":"http4k-core : - Fix #233 - MemoryBody blows up with \"java.nio.ReadOnlyBufferException\" http4k-core : - Tighten up security on Basic and Bearer auth server filters. H/T @andymoody http4k-security-oauth : - Add filter to check bearer token is valid access token. H/T @andymoody","title":"v3.141.0"},{"location":"changelog/#v31400","text":"http4k- * : Update dependencies (including Kotlin bump to 1.3.31) http4k-security-oauth : Handle user rejecting/failing authentication. H/T @andymoody","title":"v3.140.0"},{"location":"changelog/#v31390","text":"http4k-security-oauth : Allow access token generation to explicitly reject an authorization code already used. H/T @andymoody","title":"v3.139.0"},{"location":"changelog/#v31381","text":"http4k-security-oauth : Amend error responses from access token generation. H/T @andymoody","title":"v3.138.1"},{"location":"changelog/#v31380","text":"http4k-contracts : Tweaks to Security model for http4k-contracts . (Renamed) ApiKeySecurity is now a proper class, and added BasicAuthSecurity . You can now also override the security model on a per-route basis. http4k-contract : Added ability to set the Security on each individual contract route. This overrides any Security set on a contract-level basis.","title":"v3.138.0"},{"location":"changelog/#v31371","text":"http4k-serverless : Allow invocation of serverless functions locally. H/T @Charlyzzz http4k-core : Fix #226 - ResourceLoadingHandler not close stream","title":"v3.137.1"},{"location":"changelog/#v31370","text":"http4k-security-oauth : Rename AuthRequestPersistence to AuthRequestTracking","title":"v3.137.0"},{"location":"changelog/#v31360","text":"http4k-security-oauth : Allow the http request to be referenced when generating OAuth authorization codes. H/T @andymoody","title":"v3.136.0"},{"location":"changelog/#v31350","text":"http4k-core : Change mime.types location so it doesn't conflic with other libraries. H/T @benusher and @dgliosca http4k-testing-chaos : Added SnipRequestBody behaviour. http4k-core : (Small) Breaking Fixed location of some extension files to be relevant to the particular package that they are referencing. This will require reimporting the new location into your source if you were using the imports.","title":"v3.135.0"},{"location":"changelog/#v31340","text":"http4k-testing-approval : Made content-type aware approval tests check the content type after the content. This is friendlier for failing tests, as it is more important that the content is correct than the content-type (and often errors don't have content type set so you get an erroneous error message which masks the fact that the content was wrong).","title":"v3.134.0"},{"location":"changelog/#v31330","text":"http4k-cloudnative : HandleUpstreamRequestFailed client filter now takes a predicate (Response) -> Boolean instead of a boolean. This allows for more fine grained custom control of which Responses are acceptable. http4k- * : Upgrade deps, including Kotlin to 1.3.30 . http4k-contract : Fix #221 - Contract path fixed segments cannot contain slash characters.","title":"v3.133.0"},{"location":"changelog/#v31320","text":"http4k-format-jackson : Convert Jackson to use readValue instead of convertValue . This fixes some problems with type conversions.","title":"v3.132.0"},{"location":"changelog/#v31310","text":"http4k-core : (Possible) Break: Made lense implementations Query, Header etc clear previous values by default instead of appending. This leads to a more consistent behaviour. In order to be able to set multiple values on an object using a lense, use the multi form instead - eg. Header.required(\"foo\") -> Header.multi.required(\"foo\") . We envisage the impact of this change is limited as it's only Queries that generally can have multiple possible values, and in the vast majority of cases a replace rather than append is expected.","title":"v3.131.0"},{"location":"changelog/#v31300","text":"http4k-contract : Generify contract handling code to allow for custom HttpMessageMeta","title":"v3.130.0"},{"location":"changelog/#v31290","text":"(Slight) Break: Collapsed UpstreamRequestFailed exceptions to contain the status, and thus removing non-special cases like BadRequest and BadGateway . This makes them much easier to use in practice as users have access to the the status. To migrate, simply replace previous classes with UpstreamRequestFailed(Status.XYZ, message) . http4k-contract : Open up ContractRoute API to facilitate extension when defining a custom ContractRenderer . http4k- * : Upgrade deps.","title":"v3.129.0"},{"location":"changelog/#v31280","text":"http4k-core : Added base64 to the supported mappings for Query/Headers etc... http4k-testing-approval : Approver does not write actual output if there is none to write and there is no approved content","title":"v3.128.0"},{"location":"changelog/#v31270","text":"http4k-testing-approval : Improved Approver interface to more closely match the traditional assert approach - this results in a more discoverable/obvious API. http4k-testing-hamkrest : Added ability to create a Hamkrest matcher directly from the Approver instance to be combined with other relevant matchers.","title":"v3.127.0"},{"location":"changelog/#v31260","text":"http4k-testing-approval : Add support for XML and HTML approval tests.","title":"v3.126.0"},{"location":"changelog/#v31250","text":"Added http4k-testing-approval module, which is compatible with JUnit5 tests and integrates with the OkeyDoke approval testing files and IntelliJ plugin. H/T to @jshiell for the inspiration Gist containing the base Junit5 Extension.","title":"v3.125.0"},{"location":"changelog/#v31240","text":"http4k-security-oauth : Make authentication response available when creating AuthorizationCode.","title":"v3.124.0"},{"location":"changelog/#v31230","text":"http4k-security-oauth : Introduce OAuthServer to http4k-security-oauth to assist in the creation of authorization servers.","title":"v3.123.0"},{"location":"changelog/#v31220","text":"Generified GenerateXmlDataClasses filter, and added default implementations for http4k-format-jackson-xml and http4k-format-xml modules. (Rename) Break: GenerateXmlDataClasses filter in http4k-format-xml is now GsonGenerateXmlDataClasses Removed superfluous CatchLensFailure filter from http4k-contracts module. This is not required as lens failures are already handled by the main contract handler.","title":"v3.122.0"},{"location":"changelog/#v31210","text":"Moved Jackson XML support to new module http4k-format-jackson-xml . Note that this is for auto-marshalling of data-classes only and does not expose an XML DOM model.","title":"v3.121.0"},{"location":"changelog/#v31200","text":"Deprecated Body.view() lens construction in favour of a Body.viewModel() call which removes the implicitly called toLens() . This allows further mapping from one ViewModel type to another, and brings the view lens construction into line with the rest of the extension functions on Body . Add auto-marshalling XML support to http4k-format-jackson module. Upgrade deps.","title":"v3.120.0"},{"location":"changelog/#v31190","text":"Add UpstreamRequestFailed exceptions and HandleUpstreamRequestFailed filters to http4k-cloudnative . These allow apps to neatly deal with upstream failure in a sensible way.","title":"v3.119.0"},{"location":"changelog/#v31180","text":"Tweak contract() DSL to add remaining options for configuration.","title":"v3.118.0"},{"location":"changelog/#v31170","text":"Renamed ChaosControls (deprecated) to ChaosEngine .","title":"v3.117.0"},{"location":"changelog/#v31160","text":"Added new templating module http4k-templates-freemarker . H/T @amcghie for the PR implementing this http4k-contract has a new DSL for construction of the contract which replaces the old one (now deprecated). This is consistent with the meta DSL used to construct individual contract routes and avoids repetition of the old API. We attempted to implement the standard replace-with deprecation, but IntelliJ didn't like it (too complex maybe), so we've hard coded the warning instead which code which should work. Added PreFlightExtraction to contract module, which adds the ability to disable body-checking for contract routes. This will allow refining of routes or entire contracts to be more efficient. Upgrade deps.","title":"v3.116.0"},{"location":"changelog/#v31151","text":"Fix #217 - Cannot override the definitionId of a top-level array in OpenAPI Upgrade deps","title":"v3.115.1"},{"location":"changelog/#v31150","text":"Chaos now do not blat x-uri-template when used with a RoutingHttpHandler Simplified usage of Once chaos trigger. (Slight break) Consistentified (!) construction of Chaos Behaviours, Stages and Triggers. Replaced singletons with function calls. Eg. Always -> Always()","title":"v3.115.0"},{"location":"changelog/#v31140","text":"(Possible Break): Fix #215 - LensFailure does not always include target object. Only change to the API is that IN generic in Lenses is now bounded by IN : Any . This fix is a actually internally consistent as we could not always include the target otherwise (which is an Any? ). Trim leading and trailing whitespace from extracted EnvironmentKey values. Secret value is now only usable once via the use() function. Upgrade to various deps. Removed deprecations.","title":"v3.114.0"},{"location":"changelog/#v31130","text":"Added some common types for Environmental setup, and equivalent BiDiLens mappings Handle null response in Java Http client. H/T @FredNordin","title":"v3.113.0"},{"location":"changelog/#v31122","text":"Fix #212 - allow null values in HTTP contract definitions. This does mean we lose the type definition for that field, but we don't blow up silently (which was the previous behaviour). H/T @xhanin","title":"v3.112.2"},{"location":"changelog/#v31121","text":"Re-add Path.nonEmptyString() which was accidentally removed.","title":"v3.112.1"},{"location":"changelog/#v31120","text":"Add support for prohibiting String unmarshalling in JSON auto-marshalling configuration. HTTP Contracts now use the underlying ContractRenderer to produce the BadRequest and NotFound responses. Made OpenAPI open so that these responses can be customised.","title":"v3.112.0"},{"location":"changelog/#v31110","text":"Add support for JSON views in Jackson module. H/T @xhanin for the donkey work.","title":"v3.111.0"},{"location":"changelog/#v31100","text":"Breaking: slight rearrangement of RouteMeta receiving/returning methods to provide consistency when defining route contracts.","title":"v3.110.0"},{"location":"changelog/#v31090","text":"Moved the set of predefined String BiDiMapping instances to their own class. Bulked out the auto-mapping configuration options.","title":"v3.109.0"},{"location":"changelog/#v31080","text":"Upgrade to various deps. Extracted out new BiDiMapping type, which encapsulates string <-> type conversions and removes a boatload of duplications. These conversions are now used consistently across all the various places (Lenses, auto-mapping). Improved configurability of AutoMarshallingJson instances.","title":"v3.108.0"},{"location":"changelog/#v31070","text":"Upgrade to various deps. Fix #208 - Xml auto deserialisation incorrectly converting strings to numbers","title":"v3.107.0"},{"location":"changelog/#v31061","text":"Fix #207 - repeating prefixes in static routes are not handled correctly. H/T @ruXlab for the PR to fix.","title":"v3.106.1"},{"location":"changelog/#v31060","text":"Add http4k-server-ktorcio server backend. Note that whilst this module does allow http4k apps to plug into the Ktor-CIO engine, it does not provide fully front-to-back coroutine support.","title":"v3.106.0"},{"location":"changelog/#v31050","text":"Preventing FallbackCacheControl from duplicating existing headers. H/T @leandronunes85 Breaking: Make Body.length nullable instead of throwing exception when value is not available. H/T @zvozin","title":"v3.105.0"},{"location":"changelog/#v31040","text":"Upgrade to various deps. Add session token support to AWS filter, and \"credentials provider\" to allow for rotating AWS sessions. H/T @dhobbs. Breaking: Moved WsClient from org.http4k.testing to org.http4k.websocket .","title":"v3.104.0"},{"location":"changelog/#v31032","text":"Fix access-control-allow-origin returned when server supports multiple origins H/T @johnnorris","title":"v3.103.2"},{"location":"changelog/#v31031","text":"(Properly) Fix #198 - Rewrote OpenApi contract to ensure it stays fixed. H/T @reik-wargaming for the help in tracking this down.","title":"v3.103.1"},{"location":"changelog/#v31030","text":"\"Fix\" #198 - Breaking change made in http4k-contracts to clarify/deconfuse API. Hid body parameter in contract route meta DSL - it is now receiving() . Upgraded some dependencies, including Gradle to v5.0. Breaking: Resilience4j dependency upgrade causes a break when providing custom config. Simply insert the Config type generic to fix: e.g. RetryConfig.custom() -> RetryConfig.custom()","title":"v3.103.0"},{"location":"changelog/#v31021","text":"Fix #197 - Swagger spec for form fields had incorrect description.","title":"v3.102.1"},{"location":"changelog/#v31020","text":"Introduce interface for Environment","title":"v3.102.0"},{"location":"changelog/#v31010","text":"Upgrades to dependencies Improved Client-side HTTP status descriptions Lenses now support Durations out of the box Environments now support multi-value keys (comma separated)","title":"v3.101.0"},{"location":"changelog/#v31000","text":"Make Undertow API friendlier Fix to JsonReadinessCheckResultRenderer to actually implement the correct interface","title":"v3.100.0"},{"location":"changelog/#v3990","text":"Enhancement of http4k-cloudnative - now supports extra-health check routes, and provide way to load app configuration via Properties files.","title":"v3.99.0"},{"location":"changelog/#v3980","text":"Add filter allowing Gzipping based on an allowed set of content types. H/T @jshiell Change HttpHandler extending HttpClients to use object invoke() mechanism, as the individual clients have no visible API surface of their own. Introduced DualSyncAsyncHttpHandler interface.","title":"v3.98.0"},{"location":"changelog/#v3970","text":"Webdriver checkbox handling improved. H/T @gypsydave5 upgrade to various versions","title":"v3.97.0"},{"location":"changelog/#v3960","text":"upgrade to Kotlin 1.3.0","title":"v3.96.0"},{"location":"changelog/#v3951","text":"Tweak to K8S port variables.","title":"v3.95.1"},{"location":"changelog/#v3950","text":"(Unlikely break): Change Http4kServer interface to return Unit from stop() . This affects all server implementations. Added DSL function for working with JSON objects (scopes JSON as this ). fun Json.invoke(Json.() -> T) New module http4k-cloudnative contains classes to help run http4k services inside cloud-native environments, including K8S. Upgrade some dependencies Deprecation: Moved Header.Common fields to main Header object. Extension properties should go there now.","title":"v3.95.0"},{"location":"changelog/#v3941","text":"Use UTC when checking cookie expiry","title":"v3.94.1"},{"location":"changelog/#v3940","text":"Deprecate String.toBody() Fix checkbox behaviour in webdriver","title":"v3.94.0"},{"location":"changelog/#v3394_v3934","text":"Use Jetty latest release version (rather than RC one)","title":"~v3.39.4~ v3.93.4"},{"location":"changelog/#v3393","text":"Fix #189 - Uri toString now omits leading slash if the authority of a Uri is blank. This could be a potential break, but is actually more consistent as a Uri can currently be relative or absolute.","title":"v3.39.3"},{"location":"changelog/#v3392","text":"Extend SetBaseUriFrom to support query parameters","title":"v3.39.2"},{"location":"changelog/#v3391","text":"Added SetBaseUriFrom filter","title":"v3.39.1"},{"location":"changelog/#v3390","text":"(Possible breaking change): Json is now only generified by a single type parameter instead of 2. For most usages, this type would have been identical anyway, but the upgrade of Argo has finally allowed the removal of this dead generic. Simply replace Json with Json . Added Offset datetime types to all JSON auto-marshalling libraries Build logic for versioning is now in Kotlin. H/T @jmfayard for the PR Upgrade Kotlin, and various other dependencies","title":"v3.39.0"},{"location":"changelog/#v3381","text":"Fix withChaosControls URL pattern so that it matches sub-routes ok on original handler","title":"v3.38.1"},{"location":"changelog/#v3380","text":"Added BearerAuth and BasicAuth implementations which populate RequestContexts . Plus howto example :)","title":"v3.38.0"},{"location":"changelog/#v3371","text":"Fix #177 - Make RequestContexts thread-safe.","title":"v3.37.1"},{"location":"changelog/#v3370","text":"Upgrades to http4k-testing-webdriver . H/T @dickon for the PRs Added ProxyHost request filter which is useful for writing proxy-type apps.","title":"v3.37.0"},{"location":"changelog/#v3361","text":"Fix #168 - Fix rest of hamkrest matchers caused by generics mishap. Upgrade HTTP client dependency versions.","title":"v3.36.1"},{"location":"changelog/#v3360","text":"Added http4k-testing-chaos module, designed to enhance failure-mode testing for http4k apps. Massive H/T to @IgorPerikov for the PR which drove this module's creation. Added http4k-incubator module, for hosting developing projects and other code which might be promoted to top-level modules in the future.","title":"v3.36.0"},{"location":"changelog/#v3352","text":"Fix #167 - Reintroduce hasBody compatibility with common matchers such as containsString() Remove deprecations.","title":"v3.35.2"},{"location":"changelog/#v3351","text":"Fix #165 - AWS auth filter does not replace headers - it sets them (which breaks for request signing) Fix #164 - Webdriver internal state breaks when navigating to a full URL Fix #162 - SetHostFrom doesn't set 'Host' header correctly (missing port). H/T @elifarley","title":"v3.35.1"},{"location":"changelog/#v3350","text":"Added some regex matchers to http4k-testing-hamkrest . Added BearerAuth authentication Server and Client Filters - these work similarly to BasicAuth . Added option for defaulted() lenses to fall back to another supplied lens in the case of missing value. Thanks to @dmcg for the inspiration. :)","title":"v3.35.0"},{"location":"changelog/#v3343","text":"Fix #160 - JavaHttpClient does not copy body stream correctly onto URL connection.","title":"v3.34.3"},{"location":"changelog/#v3342","text":"Fix #159 - Contracts should not have Security applied to the description route by default.","title":"v3.34.2"},{"location":"changelog/#v3341","text":"Fix #158 - Static and contract routes filters are applied in the wrong order.","title":"v3.34.1"},{"location":"changelog/#v3340","text":"Add default SamplingDecision param to ZipkinTraces - defaults to always sample. Fix #150 - StaticRoutingHandler filters being called twice. Fix #151 - POTENTIAL BREAK: Rework of Status objects to fix equality against the Status constant vals when a description has been overridden. This involves the following potential breaking change: The Status class is no longer a data class to tighten up encapsulation - user calls to copy() will have to be replaced.","title":"v3.34.0"},{"location":"changelog/#v3332","text":"Raise SO_BACKLOG in Apache and Netty server implementations. Add PERMANENT_REDIRECT and UNPROCESSABLE_ENTITY Status object.","title":"v3.33.2"},{"location":"changelog/#v3331","text":"No change from 3.33.0. Previous version couldn't be made available to maven central.","title":"v3.33.1"},{"location":"changelog/#v3330","text":"Add convenient way to extract from as a Map from http message. H/T to @dmcg (this version is available in jcenter only)","title":"v3.33.0"},{"location":"changelog/#v3321","text":"Fix #142 - Pebble templates don't load from JAR files.","title":"v3.32.1"},{"location":"changelog/#v3320","text":"Add support for propagation of the Zipkin x-b3-sampled header","title":"v3.32.0"},{"location":"changelog/#v3310","text":"Changes to the Netty factory to enable running http4k on GraalVM. H/T @RichyHBM","title":"v3.31.0"},{"location":"changelog/#v3300","text":"Allow all server implementations to start on port 0 (ie. find a free port) and then report it back as a part of the Http4kServer interface","title":"v3.30.0"},{"location":"changelog/#v3290","text":"Make HTTP clients resilient to unknown host and connection refused exceptions Implemented #134 - Added default (de)serialization for common JDK primitives to all Auto-marshalling JSON modules - eg. date times and UUIDs","title":"v3.29.0"},{"location":"changelog/#v3280","text":"Fix #131 - Uri's created with paths that don't contain leading slashes. Added etag parser filter. H/T @dgliosca for the PR Fix #132 - Ensured that disableDefaultTyping is called in default Jackson implementation. This should be the default anyway, but has been added to ensure that we don't fall foul of CVE-2017-7525 and to surface awareness of this issue.","title":"v3.28.0"},{"location":"changelog/#v3270","text":"OpenAPI now provides example values in the generated schema. H/T @skewwhiffy for the PR.","title":"v3.27.0"},{"location":"changelog/#v3266","text":"Fix #126 - ResourceLoadingHandler can expose mapped resources into the root. <-- We think this is an important update, so please upgrade!","title":"v3.26.6"},{"location":"changelog/#v3265","text":"Fix #125 - ApacheServer implementation now sets content length if present.","title":"v3.26.5"},{"location":"changelog/#v3264","text":"Fix #123 - Multipart Body objects blow up when parsed after being debugged. As with all streams, care should be taken to not blow heap when internalising them for debugging purposes.","title":"v3.26.4"},{"location":"changelog/#v3263","text":"Debugging filter now supports ignoring Multipart streams.","title":"v3.26.3"},{"location":"changelog/#v3262","text":"Tweak: OpenAPI now doesn't return null values in the schema.","title":"v3.26.2"},{"location":"changelog/#v3261","text":"Fix #124 - headers in WebSocket upgrade request are incorrectly joined.","title":"v3.26.1"},{"location":"changelog/#v3260","text":"Removed supportedContentTypes field from OpenApi contract JSON, since this is a legacy field.","title":"v3.26.0"},{"location":"changelog/#v3250","text":"Added option to Undertow to enable HTTP2 from main ServerConfig","title":"v3.25.0"},{"location":"changelog/#v3240","text":"Upgrade various dependencies for Java 10 compatibility. H/T @tom Fix bug with repeated params in Websocket upgrade request. H/T @tom","title":"v3.24.0"},{"location":"changelog/#v3231","text":"Composite LensFailures now capture (at least) the first failing cause (probably the body parameter in the case of an http4k-contract module.","title":"v3.23.1"},{"location":"changelog/#v3230","text":"Fix #116 - Can provide a custom Response creation method for CatchLensFailure . H/T @elifarley for the inspiration!","title":"v3.23.0"},{"location":"changelog/#v3224","text":"Added singleton method for Json.array, since if you pass in a single JsonNode (Jackson), it accidentally iterates over the fields in the node instead of using the object as an entry in the array. Fix #115 - Only add content-length for methods that allow content in AwsAuth filter","title":"v3.22.4"},{"location":"changelog/#v3223","text":"Preserve routing information on request/response manipulation","title":"v3.22.3"},{"location":"changelog/#v3222","text":"http4k-security-oauth module added - with support for OAuth2 Authorization Grant flow Replaced classes reliant on javax.activation package, which allows Java 9+ to not require any external dependencies. \\o/ Fix #112 - ApacheClient incorrectly sets headers on GET requests (this breaks F5 load balancers). H/T @simojenki PR #110 - Websocket client timeouts are incorrectly translated as seconds instead of millis. HT @anorth Core JavaHttpClient does not support streaming due to limitations with HttpURLConnection","title":"v3.22.2"},{"location":"changelog/#v3211","text":"Fix #109 - Jackson treats integer values inconsistently, leading to matching errors when using hamkrest.","title":"v3.21.1"},{"location":"changelog/#v3210","text":"Fix #107 - Killed the x-uri-template header and fixed the ReportHttpTransaction to have access to the routingGroup . Altered ordering of filters in http4k-contract so that the route is identified before pre-filters and security are applied. This allows knowledge of the path to be accessible at the time of application of those filters.","title":"v3.21.0"},{"location":"changelog/#v3200","text":"Introduce JavaHttpClient to http4k-core . It provides a very basic http client without any other 3rd party dependencies.","title":"v3.20.0"},{"location":"changelog/#v3190","text":"PR #104 - Add optional time/date formatters to LensSpecs so you can choose you serialisation format. H/T @elifarley Fix #105 - Swagger API json file: duplicate key in \"definitions\".","title":"v3.19.0"},{"location":"changelog/#v3181","text":"Fixed PR #100 - URI template regex required extra escaping. This only affects Android deployments as IDE shows the regex escaping is redundant. H/T @privatwolke","title":"v3.18.1"},{"location":"changelog/#v3180","text":"Breaking: converted contract pre-security filter to be a post-security filter. This means that all standard filters are applied before the security later, which allows for logging and monitoring and context setup. The previous filter mechanic applied security first, which didn't allow for this. In the unlikely event that post-security filters still need to be applied, use the withPostSecurityFilter() function when building the contract. Docs for contract RouteMeta function parameters, and deprecated some unused functions (missed when we introduced the DSL). PR #99 - Contract routes now support up to 10 path segments. Thanks to @scap1784 for the PR! :)","title":"v3.18.0"},{"location":"changelog/#v3171","text":"Fix #97. Moshi does not fail when deserialise non-nullable fields correctly. Note that GSON still suffers from this problem","title":"v3.17.1"},{"location":"changelog/#v3170","text":"Added a pre-security filter option to contract creation, so that you can explicitly specify behaviour to occur before security kicks in.","title":"v3.17.0"},{"location":"changelog/#v3160","text":"Convert Security (from sealed class) and ApiKey to be interfaces. This allows users to implement their own security models.","title":"v3.16.0"},{"location":"changelog/#v3150","text":"Introduce HttpTransaction and new ReportHttpTransaction filter provide better generic API for reporting, along with the ability to label transactions for this purpose. Breaking: Rework the metrics request counter and timer Filter API. There is now a HttpTransactionLabeller for you to add as many labels as required to the transaction. Each of these labels will be used to tag the metric.","title":"v3.15.0"},{"location":"changelog/#v3141","text":"Fix #95 - Filters are now applied to \"route not found\" responses","title":"v3.14.1"},{"location":"changelog/#v3140","text":"Fix #93 - Apache server doesn't like content-length or transfer-encoding headers present in http4k response. Add ability to \"name\" input and output contract body definitions in an OpenAPI JSON doc. This applies to only the top level entity. If no override is passed, the objects are named according to their hashcode.","title":"v3.14.0"},{"location":"changelog/#3134","text":"Fix #92 - cookie date should always use US locale","title":"3.13.4"},{"location":"changelog/#v3133","text":"Further tweak to Netty. H/T @FredDeschenes","title":"v3.13.3"},{"location":"changelog/#v3132","text":"Fix #91 - large message handli ng in Netty","title":"v3.13.2"},{"location":"changelog/#v3131","text":"Upgrade to Kotlin 1.2.20","title":"v3.13.1"},{"location":"changelog/#v3130","text":"Support for operationId in OpenApi route metadata. H/T @danschultz for the PR. Removed previously deprecated methods.","title":"v3.13.0"},{"location":"changelog/#v3120","text":"New client module http4k-client-jetty , which supports both sync and async models.","title":"v3.12.0"},{"location":"changelog/#v3111","text":"Fix #84. OPTIONS requests are not detected by contract routes. Added option to NOT authorise OPTIONS requests in ApiKey security filter. Added support for Async HTTP clients and added new AsyncHttpClient interface, which is obviously used for HTTP clients only**, and not server-side calls. :) New client module http4k-client-apache-async . New metrics gathering module http4k-metrics-micrometer . Big H/T to @kirderf for the PR. Added support for async to OkHttp client module.","title":"v3.11.1"},{"location":"changelog/#v3100","text":"P/R 81 - adding headers and timeout to websocket client.","title":"v3.10.0"},{"location":"changelog/#v390","text":"Added compactify and prettify to Json implementations Added Json.hasBody Hamkrest matchers for comparing bodies. Note these are extension methods and need to be referenced/imported as such.","title":"v3.9.0"},{"location":"changelog/#v380","text":"Added facility for non-blocking websocket client to react to onConnect event. This API is the same as the inbound, server-side API - ie. there are no explicit connection event handlers. H/T @tom for the idea.","title":"v3.8.0"},{"location":"changelog/#v370","text":"P/R #13 Create extension methods for Response to add caching headers. H/T @k0zakinio.","title":"v3.7.0"},{"location":"changelog/#v361","text":"Fix #78. Serialisation of raw lists using Moshi fails in the same way as the Jackson auto-conversions do. Added convenience methods to get around this.","title":"v3.6.1"},{"location":"changelog/#v360","text":"Added http4k-format-moshi to support the Square auto-marshalling library.","title":"v3.6.0"},{"location":"changelog/#v351","text":"Fix #76 - encoding of path segments to use URI encoding instead of URL form encoding.","title":"v3.5.1"},{"location":"changelog/#v350","text":"Added support for multiple HotReload template directories in HandlebarsTemplates . H/T @TomShacham Fix #74 - Request tracing span/parentSpan set too early so was shared between outgoing requests.","title":"v3.5.0"},{"location":"changelog/#v340","text":"New server backend http4k-server-apache . H/T @kirderf for the PR :) We now set the length of the incoming request body when it is available in the incoming request.","title":"v3.4.0"},{"location":"changelog/#v331","text":"Handlebars now uses combination of Class and Template name to cache templates.","title":"v3.3.1"},{"location":"changelog/#v330","text":"Facility to compose TemplateRenderers with then() to provide fallback behaviour.","title":"v3.3.0"},{"location":"changelog/#v323","text":"PR #70: Header order equality for Request/Response - H/T @gypsydave5.","title":"v3.2.3"},{"location":"changelog/#v322","text":"Switched out Status for WsStatus (with proper RFC code set) in Websockets.","title":"v3.2.2"},{"location":"changelog/#v321","text":"Typesafe Websockets! Jetty now supports websockets, using the same style of API in the main http4k routing. (Possible) Breaking change: Because WsHandler (typealias) implements the same inbound interface as HttpHandler , you now cannot declare HttpHandlers without specifying the input type, so any \"anonymous\" handlers will not compile as a result. The required fix is very simple, but manual: `{ Response(OK) } should become { _:Request -> Response(OK) }","title":"v3.2.1"},{"location":"changelog/#v313","text":"Fix Request.form() for streaming requests","title":"v3.1.3"},{"location":"changelog/#v312","text":"Remove possibility of empty message for Path Lens failure.","title":"v3.1.2"},{"location":"changelog/#v311","text":"New (better!) API for http4k-contract module. Old meta DSL has been deprecated.","title":"v3.1.1"},{"location":"changelog/#v301","text":"Fix #63 - Apache Client Connect. timeout exception handling.","title":"v3.0.1"},{"location":"changelog/#v300","text":"Added http4k-serverless-lambda module, allowing http4k applications to be deployed into AWS Lambda and then called from API Gateway. Effectively, the combination of these two services become just another Server back-end supported by the library. \\o/","title":"v3.0.0"},{"location":"changelog/#v2381","text":"RequestContextKey now follow the standardised Lens structure of required, optional, defaulted, and can now be removed (set to null). Replace calls to RequestContextKey.of() with RequestContextKey.required() Removed previously deprecated values. See below for details on replacements.","title":"v2.38.1"},{"location":"changelog/#v2370","text":"Added http4k-resilience4j module, which adds Circuits, RateLimiters, Retrying and Bulkheading. Fix #60 (H/T @michaelhixson for the spot).","title":"v2.37.0"},{"location":"changelog/#v2360","text":"Added a couple of useful ServerFilters . Upgrade various dependency versions. Tidying of Multipart code.","title":"v2.36.0"},{"location":"changelog/#v2351","text":"Fix #57. Static handlers behave oddly when combined with an HTTP verb in the routing tree.","title":"v2.35.1"},{"location":"changelog/#v2350","text":"Fix #56. Altered behaviour of CatchLensFailure to NOT catch errors from unmarshalling Response objects. This was causing BAD_REQUEST to be incorrectly generated. Simplification of generics around LensSpecs. This should not be a breaking change, (there were 3 generics, now the MID has been removed so there are just 2) but could break if signatures are used explicitly.","title":"v2.35.0"},{"location":"changelog/#v2340","text":"Reordered generics in LensInjector to make sense. This should have no effect on most code-bases, but could break if signatures are used explicitly. Just flip the generic types to switch.","title":"v2.34.0"},{"location":"changelog/#v2331","text":"Added support for unsigned AWS requests, which enables streaming content to S3.","title":"v2.33.1"},{"location":"changelog/#v2330","text":"Added BodyMode.Request to configure streaming for clients. ResponseBodyMode is now BodyMode.Response (Breaking change. Fixable with simple find/replace).","title":"v2.33.0"},{"location":"changelog/#v2320","text":"Added ServerFilter.ProcessFiles filter to stream Multipart Files, convert them into references and replace inline in the Form.","title":"v2.32.0"},{"location":"changelog/#v2314","text":"Avoid realising StreamBody unless necessary, which could break common usages of streaming.","title":"v2.31.4"},{"location":"changelog/#v2313","text":"Tweaks to Server backends to improve efficiency.","title":"v2.31.3"},{"location":"changelog/#v2312","text":"Webdriver will keep only the final URI after redirects.","title":"v2.31.2"},{"location":"changelog/#v2311","text":"Increased granularity of Replay.DiskStream and ensure that traffic is returned in exact order on all OSes. Add support for redirects to Webdriver.","title":"v2.31.1"},{"location":"changelog/#v2310","text":"Multipart module tweaked to provide a more consistent API. Fix FollowRedirects for POST/PUT request.","title":"v2.31.0"},{"location":"changelog/#v2300","text":"Multipart form support through new module http4k-multipart . Deprecation: Replaced Swagger with OpenApi and deprecated the former (via typealias). Deprecation: Replaced FormValidator with Validator and deprecated the former (via typealias).","title":"v2.30.0"},{"location":"changelog/#v2294","text":"Refactor release.","title":"v2.29.4"},{"location":"changelog/#v2293","text":"Fix #50 - Webdriver does not normalise relative links correctly.","title":"v2.29.3"},{"location":"changelog/#v2292","text":"Http client modules now catch and convert Socket Timeout exceptions to HTTP 504s (with a custom message)","title":"v2.29.2"},{"location":"changelog/#v2291","text":"Tweaks to how recorded traffic is stored on disk. Thanks to @dkandalov for the PR around this.","title":"v2.29.1"},{"location":"changelog/#v2290","text":"Added TrafficFilters for recording and replaying HTTP traffic. See org.http4k.traffic package for details.","title":"v2.29.0"},{"location":"changelog/#v2280","text":"Added http4k-template-dust for Dust template engine support. Thanks to @npryce for the PR to add this.","title":"v2.28.0"},{"location":"changelog/#v2272","text":"Fix #44 - Use quotes around cookie values","title":"v2.27.2"},{"location":"changelog/#v2271","text":"Raise proper Exception (instead of LensFailure) when RequestContexts are not set up correctly, so we don't accidentally classify developer errors as BadRequests","title":"v2.27.1"},{"location":"changelog/#v2270","text":"Added facility to assign values into a RequestContext which is passed down the Filter chain.","title":"v2.27.0"},{"location":"changelog/#v2263","text":"Fix #44 - Request cookies should not be wrapped in quotes.","title":"v2.26.3"},{"location":"changelog/#v2262","text":"Fix #43 - AWS does not sign binary requests correctly.","title":"v2.26.2"},{"location":"changelog/#v2261","text":"Fix #41 - Sending binary body alters the size of the payload.","title":"v2.26.1"},{"location":"changelog/#v2260","text":"Added \"catch all\" routing option, which matches all methods to a handler.","title":"v2.26.0"},{"location":"changelog/#v2254","text":"Fix #40 - GZip filters now use content-encoding headers instead of transfer-encoding.","title":"v2.25.4"},{"location":"changelog/#v2253","text":"Fix #39 - ResponseBodyMode.Memory properly closes streams (breaks jetty + gzip).","title":"v2.25.3"},{"location":"changelog/#v2252","text":"Ensure that streams are closed properly when consuming from an upstream client.","title":"v2.25.2"},{"location":"changelog/#v2251","text":"Remove Apache client request streaming because it may not release connections properly.","title":"v2.25.1"},{"location":"changelog/#v2250","text":"Add streaming support to HTTP Server and Client modules. Remove CatchLensFailure ClientFilter as it will never be used.","title":"v2.25.0"},{"location":"changelog/#v2240","text":"Added CatchLensFailure for ClientFilters - which catches un-deserializable invalid responses from clients and generates a BAD_GATEWAY error.","title":"v2.24.0"},{"location":"changelog/#v2234","text":"Switch XML generation to Gson over Jackson because Jackson doesn't handle uppercase field names well. Switch native XML parsed type to Document over Node.","title":"v2.23.4"},{"location":"changelog/#v2233","text":"New algorithm for XML data class deserialisation, so un-deprecated XML methods.","title":"v2.23.3"},{"location":"changelog/#v2232","text":"Deprecated methods in XML support due to limitation with underlying Jackson implementation.","title":"v2.23.2"},{"location":"changelog/#v2231","text":"Fixed bug with GenerateXmlDataClasses filter","title":"v2.23.1"},{"location":"changelog/#v2230","text":"Renamed http4k-format-jackson-xml module to http4k-format-xml . Improved XML unmarshalling support.","title":"v2.23.0"},{"location":"changelog/#v2221","text":"Fixed 36: Form entry is too strict with content encoding.","title":"v2.22.1"},{"location":"changelog/#v2220","text":"Added http4k-format-jackson-xml module, with XML parsing support. Upgrade several dependencies","title":"v2.22.0"},{"location":"changelog/#v2212","text":"Fixed Hamkrest matchers to be on HttpMessage and not Http Request.","title":"v2.21.2"},{"location":"changelog/#v2211","text":"Default body Content Negotiation strategy changed to None","title":"v2.21.1"},{"location":"changelog/#v2210","text":"Converted Content-Negotiation strategy from an Enum to an interface, so that users can define their own strategies. We also now check encoding so there are 4-built in strategies to choose from: Strict, StrictNoDirective, NonStrict and None.","title":"v2.21.0"},{"location":"changelog/#v2201","text":"Fixed #31 - Matching of segments in URIs is done after URLs are decoded, which results in not capturing encoded slashes in the path segments.","title":"v2.20.1"},{"location":"changelog/#v2200","text":"Fixed #30 - CachingClasspath template ResourceLoader not working with non-root packages.","title":"v2.20.0"},{"location":"changelog/#v2190","text":"Fixed #29 - webdriver submission of text area. Http clients now use a new instance of the default for each instantiation. Previously there was a shared instance. Add regex body type for parsing values out of bodies, and \"None\" option for content negotiation.","title":"v2.19.0"},{"location":"changelog/#v2183","text":"Fix AWS request signing for requests containing empty path","title":"v2.18.3"},{"location":"changelog/#v2182","text":"Fix AWS request signing for requests containing path with special characters","title":"v2.18.2"},{"location":"changelog/#v2181","text":"Added support for newRequest() in new RouteBinder mechanic.","title":"v2.18.1"},{"location":"changelog/#v2180","text":"Add support for unlimited nesting for routes() blocks. Removed the raw Route object, which can be replaced with Router or RoutingHttpHandler where appropriate. As part of above, rejigged route setup logic. Deprecated old routing structure, so now \"/path\" to GET bind is \"/path\" bind GET to . To fix deprecation, simply switch the calls to \"to\" and \"bind\" in routing setup. Rename of bind() in http4k-contract to be bindContract()","title":"v2.18.0"},{"location":"changelog/#v2172","text":"Added missing eclectic HTTP method. :)","title":"v2.17.2"},{"location":"changelog/#v2171","text":"Added GZip filters to http4k-core to zip request and response bodies.","title":"v2.17.1"},{"location":"changelog/#v2161","text":"Improved messages for http4k-testing-hamkrest matchers.","title":"v2.16.1"},{"location":"changelog/#v2160","text":"Added http4k-testing-hamkrest which contains a set of Hamkrest matchers for Http4k objects.","title":"v2.16.0"},{"location":"changelog/#v2150","text":"More features for http4k-testing-webdriver . Cookie support added.","title":"v2.15.0"},{"location":"changelog/#v2140","text":"More features for http4k-testing-webdriver . We now support Form entry and submission.","title":"v2.14.0"},{"location":"changelog/#v2130","text":"More features for http4k-testing-webdriver .","title":"v2.13.0"},{"location":"changelog/#v2120","text":"Added http4k-testing-webdriver module, an ultralight Selenium WebDriver for http4k apps","title":"v2.12.0"},{"location":"changelog/#v2113","text":"Fix #26 - GenerateDataClasses does not recurse into nested object trees","title":"v2.11.3"},{"location":"changelog/#v2112","text":"Fix filter application on GroupRoutingHttpHandler to apply the filter when it is applied with then(RoutingHttpHandler()","title":"v2.11.2"},{"location":"changelog/#v2111","text":"Fix static routes not defaulting to index.html when in root context","title":"v2.11.1"},{"location":"changelog/#v2110","text":"Added SunHttp server implementation (for development use only)","title":"v2.11.0"},{"location":"changelog/#v2101","text":"Fix cookie parsing when value contains '='","title":"v2.10.1"},{"location":"changelog/#v2100","text":"Add method to set form values in the request","title":"v2.10.0"},{"location":"changelog/#v290","text":"Added PURGE HTTP method as it's used commonly by various caches.","title":"v2.9.0"},{"location":"changelog/#v281","text":"Repackage AWS classes for consistency with rest of project","title":"v2.8.1"},{"location":"changelog/#v271","text":"Alter AWS Auth filter creation. Now use ClientFilters.AwsAuth","title":"v2.7.1"},{"location":"changelog/#v270","text":"Add AWS module","title":"v2.7.0"},{"location":"changelog/#v260","text":"Newly created Zipkin traces are now populated onto incoming request in ServerFilters.","title":"v2.6.0"},{"location":"changelog/#v251","text":"Slight tweak to GSON auto-marshalling to allow for use of raw Arrays with auto-marshalling","title":"v2.5.1"},{"location":"changelog/#v250","text":"Add Thymeleaf templating support","title":"v2.5.0"},{"location":"changelog/#v240","text":"Add Pebble templating support","title":"v2.4.0"},{"location":"changelog/#v230","text":"Make Route a Router so we can nest them together.","title":"v2.3.0"},{"location":"changelog/#v221","text":"Remove excess \"charset\" from headers in Undertow.","title":"v2.2.1"},{"location":"changelog/#v220","text":"Rename by() to bind() in routing for clarity.","title":"v2.2.0"},{"location":"changelog/#v212","text":"Fix for #24 - UriTemplate captures query parameters when the trailing path parameter is a regex.","title":"v2.1.2"},{"location":"changelog/#v210","text":"Added GSON full-auto functions to convert arbitrary objects to/from JSON.","title":"v2.1.0"},{"location":"changelog/#v205","text":"Fix #23. Contract now supports multi-part URL params (for hardcoded parts)","title":"v2.0.5"},{"location":"changelog/#v204","text":"Fix #22. Uri template does not parse out correct path params when URL starts with a path part.","title":"v2.0.4"},{"location":"changelog/#v203","text":"toString() implementations to aid debugging","title":"v2.0.3"},{"location":"changelog/#v201","text":"Readded missing default parameter for newRequest() on RouteSpec","title":"v2.0.1"},{"location":"changelog/#v200","text":"Breaking: Inversion of routing API. GET to \"/someUri\" is now \"/someUri\" to GET for consistency across the entire API.","title":"v2.0.0"},{"location":"changelog/#v1331","text":"Reimplementation of http4k-contract API to match main routing API. Contracts are now nestable.","title":"v1.33.1"},{"location":"changelog/#v1322","text":"Fix Filters being applied twice in ContractRoutingHttpHandler","title":"v1.32.2"},{"location":"changelog/#v1321","text":"More work on http4k-contract contract API","title":"v1.32.1"},{"location":"changelog/#v1310","text":"Rework http4k-contract routing to be mounted in the same way as other RoutingHttpHandlers","title":"v1.31.0"},{"location":"changelog/#v1300","text":"Filters are now applied consistently to all Routers","title":"v1.30.0"},{"location":"changelog/#v1290","text":"Tweak to DSL for defining StaticRouters","title":"v1.29.0"},{"location":"changelog/#v1281","text":"Fix for #18: FollowRedirect will now work if location header includes charset information.","title":"v1.28.1"},{"location":"changelog/#v1280","text":"New DSL for defining StaticRouters","title":"v1.28.0"},{"location":"changelog/#v1270","text":"Merged StaticContent and StaticRouter and repackage of contract API into other packages","title":"v1.27.0"},{"location":"changelog/#v1262","text":"Extend fix for #17 to request Cookie header.","title":"v1.26.2"},{"location":"changelog/#v1261","text":"Fix for #17. Cookie can now parse a cookie without attributes and ending in semicolon.","title":"v1.26.1"},{"location":"changelog/#v1260","text":"Added nestable Routers. Merging of Modules and Routers. Router is the new Module ! RouteModule is now ContractRouter , so rename in code will be required.","title":"v1.26.0"},{"location":"changelog/#v1251","text":"Fix for #15. OkHttp client handling of POSTs with no body.","title":"v1.25.1"},{"location":"changelog/#v1250","text":"Can add custom mime types to Static Content GenerateDataClasses is capable of more complex object graphs","title":"v1.25.0"},{"location":"changelog/#v1240","text":"Remove HttpHandler.asServer in favour of HttpHandler.startServer to avoid confusion. Introduce Status.description() .","title":"v1.24.0"},{"location":"changelog/#v1230","text":"Netty sets content-length header.","title":"v1.23.0"},{"location":"changelog/#v1222","text":"Fix for #12. Undertow not constructing response correctly.","title":"v1.22.2"},{"location":"changelog/#v1220","text":"New module with Undertow.io support http4k-server-undertow Jackson implementation now ignores unknown properties in incoming messages Netty implementation tidied up","title":"v1.22.0"},{"location":"changelog/#v1211","text":"Fix for #11. Netty implementation returns incorrect status codes.","title":"v1.21.1"},{"location":"changelog/#v1210","text":"Add synonym methods for Lenses to aid readability. We now have invoke(IN)/extract(IN) and `invoke(IN, TARGET) /inject(IN, TARGET)","title":"v1.21.0"},{"location":"changelog/#v1200","text":"http4k-contracts : Add option to change the route of the module description route","title":"v1.20.0"},{"location":"changelog/#v1191","text":"http4k-contracts : Fix for contract module description routes not being authenticated via security filter","title":"v1.19.1"},{"location":"changelog/#v1190","text":"http4k-contracts : Add Swagger module rendering with JSON schema models for messages.","title":"v1.19.0"},{"location":"changelog/#v1180","text":"Add nonEmptyString() lens type to all request parts.","title":"v1.18.0"},{"location":"changelog/#v1170","text":"General rework","title":"v1.17.0"},{"location":"changelog/#v1160","text":"Further work on Path Lenses. They are now fully supported and consistent for both simple and contract routing scenarios.","title":"v1.16.0"},{"location":"changelog/#v1150","text":"Path lenses are now bidirectional, so can be used to populate requests as well as bodies an headers etc. Routes can now create shell Requests for themselves, using route.newRequest()","title":"v1.15.0"},{"location":"changelog/#v1140","text":"Body is now non-nullable (use Body.EMPTY instead) Rename methods BodyLens API for consistency and clarity. required() is now toLens() . to()' binding method is now of().","title":"v1.14.0"},{"location":"changelog/#v1130","text":"New client module: http4k-client-okhttp","title":"v1.13.0"},{"location":"changelog/#v1120","text":"Tidying","title":"v1.12.0"},{"location":"changelog/#v1110","text":"Added option for Body content-negotiation to be strict or non-strict (the default). Always be strict in what you send, relaxed in what you will accept. :)","title":"v1.11.0"},{"location":"changelog/#v1100","text":"Moved Credentials to org.http4k.core package. Add various filters, including SetHostFrom and CatchAll .","title":"v1.10.0"},{"location":"changelog/#v190","text":"Added GenerateDataClasses so you can generate Kotlin data classes from JSON messages.","title":"v1.9.0"},{"location":"changelog/#v180","text":"Added CORs support","title":"v1.8.0"},{"location":"changelog/#v170","text":"Added auto() to Jackson, so you can auto convert body objects into and out of Requests/Responses","title":"v1.7.0"},{"location":"changelog/#v160","text":"Added CachingFilters","title":"v1.6.0"},{"location":"changelog/#v150","text":"Removed static factory methods for Request/Response. They were confusing/incomplete and users can easily recreate them via extension functions. Merge org.http4k.core.Body and org.http4k.lens.Body . Add Request/Response message parsers.","title":"v1.5.0"},{"location":"changelog/#v140","text":"Turn Body into ByteBuffer wrapper rather than typealias. That should make .toString() behave as most people would expected.","title":"v1.4.0"},{"location":"changelog/#v130","text":"Removed non-mandatory parameters from Request and Response constructors. This is aid API clarity. and force users to use the API methods for properly constructing the objects. Regex Lens added.","title":"v1.3.0"},{"location":"changelog/#v100","text":"Initial major release.","title":"v1.0.0"},{"location":"code-of-conduct/","text":"Contributor Covenant Code of Conduct \u00b6 Our Pledge \u00b6 We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. Our Standards \u00b6 Examples of behavior that contributes to a positive environment for our community include: Demonstrating empathy and kindness toward other people Being respectful of differing opinions, viewpoints, and experiences Giving and gracefully accepting constructive feedback Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: The use of sexualized language or imagery, and sexual attention or advances of any kind Trolling, insulting or derogatory comments, and personal or political attacks Public or private harassment Publishing others' private information, such as a physical or email address, without their explicit permission Other conduct which could reasonably be considered inappropriate in a professional setting Enforcement Responsibilities \u00b6 Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. Scope \u00b6 This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Enforcement \u00b6 Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at contact@http4k.org . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident. Enforcement Guidelines \u00b6 Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 1. Correction \u00b6 Community Impact : Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. Consequence : A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 2. Warning \u00b6 Community Impact : A violation through a single incident or series of actions. Consequence : A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 3. Temporary Ban \u00b6 Community Impact : A serious violation of community standards, including sustained inappropriate behavior. Consequence : A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 4. Permanent Ban \u00b6 Community Impact : Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. Consequence : A permanent ban from any sort of public interaction within the community. Attribution \u00b6 This Code of Conduct is adapted from the Contributor Covenant , version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder . For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.","title":"Code of Conduct"},{"location":"code-of-conduct/#contributor_covenant_code_of_conduct","text":"","title":"Contributor Covenant Code of Conduct"},{"location":"code-of-conduct/#our_pledge","text":"We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.","title":"Our Pledge"},{"location":"code-of-conduct/#our_standards","text":"Examples of behavior that contributes to a positive environment for our community include: Demonstrating empathy and kindness toward other people Being respectful of differing opinions, viewpoints, and experiences Giving and gracefully accepting constructive feedback Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: The use of sexualized language or imagery, and sexual attention or advances of any kind Trolling, insulting or derogatory comments, and personal or political attacks Public or private harassment Publishing others' private information, such as a physical or email address, without their explicit permission Other conduct which could reasonably be considered inappropriate in a professional setting","title":"Our Standards"},{"location":"code-of-conduct/#enforcement_responsibilities","text":"Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.","title":"Enforcement Responsibilities"},{"location":"code-of-conduct/#scope","text":"This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.","title":"Scope"},{"location":"code-of-conduct/#enforcement","text":"Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at contact@http4k.org . All complaints will be reviewed and investigated promptly and fairly. All community leaders are obligated to respect the privacy and security of the reporter of any incident.","title":"Enforcement"},{"location":"code-of-conduct/#enforcement_guidelines","text":"Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:","title":"Enforcement Guidelines"},{"location":"code-of-conduct/#1_correction","text":"Community Impact : Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. Consequence : A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.","title":"1. Correction"},{"location":"code-of-conduct/#2_warning","text":"Community Impact : A violation through a single incident or series of actions. Consequence : A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.","title":"2. Warning"},{"location":"code-of-conduct/#3_temporary_ban","text":"Community Impact : A serious violation of community standards, including sustained inappropriate behavior. Consequence : A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.","title":"3. Temporary Ban"},{"location":"code-of-conduct/#4_permanent_ban","text":"Community Impact : Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. Consequence : A permanent ban from any sort of public interaction within the community.","title":"4. Permanent Ban"},{"location":"code-of-conduct/#attribution","text":"This Code of Conduct is adapted from the Contributor Covenant , version 2.0, available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. Community Impact Guidelines were inspired by Mozilla's code of conduct enforcement ladder . For answers to common questions about this code of conduct, see the FAQ at https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.","title":"Attribution"},{"location":"contributing/","text":"Contributors' Guide There are many ways in which you can contribute to the development of the library: Give us a Star on Github - you know you want to. ;) Using http4k to build something? Get in touch and tell everyone about it, or even just us! Sponsor us! The http4k team build the library out of love for software engineering and the OpenSource community, but running a project of this size is not without it's costs. Please see below for sponsorship options to help us keep the project running. Get help! The http4k team have produced a reasonable amount of training materials and are available for onsite or remote consulting engagements to help companies get the most out of the library. Pull requests \u00b6 If there are any message format library or templating engine bindings that you'd like to see supported, then please feel free to suggest them or provide a PR. JSON formats: create a new module with an implementation of Json by following the Argo example in the source. Templating engines: create a new module with a Templates implementation by following the HandlebarsTemplates example in the source. Server implementations: create a new module with a Server implementation by following the Jetty example in the source. Client implementations: create a new module with a Client implementation by following the OkHttp example in the source. General guidelines \u00b6 Questions can be directed towards the Slack #http4k channel, or on Twitter @http4k For issues, please describe giving as much detail as you can - including version and steps to recreate At the moment, PRs should be sent to the master branch - this might change in future so check back everytime! Source/binary compatibility always must be kept as far as possible - this is a must for minor and patch versions PR changes should have test coverage. Note that we use Junit 5 as a test engine - which uses new @Test annotations. All the PRs must pass the GitHub CI jobs before merging them https://github.com/http4k/http4k Testing with default settings is required when push changes. Note that we currently build against Java 21 ( jEnv is good for managing multiple java versions): ./gradlew check Credits \u00b6 Contributors \u00b6 Thank you to all the people who have already contributed to http4k! Backers \u00b6 Thank you to all our backers! [ Become a backer ] Sponsors \u00b6 Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor, either on GitHub Sponsors or directly with OpenCollective ) Vendor support \u00b6 Many thanks to all of the software vendors who supply tools to help us deliver http4k to it's community: Kotlin IDE \u00b6 Jetbrains kindly supplies the project with an Open Source License for the amazing IntelliJ IDE. Pairing tools \u00b6 Tuple supplies the http4k team with their amazing Pair-Programming tool Tuple allowing us to collaborate to build the library. Pairing is ace - everyone should do it! JVM Profiling tools \u00b6 YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of YourKit Java Profiler , YourKit .NET Profiler , and YourKit YouMonitor .","title":"Contribute/support http4k"},{"location":"contributing/#pull_requests","text":"If there are any message format library or templating engine bindings that you'd like to see supported, then please feel free to suggest them or provide a PR. JSON formats: create a new module with an implementation of Json by following the Argo example in the source. Templating engines: create a new module with a Templates implementation by following the HandlebarsTemplates example in the source. Server implementations: create a new module with a Server implementation by following the Jetty example in the source. Client implementations: create a new module with a Client implementation by following the OkHttp example in the source.","title":"Pull requests"},{"location":"contributing/#general_guidelines","text":"Questions can be directed towards the Slack #http4k channel, or on Twitter @http4k For issues, please describe giving as much detail as you can - including version and steps to recreate At the moment, PRs should be sent to the master branch - this might change in future so check back everytime! Source/binary compatibility always must be kept as far as possible - this is a must for minor and patch versions PR changes should have test coverage. Note that we use Junit 5 as a test engine - which uses new @Test annotations. All the PRs must pass the GitHub CI jobs before merging them https://github.com/http4k/http4k Testing with default settings is required when push changes. Note that we currently build against Java 21 ( jEnv is good for managing multiple java versions): ./gradlew check","title":"General guidelines"},{"location":"contributing/#credits","text":"","title":"Credits"},{"location":"contributing/#contributors","text":"Thank you to all the people who have already contributed to http4k!","title":"Contributors"},{"location":"contributing/#backers","text":"Thank you to all our backers! [ Become a backer ]","title":"Backers"},{"location":"contributing/#sponsors","text":"Thank you to all our sponsors! (please ask your company to also support this open source project by becoming a sponsor, either on GitHub Sponsors or directly with OpenCollective )","title":"Sponsors"},{"location":"contributing/#vendor_support","text":"Many thanks to all of the software vendors who supply tools to help us deliver http4k to it's community:","title":"Vendor support"},{"location":"contributing/#kotlin_ide","text":"Jetbrains kindly supplies the project with an Open Source License for the amazing IntelliJ IDE.","title":"Kotlin IDE"},{"location":"contributing/#pairing_tools","text":"Tuple supplies the http4k team with their amazing Pair-Programming tool Tuple allowing us to collaborate to build the library. Pairing is ace - everyone should do it!","title":"Pairing tools"},{"location":"contributing/#jvm_profiling_tools","text":"YourKit supports open source projects with innovative and intelligent tools for monitoring and profiling Java and .NET applications. YourKit is the creator of YourKit Java Profiler , YourKit .NET Profiler , and YourKit YouMonitor .","title":"JVM Profiling tools"},{"location":"documentation/","text":"http4k is a lightweight but fully-featured HTTP toolkit written in pure Kotlin that enables the serving and consuming of HTTP services in a functional and consistent way. http4k applications are just Kotlin functions. For example, here's a simple echo server: val app : HttpHandler = { request : Request -> Response ( OK ). body ( request . body ) } val server = app . asServer ( SunHttp ( 8000 )). start () http4k consists of a lightweight core library, http4k-core , providing a base HTTP implementation and Server/Client implementations based on the JDK classes. Further servers, clients, serverless, templating, websockets capabilities are then implemented in add-on modules. http4k apps can be simply mounted into a running Server, Serverless platform, or compiled to GraalVM and run as a super-lightweight binary. The principles of http4k are: Application as a Function: Based on the Twitter paper \"Your Server as a Function\" , all HTTP services can be composed of 2 types of simple function: HttpHandler: (Request) -> Response - provides a remote call for processing a Request. Filter: (HttpHandler) -> HttpHandler - adds Request/Response pre/post-processing. These filters are composed to make stacks of reusable behaviour that can then be applied to an HttpHandler . Immutability: All entities in the library are immutable unless their function explicitly disallows this. Symmetric: The HttpHandler interface is identical for both HTTP services and clients. This allows for simple offline testability of applications, as well as plugging together of services without HTTP container being required. Dependency-lite: Apart from the Kotlin StdLib, http4k-core module has ZERO dependencies and weighs in at ~1mb. Add-on modules only have dependencies required for specific implementation. Testability Built by TDD enthusiasts, so supports super-easy mechanisms for both in-memory and port-based testing of: individual endpoints applications websockets/sse full suites of microservices Portable Apps are completely portable across deployment platform in either a Server-based, Serverless or Native binaries. Quickstart \u00b6 Bored with reading already and just want to get coding? For the impatient, visit the http4k toolbox to generate a complete project from the wide variety of http4k modules. Alternatively, read the quickstart or take a look at the examples repo , which showcases a variety of http4k use-cases and features. Module feature overview \u00b6 Core: Base HTTP handler and immutable HTTP message objects, cookie handling. Commonly used HTTP functionalities provided as reusable Filters (caching, debugging, Zipkin request tracing ) Path-based routing , including nestable contexts Typesafe HTTP message construction/deconstruction and Request Contexts using Lenses Servlet implementation to allow plugin to any Servlet container Launch applications in 1LOC with an embedded SunHttp server backend (recommended for development use only) Lightweight JavaHttpClient implementation - perfect for Serverless contexts where binary size is a factor. Path-based WebSockets including typesafe message marshalling using Lenses, which are testable without a running container Path-based Server-Sent Events which are testable without a running container APIs to record and replay HTTP traffic to disk or memory Static file-serving capability with Caching and Hot-Reload Single Page Application support with Caching and Hot-Reload WebJars support in 1LOC ` Client: 1LOC client adapters Apache sync + async HTTP Java (bundled with http4k-core ) Fuel HTTP (supports sync and async HTTP) Jetty HTTP (supports sync and async HTTP and websockets) OkHttp HTTP (supports sync and async HTTP) 1LOC Websocket client, with blocking and non-blocking modes GraphQL client (bundled with GraphQL module) Server: 1LOC server backend spin-up for: Apache v4 & v5 (from httpcore) Jetty & Jetty Loom (including SSE and Websocket support) Helidon (Loom) Ktor CIO & Netty Netty (including Websocket support) SunHttp & SunHttpLoom (bundled with http4k-core ) Undertow (including SSE and Websocket support) Java-WebSocket (Websocket support only) API design allows for simple customization of underlying backend. Native Friendly Several of the supported backends can be compiled with GraalVM and Quarkus with zero configuration. Serverless: Function-based support for both HTTP and Event-based applications via adapters, using the simple and testable HttpHandler and FnHandler types. AWS Lambda Extend custom adapters to serve HTTP apps from APIGateway or use react to AWS events (without using the heavyweight AWS serialisation). Custom AWS Lambda Runtime Avoid the heavyweight AWS runtime, or simply compile your http4k app to GraalVM and get cold-starts in a few milliseconds! Google Cloud Functions Extend custom adapters to serve HTTP apps from Google Cloud Functions or use react to GCloud events. Apache OpenWhisk Extend custom adapters to serve HTTP apps or react to JSON events in IBM Cloud/OpenWhisk installations. Azure Functions Extend custom adapters to serve HTTP apps from AzureCloud. Alibaba Function Compute Extend custom adapters to serve HTTP apps from Alibaba. Tencent Serverless Cloud Functions Extend custom adapters to serve HTTP apps from SCF. Contracts: Define Typesafe HTTP contracts, with required and optional path/query/header/bodies Typesafe path matching Auto-validation of incoming requests == zero boilerplate validation code Self-documenting for all routes - e.g. Built in support for live OpenApi v2 and v3 description endpoints including JSON Schema model breakdown. Redoc and Swagger UI for OpenApi descriptions Templating: Pluggable templating system support for: Freemarker Handlebars JTE Pebble Pug4j Rocker Thymeleaf Caching and Hot-Reload template support Message formats: Consistent API provides first class support for marshalling formats to/from HTTP messages for: JSON - with support for: Jackson - includes support for fully automatic marshalling of Data classes Moshi - includes support for fully automatic marshalling of Data classes KondorJson - includes support for fully automatic marshalling of Data classes KotlinX Serialization - official Kotlin JSON API. Klaxon - includes support for fully automatic marshalling of Data classes Gson - includes support for fully automatic marshalling of Data classes Argo - lightweight Java JSON API with zero dependencies. XML - includes support for: Jackson - includes support for fully automatic marshalling of Data classes Xml - includes support for one way automatic marshalling of Data classes YAML - includes support for: Jackson - includes support for fully automatic marshalling of Data classes Moshi - includes support for fully automatic marshalling of Data classes DataFrame - with support for: CSV JSON CSV - includes support for: Jackson - CSV format for Jackson Multipart: Support for Multipart HTML forms, including Lens extensions for type-safe marshalling of fields. Resilience4J: Circuits, Retrying, Rate-Limiting, Bulkheading via Resilience4J integration Micrometer: Support for plugging http4k apps into Micrometer. Cloud Events: Consume and produce CloudEvents using typesafe lenses. OpenTelemetry: Instrument http4k apps with OpenTelemetry tooling. htmx: Support for powering http4k apps with htmx. Webhooks: Simply use the Standard Webhooks format to send signed and consistent Webhook events. GraphQL: Integration with GraphQL Java library to route and serve Graph-based apps. Plus conversion of any HttpHandler to be a GraphQL client. AWS: Plug a standard HttpHandler into the AWS v2 SDKs. This massively simplifies testing and allows for sniffing of the exact traffic going to AWS - brilliant for debugging and building fakes. Client filter to allow super-simple interaction with AWS services (via request signing) OAuth Security: Implement OAuth Authorisation Code Grant flow with a single Interface Pre-configured OAuth for following providers: Auth0 Discord Dropbox Facebook GitLab Google Soundcloud Digest Security: Implement the Digest Authentication flow for clients and servers Supports the null and Auth QoPs Supports Proxy Authentication Typesafe Configuration: Tooling to support operating http4k applications with 12-factor typesafe configuration. Cloud Native: Tooling to support operating http4k applications in orchestrated cloud environments such as Kubernetes and CloudFoundry., dual-port servers and health checks such as liveness and readiness checking. Approval Testing: JUnit 5 extensions for Approval testing of http4k Request and Response messages. Chaos: API for declaring and injecting failure modes into http4k applications, allowing modelling and hence answering of \"what if\" style questions to help understand how code fares under failure conditions such as latency and dying processes. Hamkrest: A set of Hamkrest matchers for testing http4k Request and Response messages. Kotest: A set of Kotest matchers for testing http4k Request and Response messages. Service Virtualisation: Record and replay versioned HTTP contracts to/from Servirtium Markdown format. Includes Servirtium MiTM server and simple JUnit extensions. Strikt: A set of Strikt matchers for testing http4k Request and Response messages. TracerBullet: Visually document your applications using the JUnit plugin. Playwright: Simplify in-browser testing with this JUnit extension. WebDriver: Ultra-lightweight Selenium WebDriver implementation for http4k applications. Example \u00b6 This quick example is designed to convey the simplicity & features of http4k . See also the quickstart for the simplest possible starting point and demonstrates how to serve and consume HTTP services with dynamic routing. To install, add these dependencies to your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) implementation ( \"org.http4k:http4k-client-okhttp\" ) } package guide.howto.readme import org.http4k.client.OkHttp import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.CachingFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { // we can bind HttpHandlers (which are just functions from Request -> Response) to paths/methods to create a Route, // then combine many Routes together to make another HttpHandler val app : HttpHandler = routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ). body ( \"pong!\" ) }, \"/greet/{name}\" bind GET to { req : Request -> val name : String? = req . path ( \"name\" ) Response ( OK ). body ( \"hello ${ name ?: \" anon ! \" } \" ) } ) // call the handler in-memory without spinning up a server val inMemoryResponse : Response = app ( Request ( GET , \"/greet/Bob\" )) println ( inMemoryResponse ) // Produces: // HTTP/1.1 200 OK // // // hello Bob // this is a Filter - it performs pre/post processing on a request or response val timingFilter = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"Request to ${ request . uri } took ${ latency } ms\" ) response } } // we can \"stack\" filters to create reusable units, and then apply them to an HttpHandler val compositeFilter = CachingFilters . Response . NoCache (). then ( timingFilter ) val filteredApp : HttpHandler = compositeFilter . then ( app ) // only 1 LOC to mount an app and start it in a container filteredApp . asServer ( Jetty ( 9000 )). start () // HTTP clients are also HttpHandlers! val client : HttpHandler = OkHttp () val networkResponse : Response = client ( Request ( GET , \"http://localhost:9000/greet/Bob\" )) println ( networkResponse ) // Produces: // Request to /api/greet/Bob took 1ms // HTTP/1.1 200 // cache-control: private, must-revalidate // content-length: 9 // date: Thu, 08 Jun 2017 13:01:13 GMT // expires: 0 // server: Jetty(9.3.16.v20170120) // // hello Bob } Acknowledgments \u00b6 Dan Bodart 's utterlyidle Ivan Moore for pairing on \"BarelyMagical\", a 50-line wrapper around utterlyidle to allow \"Server as a Function\" Contributors \u00b6 This project exists thanks to all the people who contribute . Backers & Sponsors \u00b6 If you use http4k in your project or enterprise and would like to support ongoing development, please consider becoming a backer or a sponsor. Sponsor logos will show up here with a link to your website.","title":"Introduction"},{"location":"documentation/#quickstart","text":"Bored with reading already and just want to get coding? For the impatient, visit the http4k toolbox to generate a complete project from the wide variety of http4k modules. Alternatively, read the quickstart or take a look at the examples repo , which showcases a variety of http4k use-cases and features.","title":"Quickstart"},{"location":"documentation/#module_feature_overview","text":"Core: Base HTTP handler and immutable HTTP message objects, cookie handling. Commonly used HTTP functionalities provided as reusable Filters (caching, debugging, Zipkin request tracing ) Path-based routing , including nestable contexts Typesafe HTTP message construction/deconstruction and Request Contexts using Lenses Servlet implementation to allow plugin to any Servlet container Launch applications in 1LOC with an embedded SunHttp server backend (recommended for development use only) Lightweight JavaHttpClient implementation - perfect for Serverless contexts where binary size is a factor. Path-based WebSockets including typesafe message marshalling using Lenses, which are testable without a running container Path-based Server-Sent Events which are testable without a running container APIs to record and replay HTTP traffic to disk or memory Static file-serving capability with Caching and Hot-Reload Single Page Application support with Caching and Hot-Reload WebJars support in 1LOC ` Client: 1LOC client adapters Apache sync + async HTTP Java (bundled with http4k-core ) Fuel HTTP (supports sync and async HTTP) Jetty HTTP (supports sync and async HTTP and websockets) OkHttp HTTP (supports sync and async HTTP) 1LOC Websocket client, with blocking and non-blocking modes GraphQL client (bundled with GraphQL module) Server: 1LOC server backend spin-up for: Apache v4 & v5 (from httpcore) Jetty & Jetty Loom (including SSE and Websocket support) Helidon (Loom) Ktor CIO & Netty Netty (including Websocket support) SunHttp & SunHttpLoom (bundled with http4k-core ) Undertow (including SSE and Websocket support) Java-WebSocket (Websocket support only) API design allows for simple customization of underlying backend. Native Friendly Several of the supported backends can be compiled with GraalVM and Quarkus with zero configuration. Serverless: Function-based support for both HTTP and Event-based applications via adapters, using the simple and testable HttpHandler and FnHandler types. AWS Lambda Extend custom adapters to serve HTTP apps from APIGateway or use react to AWS events (without using the heavyweight AWS serialisation). Custom AWS Lambda Runtime Avoid the heavyweight AWS runtime, or simply compile your http4k app to GraalVM and get cold-starts in a few milliseconds! Google Cloud Functions Extend custom adapters to serve HTTP apps from Google Cloud Functions or use react to GCloud events. Apache OpenWhisk Extend custom adapters to serve HTTP apps or react to JSON events in IBM Cloud/OpenWhisk installations. Azure Functions Extend custom adapters to serve HTTP apps from AzureCloud. Alibaba Function Compute Extend custom adapters to serve HTTP apps from Alibaba. Tencent Serverless Cloud Functions Extend custom adapters to serve HTTP apps from SCF. Contracts: Define Typesafe HTTP contracts, with required and optional path/query/header/bodies Typesafe path matching Auto-validation of incoming requests == zero boilerplate validation code Self-documenting for all routes - e.g. Built in support for live OpenApi v2 and v3 description endpoints including JSON Schema model breakdown. Redoc and Swagger UI for OpenApi descriptions Templating: Pluggable templating system support for: Freemarker Handlebars JTE Pebble Pug4j Rocker Thymeleaf Caching and Hot-Reload template support Message formats: Consistent API provides first class support for marshalling formats to/from HTTP messages for: JSON - with support for: Jackson - includes support for fully automatic marshalling of Data classes Moshi - includes support for fully automatic marshalling of Data classes KondorJson - includes support for fully automatic marshalling of Data classes KotlinX Serialization - official Kotlin JSON API. Klaxon - includes support for fully automatic marshalling of Data classes Gson - includes support for fully automatic marshalling of Data classes Argo - lightweight Java JSON API with zero dependencies. XML - includes support for: Jackson - includes support for fully automatic marshalling of Data classes Xml - includes support for one way automatic marshalling of Data classes YAML - includes support for: Jackson - includes support for fully automatic marshalling of Data classes Moshi - includes support for fully automatic marshalling of Data classes DataFrame - with support for: CSV JSON CSV - includes support for: Jackson - CSV format for Jackson Multipart: Support for Multipart HTML forms, including Lens extensions for type-safe marshalling of fields. Resilience4J: Circuits, Retrying, Rate-Limiting, Bulkheading via Resilience4J integration Micrometer: Support for plugging http4k apps into Micrometer. Cloud Events: Consume and produce CloudEvents using typesafe lenses. OpenTelemetry: Instrument http4k apps with OpenTelemetry tooling. htmx: Support for powering http4k apps with htmx. Webhooks: Simply use the Standard Webhooks format to send signed and consistent Webhook events. GraphQL: Integration with GraphQL Java library to route and serve Graph-based apps. Plus conversion of any HttpHandler to be a GraphQL client. AWS: Plug a standard HttpHandler into the AWS v2 SDKs. This massively simplifies testing and allows for sniffing of the exact traffic going to AWS - brilliant for debugging and building fakes. Client filter to allow super-simple interaction with AWS services (via request signing) OAuth Security: Implement OAuth Authorisation Code Grant flow with a single Interface Pre-configured OAuth for following providers: Auth0 Discord Dropbox Facebook GitLab Google Soundcloud Digest Security: Implement the Digest Authentication flow for clients and servers Supports the null and Auth QoPs Supports Proxy Authentication Typesafe Configuration: Tooling to support operating http4k applications with 12-factor typesafe configuration. Cloud Native: Tooling to support operating http4k applications in orchestrated cloud environments such as Kubernetes and CloudFoundry., dual-port servers and health checks such as liveness and readiness checking. Approval Testing: JUnit 5 extensions for Approval testing of http4k Request and Response messages. Chaos: API for declaring and injecting failure modes into http4k applications, allowing modelling and hence answering of \"what if\" style questions to help understand how code fares under failure conditions such as latency and dying processes. Hamkrest: A set of Hamkrest matchers for testing http4k Request and Response messages. Kotest: A set of Kotest matchers for testing http4k Request and Response messages. Service Virtualisation: Record and replay versioned HTTP contracts to/from Servirtium Markdown format. Includes Servirtium MiTM server and simple JUnit extensions. Strikt: A set of Strikt matchers for testing http4k Request and Response messages. TracerBullet: Visually document your applications using the JUnit plugin. Playwright: Simplify in-browser testing with this JUnit extension. WebDriver: Ultra-lightweight Selenium WebDriver implementation for http4k applications.","title":"Module feature overview"},{"location":"documentation/#example","text":"This quick example is designed to convey the simplicity & features of http4k . See also the quickstart for the simplest possible starting point and demonstrates how to serve and consume HTTP services with dynamic routing. To install, add these dependencies to your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) implementation ( \"org.http4k:http4k-client-okhttp\" ) } package guide.howto.readme import org.http4k.client.OkHttp import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.CachingFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { // we can bind HttpHandlers (which are just functions from Request -> Response) to paths/methods to create a Route, // then combine many Routes together to make another HttpHandler val app : HttpHandler = routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ). body ( \"pong!\" ) }, \"/greet/{name}\" bind GET to { req : Request -> val name : String? = req . path ( \"name\" ) Response ( OK ). body ( \"hello ${ name ?: \" anon ! \" } \" ) } ) // call the handler in-memory without spinning up a server val inMemoryResponse : Response = app ( Request ( GET , \"/greet/Bob\" )) println ( inMemoryResponse ) // Produces: // HTTP/1.1 200 OK // // // hello Bob // this is a Filter - it performs pre/post processing on a request or response val timingFilter = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"Request to ${ request . uri } took ${ latency } ms\" ) response } } // we can \"stack\" filters to create reusable units, and then apply them to an HttpHandler val compositeFilter = CachingFilters . Response . NoCache (). then ( timingFilter ) val filteredApp : HttpHandler = compositeFilter . then ( app ) // only 1 LOC to mount an app and start it in a container filteredApp . asServer ( Jetty ( 9000 )). start () // HTTP clients are also HttpHandlers! val client : HttpHandler = OkHttp () val networkResponse : Response = client ( Request ( GET , \"http://localhost:9000/greet/Bob\" )) println ( networkResponse ) // Produces: // Request to /api/greet/Bob took 1ms // HTTP/1.1 200 // cache-control: private, must-revalidate // content-length: 9 // date: Thu, 08 Jun 2017 13:01:13 GMT // expires: 0 // server: Jetty(9.3.16.v20170120) // // hello Bob }","title":"Example"},{"location":"documentation/#acknowledgments","text":"Dan Bodart 's utterlyidle Ivan Moore for pairing on \"BarelyMagical\", a 50-line wrapper around utterlyidle to allow \"Server as a Function\"","title":"Acknowledgments"},{"location":"documentation/#contributors","text":"This project exists thanks to all the people who contribute .","title":"Contributors"},{"location":"documentation/#backers_sponsors","text":"If you use http4k in your project or enterprise and would like to support ongoing development, please consider becoming a backer or a sponsor. Sponsor logos will show up here with a link to your website.","title":"Backers & Sponsors"},{"location":"faq/","text":"Find here answers to the most common questions that we get asked about http4k: General \u00b6 Q. Is http4k a library or a framework? A. Although it has many of the features of a framework, we consider http4k to be a library which adds a common HTTP routing layer. It is incredibly unopinionated and has been designed to not enforce design decisions on the API user. We use http4k for applications both large and small, using no DI framework. Q. Is http4k currently used in production? A. Absolutely! Whilst overall stats are obviously hard to come by, the biggest known usage of the library is serving the global site traffic (rank ~700 globally) for a large academic publisher, easily serving 10s of millions of requests per day on a few nodes. Additionally judging from the download stats and interest in the Slack channel indicate that take-up is increasing nicely. http4k also appears in the Thoughtworks Tech Radar , which covers useful and upcoming technologies used in Thoughtworks-delivered projects. If you're running http4k in production and would like to be listed on the site as an adopter, please get in touch. Q. Does http4k support an Async model? I need webscale! A. Currently there is no coroutine support in http4k. However, with the advent of project Loom on the JVM, we get a lot of the benefits of async model out of the box without the need to complicate the API with the use of suspend etc. As for scaling arguments, see the above answer relating to production usage, or checkout the benchmark results to see how http4k compares to other JVM-based sync and async web libraries. API \u00b6 Q. I'm attempting to build HTTP messages using the API, but changes don't affect the object (e.g. calling request.body(\"hello\") )? A. http4k HTTP message objects are immutable , so you need to chain or reassign the value from the method call to get the updated version. Q. Where are all the useful Filters defined? A. Filters are all in the import org.http4k.filter package and are located as methods on a singleton object relevant to their use: org.http4k.filter.CachingFilters.Request & org.http4k.filter.CachingFilters.Response org.http4k.filter.ClientFilters org.http4k.filter.DebuggingFilters org.http4k.filter.RequestFilters org.http4k.filter.ResponseFilters org.http4k.filter.ServerFilters org.http4k.filter.TrafficFilters Lenses & Auto-Marshalling \u00b6 Q. I am having a problem with the usage of Moshi, Jackson or GSON for auto marshalling A. Please see the custom FAQ for JSON handling questions. Q. My application uses Lenses, but when they fail I get an HTTP 500 instead of the promised 400. A. You forgot to add the ServerFilters.CatchLensFailure filter to your Server stack. OpenAPI Contracts \u00b6 Q. When I use binary uploads, my OpenAPI endpoint receives no data. A. With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. You can do this by instruction http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) } Serverless \u00b6 Q. When using AWS Lambda, I get an \"method is invalid\" error when testing my lambda. A. This comes from the fact that there are 2 different payload formats for AWS Lambda HTTP functions. We support both v1 and v2 formats, but recommend V2 is used as the JSON format is superior. To fix the problem, ensure that your Lambda function payload version matches the name of the AWS adapter function class being used (v1 or v2)","title":"FAQ"},{"location":"faq/#general","text":"Q. Is http4k a library or a framework? A. Although it has many of the features of a framework, we consider http4k to be a library which adds a common HTTP routing layer. It is incredibly unopinionated and has been designed to not enforce design decisions on the API user. We use http4k for applications both large and small, using no DI framework. Q. Is http4k currently used in production? A. Absolutely! Whilst overall stats are obviously hard to come by, the biggest known usage of the library is serving the global site traffic (rank ~700 globally) for a large academic publisher, easily serving 10s of millions of requests per day on a few nodes. Additionally judging from the download stats and interest in the Slack channel indicate that take-up is increasing nicely. http4k also appears in the Thoughtworks Tech Radar , which covers useful and upcoming technologies used in Thoughtworks-delivered projects. If you're running http4k in production and would like to be listed on the site as an adopter, please get in touch. Q. Does http4k support an Async model? I need webscale! A. Currently there is no coroutine support in http4k. However, with the advent of project Loom on the JVM, we get a lot of the benefits of async model out of the box without the need to complicate the API with the use of suspend etc. As for scaling arguments, see the above answer relating to production usage, or checkout the benchmark results to see how http4k compares to other JVM-based sync and async web libraries.","title":"General"},{"location":"faq/#api","text":"Q. I'm attempting to build HTTP messages using the API, but changes don't affect the object (e.g. calling request.body(\"hello\") )? A. http4k HTTP message objects are immutable , so you need to chain or reassign the value from the method call to get the updated version. Q. Where are all the useful Filters defined? A. Filters are all in the import org.http4k.filter package and are located as methods on a singleton object relevant to their use: org.http4k.filter.CachingFilters.Request & org.http4k.filter.CachingFilters.Response org.http4k.filter.ClientFilters org.http4k.filter.DebuggingFilters org.http4k.filter.RequestFilters org.http4k.filter.ResponseFilters org.http4k.filter.ServerFilters org.http4k.filter.TrafficFilters","title":"API"},{"location":"faq/#lenses_auto-marshalling","text":"Q. I am having a problem with the usage of Moshi, Jackson or GSON for auto marshalling A. Please see the custom FAQ for JSON handling questions. Q. My application uses Lenses, but when they fail I get an HTTP 500 instead of the promised 400. A. You forgot to add the ServerFilters.CatchLensFailure filter to your Server stack.","title":"Lenses & Auto-Marshalling"},{"location":"faq/#openapi_contracts","text":"Q. When I use binary uploads, my OpenAPI endpoint receives no data. A. With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. You can do this by instruction http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) }","title":"OpenAPI Contracts"},{"location":"faq/#serverless","text":"Q. When using AWS Lambda, I get an \"method is invalid\" error when testing my lambda. A. This comes from the fact that there are 2 different payload formats for AWS Lambda HTTP functions. We support both v1 and v2 formats, but recommend V2 is used as the JSON format is superior. To fix the problem, ensure that your Lambda function payload version matches the name of the AWS adapter function class being used (v1 or v2)","title":"Serverless"},{"location":"guide/","text":"The http4k technical documentation has been designed following the Grand Unified Theory of Documentation . Overall, the http4k developers firmly believe that API design should be natural and friendly to the user, and hence the codebase is not heavily commented. If we have done our jobs correctly, someone with the correct knowledge of a particular domain or platform should be able to implement systems using the http4k APIs by just using an IDE. That said, there remains a lot to be written to ensure that the basic concepts of the toolkit are written down, and that users can leverage the significant number of features that http4k provides. You can read more about the theory here , but essentially there are four distinct styles of useful documentation, based on what mode the reader is operating in. Regardless of which section you are reading, as much of the code as possible exists in the repository and is built with the rest of http4k in our CI. This has the effect of a making the code more verbose (including import statements and similar), but at the same time we can guarantee that the code compiles and you can navigate around it to find where everything is coming from. We hope you agree that this tradeoff is worth it. Concepts \u00b6 http4k is a simple framework based around several function types, and hopefully the ideas behind it are not difficult to grasp. This section conveys the mindset and rationale behind http4k, and lays out each of the main function types used in the toolkit and how they relate to each other. We recommend that all new users familiarise themselves with at least the rationale and HTTP pages of this section. Read more about the theory behind Concepts here . Tutorials \u00b6 Getting started with a new library can be quite daunting, and sometimes everyone needs a little hand holding to get comfortable with how things fit together. This section contains step-by-step guides to get you started with each of http4k's main conceptual areas. The first tutorial will get you out of the gate and up and running in no time. Read more about the theory behind Tutorials here . How-to guides \u00b6 The meat of the http4k documentation is in this section, in which you'll find ready made solutions to many common use-cases. Because if you've got something to achieve - it's pretty likely that we've probably come across it already \ud83d\ude09. The format for the recipes contains: Required Gradle dependencies A brief description of the problem Fully runnable code example displaying the solution. Think of it like a mini StackOverflow - but better because you've got the entire solution available to adapt to your particular use-case - \ud83d\ude03. As a community-driven project, we would welcome new or updated recipes to make http4k easier to use. The format of the new and updated recipes should follow this Markdown template . Read more about the theory behind How-to guides here . Reference \u00b6 In order to \"fly like a butterfly and sting like a bee\", http4k is heavily modularised. This section contains more detailed technical notes on the capabilities present each of the http4k modules. It's more of a \"what\" than a \"why\". Read more about the theory behind Reference guides here .","title":"About the docs"},{"location":"guide/#concepts","text":"http4k is a simple framework based around several function types, and hopefully the ideas behind it are not difficult to grasp. This section conveys the mindset and rationale behind http4k, and lays out each of the main function types used in the toolkit and how they relate to each other. We recommend that all new users familiarise themselves with at least the rationale and HTTP pages of this section. Read more about the theory behind Concepts here .","title":"Concepts"},{"location":"guide/#tutorials","text":"Getting started with a new library can be quite daunting, and sometimes everyone needs a little hand holding to get comfortable with how things fit together. This section contains step-by-step guides to get you started with each of http4k's main conceptual areas. The first tutorial will get you out of the gate and up and running in no time. Read more about the theory behind Tutorials here .","title":"Tutorials"},{"location":"guide/#how-to_guides","text":"The meat of the http4k documentation is in this section, in which you'll find ready made solutions to many common use-cases. Because if you've got something to achieve - it's pretty likely that we've probably come across it already \ud83d\ude09. The format for the recipes contains: Required Gradle dependencies A brief description of the problem Fully runnable code example displaying the solution. Think of it like a mini StackOverflow - but better because you've got the entire solution available to adapt to your particular use-case - \ud83d\ude03. As a community-driven project, we would welcome new or updated recipes to make http4k easier to use. The format of the new and updated recipes should follow this Markdown template . Read more about the theory behind How-to guides here .","title":"How-to guides"},{"location":"guide/#reference","text":"In order to \"fly like a butterfly and sting like a bee\", http4k is heavily modularised. This section contains more detailed technical notes on the capabilities present each of the http4k modules. It's more of a \"what\" than a \"why\". Read more about the theory behind Reference guides here .","title":"Reference"},{"location":"guide/concepts/http/","text":"HTTP application use-cases are the original and primary focus of http4k . Based on the Server as a Function concept, http4k provides a set of function types which can be used to write, test and deploy HTTP applications with simplicity and ease. Make no mistake - this model is deceptively simple but exceptionally powerful. These core concepts are repeated and combined in many different ways over the various functionalities provided by the toolkit. HttpMessage \u00b6 In http4k, an HttpMessage is an immutable entity representing either a Request or a Response . This immutability is a powerful alternative to the mutable versions found in other web libraries, as it provides a exact record of the state of the messages as they travel through an HTTP application, so for debugging purposes you can time-travel through an application inspecting the exact state at any point in the stack. Data class semantics for comparison also make HttpMessages incredibly simple to assert against in testing scenarios, and this ease of testing is one of the most important parts of the http4k ethos . HttpHandler \u00b6 typealias HttpHandler = ( Request ) -> Response A simple function representing all incoming and outgoing HTTP calls. HttpHandlers can be bound to a container (to create an Http4kServer ) with 1 LOC. This decouples the server implementation from the business logic: val jettyServer = app . asServer ( Jetty ( 9000 )). start () An Http client is also a HttpHandler : val client : HttpHandler = ApacheClient () Because the client and server interfaces are the same, apps can simply be plugged together out-of-container by just injecting one into the other: val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 ) Filter \u00b6 fun interface Filter : ( HttpHandler ) -> HttpHandler A function which decorates an HttpHandler to perform pre/post request processing. Filters can be composed together to make reusable \"stacks\" of behaviour which can be applied to a terminating HttpHandler - to yield another, decorated, HttpHandler. Router \u00b6 interface Router { fun match ( request : Request ): RouterMatch } A selective request handler, which attempts to match an incoming call against a bound HttpHandler.","title":"HTTP"},{"location":"guide/concepts/http/#httpmessage","text":"In http4k, an HttpMessage is an immutable entity representing either a Request or a Response . This immutability is a powerful alternative to the mutable versions found in other web libraries, as it provides a exact record of the state of the messages as they travel through an HTTP application, so for debugging purposes you can time-travel through an application inspecting the exact state at any point in the stack. Data class semantics for comparison also make HttpMessages incredibly simple to assert against in testing scenarios, and this ease of testing is one of the most important parts of the http4k ethos .","title":"HttpMessage"},{"location":"guide/concepts/http/#httphandler","text":"typealias HttpHandler = ( Request ) -> Response A simple function representing all incoming and outgoing HTTP calls. HttpHandlers can be bound to a container (to create an Http4kServer ) with 1 LOC. This decouples the server implementation from the business logic: val jettyServer = app . asServer ( Jetty ( 9000 )). start () An Http client is also a HttpHandler : val client : HttpHandler = ApacheClient () Because the client and server interfaces are the same, apps can simply be plugged together out-of-container by just injecting one into the other: val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 )","title":"HttpHandler"},{"location":"guide/concepts/http/#filter","text":"fun interface Filter : ( HttpHandler ) -> HttpHandler A function which decorates an HttpHandler to perform pre/post request processing. Filters can be composed together to make reusable \"stacks\" of behaviour which can be applied to a terminating HttpHandler - to yield another, decorated, HttpHandler.","title":"Filter"},{"location":"guide/concepts/http/#router","text":"interface Router { fun match ( request : Request ): RouterMatch } A selective request handler, which attempts to match an incoming call against a bound HttpHandler.","title":"Router"},{"location":"guide/concepts/lens/","text":"Lenses provide typesafe parameter destructuring/construction of HTTP messages. Getting values from HTTP messages is one thing, but we want to ensure that those values are both present and valid. For this purpose, we can use a Lens . A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional). To utilise a lens, first you have to declare it with the form .. . There is one \"location\" type for each part of the message, each with config/mapping operations which are specific to that location: Location Starting type Applicable to Multiplicity Requirement terminator Examples Query String Request Singular or multiple Optional or Required Query.optional(\"name\") Query.required(\"name\") Query.int().required(\"name\") Query.localDate().multi.required(\"name\") Query.map(::CustomType, { it.value }).required(\"name\") Header String Request or Response Singular or multiple Optional or Required Header.optional(\"name\") Header.required(\"name\") Header.int().required(\"name\") Header.localDate().multi.required(\"name\") Header.map(::CustomType, { it.value }).required(\"name\") Path String Request Singular Required Path.of(\"name\") Path.int().of(\"name\") Path.map(::CustomType, { it.value }).of(\"name\") FormField String WebForm Singular or multiple Optional or Required FormField.optional(\"name\") FormField.required(\"name\") FormField.int().required(\"name\") FormField.localDate().multi.required(\"name\") FormField.map(::CustomType, { it.value }).required(\"name\") Body ByteBuffer Request or Response Singular Required Body.string(ContentType.TEXT_PLAIN).toLens() Body.json().toLens() Body.webForm(Validator.Strict, FormField.required(\"name\")).toLens() Once the lens is declared, you can use it on a target object to either get or set the value: Retrieving a value: use .extract() , or the more concise invoke form: () Setting a value: use .inject(, ) , or the more concise invoke form: (, ) Code \u00b6 package guide.reference.core import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.localDate import org.http4k.lens.nonEmptyString import org.http4k.lens.string import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import java.time.LocalDate val pathLocalDate = Path . localDate (). of ( \"date\" ) val requiredQuery = Query . required ( \"myQueryName\" ) val nonEmptyQuery = Query . nonEmptyString (). required ( \"myNonEmptyQuery\" ) val optionalHeader = Header . int (). optional ( \"Content-Length\" ) val responseBody = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Most of the useful common JDK types are covered. However, if we want to use our own types, we can just use `map()` data class CustomType ( val value : String ) val requiredCustomQuery = Query . map ( :: CustomType , { it . value }). required ( \"myCustomType\" ) //To use the Lens, simply `invoke() or extract()` it using an HTTP message to extract the value, or alternatively `invoke() or inject()` it with the value if we are modifying (via copy) the message: val handler : RoutingHttpHandler = routes ( \"/hello/{date:.*}\" bind GET to { request : Request -> val pathDate : LocalDate = pathLocalDate ( request ) // SAME AS: // val pathDate: LocalDate = pathLocalDate.extract(request) val customType : CustomType = requiredCustomQuery ( request ) val anIntHeader : Int? = optionalHeader ( request ) val baseResponse = Response ( OK ) val responseWithHeader = optionalHeader ( anIntHeader , baseResponse ) // SAME AS: // val responseWithHeader = optionalHeader.inject(anIntHeader, baseResponse) responseBody ( \"you sent $ pathDate and $ customType \" , responseWithHeader ) } ) //With the addition of the `CatchLensFailure` filter, no other validation is required when using Lenses, as http4k will handle invalid requests by returning a BAD_REQUEST (400) response. val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" ) ) //More conveniently for construction of HTTP messages, multiple lenses can be used at once to modify a message, which is useful for properly building both requests and responses in a typesafe way without resorting to string values (especially in URLs which should never be constructed using String concatenation): val modifiedRequest : Request = Request ( GET , \"http://google.com/{pathLocalDate}\" ). with ( pathLocalDate of LocalDate . now (), requiredQuery of \"myAmazingString\" , optionalHeader of 123 )","title":"Lenses"},{"location":"guide/concepts/lens/#code","text":"package guide.reference.core import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.localDate import org.http4k.lens.nonEmptyString import org.http4k.lens.string import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import java.time.LocalDate val pathLocalDate = Path . localDate (). of ( \"date\" ) val requiredQuery = Query . required ( \"myQueryName\" ) val nonEmptyQuery = Query . nonEmptyString (). required ( \"myNonEmptyQuery\" ) val optionalHeader = Header . int (). optional ( \"Content-Length\" ) val responseBody = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Most of the useful common JDK types are covered. However, if we want to use our own types, we can just use `map()` data class CustomType ( val value : String ) val requiredCustomQuery = Query . map ( :: CustomType , { it . value }). required ( \"myCustomType\" ) //To use the Lens, simply `invoke() or extract()` it using an HTTP message to extract the value, or alternatively `invoke() or inject()` it with the value if we are modifying (via copy) the message: val handler : RoutingHttpHandler = routes ( \"/hello/{date:.*}\" bind GET to { request : Request -> val pathDate : LocalDate = pathLocalDate ( request ) // SAME AS: // val pathDate: LocalDate = pathLocalDate.extract(request) val customType : CustomType = requiredCustomQuery ( request ) val anIntHeader : Int? = optionalHeader ( request ) val baseResponse = Response ( OK ) val responseWithHeader = optionalHeader ( anIntHeader , baseResponse ) // SAME AS: // val responseWithHeader = optionalHeader.inject(anIntHeader, baseResponse) responseBody ( \"you sent $ pathDate and $ customType \" , responseWithHeader ) } ) //With the addition of the `CatchLensFailure` filter, no other validation is required when using Lenses, as http4k will handle invalid requests by returning a BAD_REQUEST (400) response. val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" ) ) //More conveniently for construction of HTTP messages, multiple lenses can be used at once to modify a message, which is useful for properly building both requests and responses in a typesafe way without resorting to string values (especially in URLs which should never be constructed using String concatenation): val modifiedRequest : Request = Request ( GET , \"http://google.com/{pathLocalDate}\" ). with ( pathLocalDate of LocalDate . now (), requiredQuery of \"myAmazingString\" , optionalHeader of 123 )","title":"Code"},{"location":"guide/concepts/rationale/","text":"http4k was created as the distillation of 15 years worth of experience of using various server-side libraries and we've stolen good ideas from everywhere we can. For instance - the routing module is inspired by UtterlyIdle , the \"Server as a function\" and filter model is stolen from Finagle , and the contract module OpenApi generator is ported from Fintrospect . With the growing adoption of Kotlin, we wanted something that would fully leverage the features of the language and it felt like a good time to start something from scratch. For our purposes, we wanted something that: Was based on simple functional concepts and embraced immutability. Embraced the \"Server as a Function\" model a uniform server/client API. Absolutely no magic involved: No reflection. No annotations. Lightweight with minimal dependencies (apart from the Kotlin StdLib, http4k-core has zero). Embraced Test-Driven approaches, was testable outside of an HTTP container, and testing should require no custom infrastructure. Starts/stops ultra quickly. Provides typesafe HTTP message deconstruction/construction. Automatically dealt with contract breaches to remove boilerplate. Automatic generation of OpenApi documentation (including JSON Schema models). http4k ticks all of these boxes. It allow us to construct entire suites of services which can be tested either wired together without HTTP, or spun up in containers using a single line of code. The symmetric HTTP API also allows Filter chains (often called \"Middleware\" or \"Interceptors\" in other frameworks) to be constructed into reusable units/stacks for both server and client sides (eg. logging/metrics/caching...) since they can be composed together for later use. As a bonus, we can also easily create simple Fake servers for any HTTP contract, which means (in combination with CDC suites) you can end-to-end test micro-services in an outside-in way (using GOOS -style acceptance tests). Scenarios such as \"what happens if this HTTP dependency continually takes > 5 seconds to respond?\" are easily modelled - answers you can't easily get if you're faking out your dependencies inside the HTTP boundary.","title":"Rationale"},{"location":"guide/concepts/server-sent-events/","text":"http4k provides SSE (Server-Sent Events) support using a simple, consistent, typesafe, and testable API on supported server backends. Sse communication consists of a few main concepts: SseMessage \u00b6 As per the http4k ethos, an immutable message object to be pushed from the server to the connected client. There are 2 types of SseMessage - Events (for sending known constructs), and Data (for sending byte streams). Lenses can be used to provide typesafe object marshalling with SseMessages. Sse \u00b6 interface Sse { val connectRequest : Request fun send ( message : SseMessage ) fun close () fun onClose ( fn : () -> Unit ) } An interface representing the available server callback API to the Server-Sent Event channel. Sse objects can send() SseMessages to the client, or close() the connection. The Sse has a reference to the incoming HTTP Request which was used during connection. SseConsumer \u00b6 typealias SseConsumer = ( Sse ) -> Unit The primary callback received when an Sse server is connected to a client. API user behaviour is configured here. SseHandler \u00b6 typelias SseHandler = ( Request ) -> SseResponse Provides the route mapping of an HTTP Request to a particular SseResponse (which contains a SseConsumer). SseFilter \u00b6 fun interface SseFilter : ( SseConsumer ) -> SseConsumer Applies decoration to a matched SseConsumer before it is invoked. SseFilters can be used to apply tangental effects to the matched SseConsumer such as logging/security, or to modify the incoming HTTP Request . SseRouter \u00b6 interface SseRouter { fun match ( request : Request ): SseRouterMatch fun withBasePath ( new : String ): SseRouter fun withFilter ( new : SseFilter ): SseRouter } Applies the route matching functionality when requests for Sse connections are received by the server.","title":"Server-Sent Events"},{"location":"guide/concepts/server-sent-events/#ssemessage","text":"As per the http4k ethos, an immutable message object to be pushed from the server to the connected client. There are 2 types of SseMessage - Events (for sending known constructs), and Data (for sending byte streams). Lenses can be used to provide typesafe object marshalling with SseMessages.","title":"SseMessage"},{"location":"guide/concepts/server-sent-events/#sse","text":"interface Sse { val connectRequest : Request fun send ( message : SseMessage ) fun close () fun onClose ( fn : () -> Unit ) } An interface representing the available server callback API to the Server-Sent Event channel. Sse objects can send() SseMessages to the client, or close() the connection. The Sse has a reference to the incoming HTTP Request which was used during connection.","title":"Sse"},{"location":"guide/concepts/server-sent-events/#sseconsumer","text":"typealias SseConsumer = ( Sse ) -> Unit The primary callback received when an Sse server is connected to a client. API user behaviour is configured here.","title":"SseConsumer"},{"location":"guide/concepts/server-sent-events/#ssehandler","text":"typelias SseHandler = ( Request ) -> SseResponse Provides the route mapping of an HTTP Request to a particular SseResponse (which contains a SseConsumer).","title":"SseHandler"},{"location":"guide/concepts/server-sent-events/#ssefilter","text":"fun interface SseFilter : ( SseConsumer ) -> SseConsumer Applies decoration to a matched SseConsumer before it is invoked. SseFilters can be used to apply tangental effects to the matched SseConsumer such as logging/security, or to modify the incoming HTTP Request .","title":"SseFilter"},{"location":"guide/concepts/server-sent-events/#sserouter","text":"interface SseRouter { fun match ( request : Request ): SseRouterMatch fun withBasePath ( new : String ): SseRouter fun withFilter ( new : SseFilter ): SseRouter } Applies the route matching functionality when requests for Sse connections are received by the server.","title":"SseRouter"},{"location":"guide/concepts/serverless/","text":"Serverless \u00b6 http4k provides Serverless support using a simple, consistent, typesafe, and testable API on multiple supported Serverless platforms. There are two main types of API that can be served using the http4k infrastructure, although they are backed by the same lightweight system. As with the http4k ethos, there is a primary focus on simplicity, testability and portability - http4k simply acts as a shim layer over the top of the underlying platform. Because of the way in which Serverless functions are bound in the runtime, is it required by most platforms to create a Kotlin class which receives and wraps the http4k code, and then to configure the identity of this class in the function configuration. http4k Serverless modules are named: http4k-serverless- . HTTP-based applications \u00b6 Any standard HttpHandler can be mounted and served in a Serverless context - the underlying platform can be thought of as just another supported backend for http4k applications, with HTTP traffic routed to it via a custom vendor cloud technology (eg. AWS APIGateway). This makes the http4k model especially powerful as applications can be built, run, and tested locally by using any supported Server-backend, then transparently deployed to the Serverless platform with zero modification. There is a single extra interface introduced for deploying HTTP apps: AppLoader \u00b6 fun interface AppLoader : ( Map < String , String > ) -> HttpHandler As per 12-factor configuration principles, the AppLoader is responsible for converting a set of Environment properties (aka System.getEnv() ) into the application HttpHandler instance. Event-based applications \u00b6 Serverless platforms also generally provide the facility to write arbitrary functions which react to events generated within the vendor cloud - e.g on a schedule or when a message is sent to a queue. Whilst the type of events vary by platform, http4k provides a lightweight, easily testable and, most importantly, vendor-neutral API. FnHandler \u00b6 fun interface FnHandler < In , Ctx , Out > : ( In , Ctx ) -> Out The polymorphic interface representing the Serverless function signature for receiving an Event. The Ctx parameter is custom to the vendor platform, but generally encapsulates contextual state regarding the function invocation. FnLoader \u00b6 typealias FnLoader < Ctx > = ( Map < String , String > ) -> FnHandler < InputStream , Ctx , InputStream > As per 12-factor configuration principles, the FnLoader is responsible for converting a set of Environment properties (aka System.getEnv() ) into the application FnHandler instance. Note that the result of this call is generified by InputStream request and response types. The various http4k Serverless modules also provide a custom converter function to auto-marshall event object in and out of the InputStream, which makes the conversion invisible to the API user. FnFilter \u00b6 fun interface FnFilter < In , Ctx , Out > : ( FnHandler < In , Ctx , Out > ) -> FnHandler < In , Ctx , Out > Applies decoration to a matched FnHandler before it is invoked. FnFilters can be used to apply tangental effects to the matched FnHandler such as logging, or to modify the incoming event.","title":"Serverless"},{"location":"guide/concepts/serverless/#serverless","text":"http4k provides Serverless support using a simple, consistent, typesafe, and testable API on multiple supported Serverless platforms. There are two main types of API that can be served using the http4k infrastructure, although they are backed by the same lightweight system. As with the http4k ethos, there is a primary focus on simplicity, testability and portability - http4k simply acts as a shim layer over the top of the underlying platform. Because of the way in which Serverless functions are bound in the runtime, is it required by most platforms to create a Kotlin class which receives and wraps the http4k code, and then to configure the identity of this class in the function configuration. http4k Serverless modules are named: http4k-serverless- .","title":"Serverless"},{"location":"guide/concepts/serverless/#http-based_applications","text":"Any standard HttpHandler can be mounted and served in a Serverless context - the underlying platform can be thought of as just another supported backend for http4k applications, with HTTP traffic routed to it via a custom vendor cloud technology (eg. AWS APIGateway). This makes the http4k model especially powerful as applications can be built, run, and tested locally by using any supported Server-backend, then transparently deployed to the Serverless platform with zero modification. There is a single extra interface introduced for deploying HTTP apps:","title":"HTTP-based applications"},{"location":"guide/concepts/serverless/#apploader","text":"fun interface AppLoader : ( Map < String , String > ) -> HttpHandler As per 12-factor configuration principles, the AppLoader is responsible for converting a set of Environment properties (aka System.getEnv() ) into the application HttpHandler instance.","title":"AppLoader"},{"location":"guide/concepts/serverless/#event-based_applications","text":"Serverless platforms also generally provide the facility to write arbitrary functions which react to events generated within the vendor cloud - e.g on a schedule or when a message is sent to a queue. Whilst the type of events vary by platform, http4k provides a lightweight, easily testable and, most importantly, vendor-neutral API.","title":"Event-based applications"},{"location":"guide/concepts/serverless/#fnhandler","text":"fun interface FnHandler < In , Ctx , Out > : ( In , Ctx ) -> Out The polymorphic interface representing the Serverless function signature for receiving an Event. The Ctx parameter is custom to the vendor platform, but generally encapsulates contextual state regarding the function invocation.","title":"FnHandler"},{"location":"guide/concepts/serverless/#fnloader","text":"typealias FnLoader < Ctx > = ( Map < String , String > ) -> FnHandler < InputStream , Ctx , InputStream > As per 12-factor configuration principles, the FnLoader is responsible for converting a set of Environment properties (aka System.getEnv() ) into the application FnHandler instance. Note that the result of this call is generified by InputStream request and response types. The various http4k Serverless modules also provide a custom converter function to auto-marshall event object in and out of the InputStream, which makes the conversion invisible to the API user.","title":"FnLoader"},{"location":"guide/concepts/serverless/#fnfilter","text":"fun interface FnFilter < In , Ctx , Out > : ( FnHandler < In , Ctx , Out > ) -> FnHandler < In , Ctx , Out > Applies decoration to a matched FnHandler before it is invoked. FnFilters can be used to apply tangental effects to the matched FnHandler such as logging, or to modify the incoming event.","title":"FnFilter"},{"location":"guide/concepts/websockets/","text":"http4k provides WebSocket support using a simple, consistent, typesafe, and testable API on supported server backends. WebSocket communication consists of a few main concepts: WsMessage \u00b6 As per the http4k ethos, an immutable message object providing duplex communication between the server to the connected client. Lenses can be used to provide typesafe object marshalling with WsMessages. WebSocket \u00b6 interface Websocket { val upgradeRequest : Request fun send ( message : WsMessage ) fun close ( status : WsStatus = NORMAL ) fun onError ( fn : ( Throwable ) -> Unit ) fun onClose ( fn : ( WsStatus ) -> Unit ) fun onMessage ( fn : ( WsMessage ) -> Unit ) } An interface representing the available server callback API to the WebSocket channel. WebSocket objects can send() WsMessages to the client, or react to incoming events by binding behaviour with onMessage() , onError() or onClose() . The WebSocket has a reference to the incoming HTTP Request which was used during connection. WsConsumer \u00b6 typealias WsConsumer = ( WebSocket ) -> Unit The primary callback received when an WebSocket server is connected to a client. API user behaviour is configured here. WsHandler \u00b6 typealias WsHandler = ( Request ) -> WsConsumer Provides the route mapping of an HTTP Request to a particular WsConsumer. WsFilter \u00b6 fun interface WsFilter : ( WsConsumer ) -> WsConsumer Applies decoration to a matched WsConsumer before it is invoked. WsFilters can be used to apply tangental effects to the matched WsConsumer such as logging/security, or to modify the incoming HTTP Request . WsRouter \u00b6 interface WsRouter { fun match ( request : Request ): WsRouterMatch fun withBasePath ( new : String ): WsRouter fun withFilter ( new : WsFilter ): WsRouter } Applies the route matching functionality when requests for WebSocket connections are received by the server.","title":"WebSockets"},{"location":"guide/concepts/websockets/#wsmessage","text":"As per the http4k ethos, an immutable message object providing duplex communication between the server to the connected client. Lenses can be used to provide typesafe object marshalling with WsMessages.","title":"WsMessage"},{"location":"guide/concepts/websockets/#websocket","text":"interface Websocket { val upgradeRequest : Request fun send ( message : WsMessage ) fun close ( status : WsStatus = NORMAL ) fun onError ( fn : ( Throwable ) -> Unit ) fun onClose ( fn : ( WsStatus ) -> Unit ) fun onMessage ( fn : ( WsMessage ) -> Unit ) } An interface representing the available server callback API to the WebSocket channel. WebSocket objects can send() WsMessages to the client, or react to incoming events by binding behaviour with onMessage() , onError() or onClose() . The WebSocket has a reference to the incoming HTTP Request which was used during connection.","title":"WebSocket"},{"location":"guide/concepts/websockets/#wsconsumer","text":"typealias WsConsumer = ( WebSocket ) -> Unit The primary callback received when an WebSocket server is connected to a client. API user behaviour is configured here.","title":"WsConsumer"},{"location":"guide/concepts/websockets/#wshandler","text":"typealias WsHandler = ( Request ) -> WsConsumer Provides the route mapping of an HTTP Request to a particular WsConsumer.","title":"WsHandler"},{"location":"guide/concepts/websockets/#wsfilter","text":"fun interface WsFilter : ( WsConsumer ) -> WsConsumer Applies decoration to a matched WsConsumer before it is invoked. WsFilters can be used to apply tangental effects to the matched WsConsumer such as logging/security, or to modify the incoming HTTP Request .","title":"WsFilter"},{"location":"guide/concepts/websockets/#wsrouter","text":"interface WsRouter { fun match ( request : Request ): WsRouterMatch fun withBasePath ( new : String ): WsRouter fun withFilter ( new : WsFilter ): WsRouter } Applies the route matching functionality when requests for WebSocket connections are received by the server.","title":"WsRouter"},{"location":"guide/howto/","text":"How-to guides \u00b6 The meat of the http4k documentation is in this section, in which you'll find ready made solutions to many common use-cases. Because if you've got something to achieve - it's pretty likely that we've probably come across it already \ud83d\ude09. Check out the nav on the left for the list of recipes. The format for the recipes contains: Required Gradle dependencies A brief description of the problem Fully runnable code example displaying the solution. Think of it like a mini StackOverflow - but better because you've got the entire solution available to adapt to your particular use-case - \ud83d\ude03. As a community-driven project, we would welcome new or updated recipes to make http4k easier to use. The format of the new and updated recipes should follow this Markdown template . Read more about the theory behind How-to guides here .","title":"Overview"},{"location":"guide/howto/#how-to_guides","text":"The meat of the http4k documentation is in this section, in which you'll find ready made solutions to many common use-cases. Because if you've got something to achieve - it's pretty likely that we've probably come across it already \ud83d\ude09. Check out the nav on the left for the list of recipes. The format for the recipes contains: Required Gradle dependencies A brief description of the problem Fully runnable code example displaying the solution. Think of it like a mini StackOverflow - but better because you've got the entire solution available to adapt to your particular use-case - \ud83d\ude03. As a community-driven project, we would welcome new or updated recipes to make http4k easier to use. The format of the new and updated recipes should follow this Markdown template . Read more about the theory behind How-to guides here .","title":"How-to guides"},{"location":"guide/howto/arrange_filters_into_stacks/","text":"http4k Filters are just decorator functions for HttpHandlers and process requests by applying the following process: Receive the Request Modify it Pass it to the next HttpHandler in the chain Receive the Response Modify it Return it to the caller We can reason that we can combine filters together to form chains, or \"Stacks\" of processing logic - moving from the most generic to the most specific. But the ordering of the filters is important in order that we have the information at the point in the stack when we need it. For example - if we want to record all HTTP traffic, we much ensure that we do this after any exception handling has occurred (so that we can record the 5XX properly). Experience has shown that there is a general formula to be used when constructing stacks. Serverside \u00b6 A typical stack looks like: Debugging/Tracing <-- to ensure that we see all traffic Reporting & metrics capture <-- to record accurately what we sent back Catch unexpected exceptions <-- to ensure that all responses are handled by the application instead of the runtime Catching expected exceptions <-- for instance LensFailures which are converted to 400s Routing <-- if we want to route traffic based on the request (eg. routes() ) HttpHandlers <-- to process the traffic Clientside \u00b6 The client-side is similar, but simpler: Debugging/Tracing <-- to ensure that we see all traffic Reporting & metrics capture <-- to record accurately what we sent back Routing <-- if we want to route traffic based on the request (eg. reverseProxy() ) Http client <-- to process the traffic Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Code \u00b6 package guide.howto.arrange_filters_into_stacks import io.micrometer.core.instrument.MeterRegistry import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.OkHttp import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.NoOp import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE import org.http4k.core.then import org.http4k.events.Event.Companion.Error import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.RequestTracing import org.http4k.filter.DebuggingFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters import org.http4k.filter.ServerFilters.CatchAll import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.routing.bind import org.http4k.routing.routes fun IncomingStack ( debug : Boolean , events : Events , registry : MeterRegistry , http : HttpHandler ): HttpHandler = RequestTracing () . then ( if ( debug ) DebuggingFilters . PrintRequestAndResponse ( System . out ) else Filter . NoOp ) . then ( ReportHttpTransaction { events ( Incoming ( it )) }) . then ( CatchAll { events ( Error ( \"Uncaught\" , it )) Response ( SERVICE_UNAVAILABLE ) }) . then ( CatchLensFailure ()) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ServerFilters . MicrometerMetrics . RequestCounter ( registry )) . then ( http ) fun OutgoingHttpStack ( debug : Boolean , events : Events , registry : MeterRegistry , http : HttpHandler ) = RequestTracing () . then ( if ( debug ) DebuggingFilters . PrintRequestAndResponse ( System . out ) else Filter . NoOp ) . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ClientFilters . MicrometerMetrics . RequestCounter ( registry )) . then ( http ) fun App ( debug : Boolean ): HttpHandler { val events : Events = :: println val registry = SimpleMeterRegistry () val outgoing = OutgoingHttpStack ( debug , events , registry , OkHttp ()) val endpoints = routes ( \"/\" bind outgoing ) return IncomingStack ( debug , events , registry , endpoints ) } fun main () { val debug = true val app = App ( debug ) // this just proxies the request to the internet app ( Request ( GET , \"https://www.http4k.org\" )) }","title":"Basic: Arrange Filters into Stacks"},{"location":"guide/howto/arrange_filters_into_stacks/#serverside","text":"A typical stack looks like: Debugging/Tracing <-- to ensure that we see all traffic Reporting & metrics capture <-- to record accurately what we sent back Catch unexpected exceptions <-- to ensure that all responses are handled by the application instead of the runtime Catching expected exceptions <-- for instance LensFailures which are converted to 400s Routing <-- if we want to route traffic based on the request (eg. routes() ) HttpHandlers <-- to process the traffic","title":"Serverside"},{"location":"guide/howto/arrange_filters_into_stacks/#clientside","text":"The client-side is similar, but simpler: Debugging/Tracing <-- to ensure that we see all traffic Reporting & metrics capture <-- to record accurately what we sent back Routing <-- if we want to route traffic based on the request (eg. reverseProxy() ) Http client <-- to process the traffic","title":"Clientside"},{"location":"guide/howto/arrange_filters_into_stacks/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/arrange_filters_into_stacks/#code","text":"package guide.howto.arrange_filters_into_stacks import io.micrometer.core.instrument.MeterRegistry import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.OkHttp import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.NoOp import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.SERVICE_UNAVAILABLE import org.http4k.core.then import org.http4k.events.Event.Companion.Error import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.RequestTracing import org.http4k.filter.DebuggingFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters import org.http4k.filter.ServerFilters.CatchAll import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.routing.bind import org.http4k.routing.routes fun IncomingStack ( debug : Boolean , events : Events , registry : MeterRegistry , http : HttpHandler ): HttpHandler = RequestTracing () . then ( if ( debug ) DebuggingFilters . PrintRequestAndResponse ( System . out ) else Filter . NoOp ) . then ( ReportHttpTransaction { events ( Incoming ( it )) }) . then ( CatchAll { events ( Error ( \"Uncaught\" , it )) Response ( SERVICE_UNAVAILABLE ) }) . then ( CatchLensFailure ()) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ServerFilters . MicrometerMetrics . RequestCounter ( registry )) . then ( http ) fun OutgoingHttpStack ( debug : Boolean , events : Events , registry : MeterRegistry , http : HttpHandler ) = RequestTracing () . then ( if ( debug ) DebuggingFilters . PrintRequestAndResponse ( System . out ) else Filter . NoOp ) . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ClientFilters . MicrometerMetrics . RequestCounter ( registry )) . then ( http ) fun App ( debug : Boolean ): HttpHandler { val events : Events = :: println val registry = SimpleMeterRegistry () val outgoing = OutgoingHttpStack ( debug , events , registry , OkHttp ()) val endpoints = routes ( \"/\" bind outgoing ) return IncomingStack ( debug , events , registry , endpoints ) } fun main () { val debug = true val app = App ( debug ) // this just proxies the request to the internet app ( Request ( GET , \"https://www.http4k.org\" )) }","title":"Code"},{"location":"guide/howto/attach_context_to_a_request/","text":"A RequestContext makes it possible to attach objects to a request whilst it is being passed down through the layers of an application. The basic concept is that there is a global shared object which holds a bag of state (indexed by Request). This state can be modified in Filters and then that state accessed inside other Filters or the terminating HttpHandler. There are 2 available choices for manipulating this data: Using simple Strings to represent the keys. Using RequestContextKey s and the Lens mechanism from the http4k-core module. Whilst the first method looks technically simpler, the use of simple Strings does not provide the type-safety of the second, which uses unique shared Key objects to guarantee non-clashing of keys and type-safety of the state. Regardless of which of the above mechanisms are used, an instance of the ServerFilters.InitialiseRequestContext Filter must wrap the HttpHandler(s) to activate the shared bag of state for each request, and to remove the state after the request is complete. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } String-based keys \u00b6 package guide.howto.attach_context_to_a_request import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters fun main () { data class SharedState ( val message : String ) fun AddState ( contexts : RequestContexts ) = Filter { next -> { contexts [ it ][ \"myKey\" ] = SharedState ( \"hello there\" ) next ( it ) } } fun PrintState ( contexts : RequestContexts ): HttpHandler = { request -> val message : SharedState? = contexts [ request ][ \"myKey\" ] println ( message ) Response ( OK ) } // this is the shared RequestContexts object - it holds the bag of state for each request and // tidies up afterwards val contexts = RequestContexts () // The first Filter is required to initialise the bag of state. // The second Filter modifies the bag // The handler just prints out the state val app = ServerFilters . InitialiseRequestContext ( contexts ) . then ( AddState ( contexts )) . then ( PrintState ( contexts )) app ( Request ( GET , \"/hello\" )) } Lens-based keys \u00b6 package guide.howto.attach_context_to_a_request import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.RequestContextKey import org.http4k.lens.RequestContextLens fun main () { data class SharedState ( val message : String ) fun AddState ( key : RequestContextLens < SharedState > ) = Filter { next -> { // \"modify\" the request like any other Lens next ( it . with ( key of SharedState ( \"hello there\" ))) } } fun PrintState ( key : RequestContextLens < SharedState > ): HttpHandler = { request -> // we can just extract the Lens state from the request like any other standard Lens println ( key ( request )) Response ( OK ) } // this is the shared RequestContexts object - it holds the bag of state for each request and // tidies up afterwards. val contexts = RequestContexts () // this Lens is the key we use to set and get the type-safe state. By using this, we gain // typesafety and the guarantee that there will be no clash of keys. // RequestContextKeys can be required, optional, or defaulted, as per the standard Lens mechanism. val key = RequestContextKey . required < SharedState > ( contexts ) // The first Filter is required to initialise the bag of state. // The second Filter modifies the bag. // The handler just prints out the state. val app = ServerFilters . InitialiseRequestContext ( contexts ) . then ( AddState ( key )) . then ( PrintState ( key )) app ( Request ( GET , \"/hello\" )) }","title":"Attach context to a request"},{"location":"guide/howto/attach_context_to_a_request/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/attach_context_to_a_request/#string-based_keys","text":"package guide.howto.attach_context_to_a_request import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters fun main () { data class SharedState ( val message : String ) fun AddState ( contexts : RequestContexts ) = Filter { next -> { contexts [ it ][ \"myKey\" ] = SharedState ( \"hello there\" ) next ( it ) } } fun PrintState ( contexts : RequestContexts ): HttpHandler = { request -> val message : SharedState? = contexts [ request ][ \"myKey\" ] println ( message ) Response ( OK ) } // this is the shared RequestContexts object - it holds the bag of state for each request and // tidies up afterwards val contexts = RequestContexts () // The first Filter is required to initialise the bag of state. // The second Filter modifies the bag // The handler just prints out the state val app = ServerFilters . InitialiseRequestContext ( contexts ) . then ( AddState ( contexts )) . then ( PrintState ( contexts )) app ( Request ( GET , \"/hello\" )) }","title":"String-based keys"},{"location":"guide/howto/attach_context_to_a_request/#lens-based_keys","text":"package guide.howto.attach_context_to_a_request import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.RequestContextKey import org.http4k.lens.RequestContextLens fun main () { data class SharedState ( val message : String ) fun AddState ( key : RequestContextLens < SharedState > ) = Filter { next -> { // \"modify\" the request like any other Lens next ( it . with ( key of SharedState ( \"hello there\" ))) } } fun PrintState ( key : RequestContextLens < SharedState > ): HttpHandler = { request -> // we can just extract the Lens state from the request like any other standard Lens println ( key ( request )) Response ( OK ) } // this is the shared RequestContexts object - it holds the bag of state for each request and // tidies up afterwards. val contexts = RequestContexts () // this Lens is the key we use to set and get the type-safe state. By using this, we gain // typesafety and the guarantee that there will be no clash of keys. // RequestContextKeys can be required, optional, or defaulted, as per the standard Lens mechanism. val key = RequestContextKey . required < SharedState > ( contexts ) // The first Filter is required to initialise the bag of state. // The second Filter modifies the bag. // The handler just prints out the state. val app = ServerFilters . InitialiseRequestContext ( contexts ) . then ( AddState ( key )) . then ( PrintState ( key )) app ( Request ( GET , \"/hello\" )) }","title":"Lens-based keys"},{"location":"guide/howto/client_as_a_function/","text":"This example demonstrates using http4k as a client, to consume HTTP services. A client is just another HttpHandler. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Code \u00b6 package guide.howto.client_as_a_function import org.http4k.client.JavaHttpClient import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request fun main () { val request = Request ( Method . GET , \"https://xkcd.com/info.0.json\" ) val client : HttpHandler = JavaHttpClient () println ( client ( request )) }","title":"Basic: Client as a function"},{"location":"guide/howto/client_as_a_function/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/client_as_a_function/#code","text":"package guide.howto.client_as_a_function import org.http4k.client.JavaHttpClient import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request fun main () { val request = Request ( Method . GET , \"https://xkcd.com/info.0.json\" ) val client : HttpHandler = JavaHttpClient () println ( client ( request )) }","title":"Code"},{"location":"guide/howto/configure_an_oauth_server/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-security-oauth\" ) } For this example, you need to configure OAuthServer instance with the correct implementations of your login pages, generation of authentication codes and access tokens. Code \u00b6 import dev.forkhandles.result4k.Failure import dev.forkhandles.result4k.Success import org.http4k.client.OkHttp import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.format.Jackson import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessToken import org.http4k.security.InsecureCookieBasedOAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.OAuthProviderConfig import org.http4k.security.oauth.server.AccessTokens import org.http4k.security.oauth.server.AuthRequest import org.http4k.security.oauth.server.AuthorizationCode import org.http4k.security.oauth.server.AuthorizationCodeDetails import org.http4k.security.oauth.server.AuthorizationCodes import org.http4k.security.oauth.server.ClientId import org.http4k.security.oauth.server.ClientValidator import org.http4k.security.oauth.server.InsecureCookieBasedAuthRequestTracking import org.http4k.security.oauth.server.OAuthServer import org.http4k.security.oauth.server.TokenRequest import org.http4k.security.oauth.server.UnsupportedGrantType import org.http4k.security.oauth.server.accesstoken.AuthorizationCodeAccessTokenRequest import org.http4k.server.Jetty import org.http4k.server.asServer import java.time.Clock import java.time.temporal.ChronoUnit.DAYS import java.util.UUID fun main () { fun authorizationServer (): RoutingHttpHandler { val server = OAuthServer ( tokenPath = \"/oauth2/token\" , authRequestTracking = InsecureCookieBasedAuthRequestTracking (), clientValidator = InsecureClientValidator (), authorizationCodes = InsecureAuthorizationCodes (), accessTokens = InsecureAccessTokens (), json = Jackson , clock = Clock . systemUTC (), documentationUri = \"See the full API docs at https://example.com/docs/access_token\" ) return routes ( server . tokenRoute , \"/my-login-page\" bind GET to server . authenticationStart . then { Response ( OK ). body ( \"\"\" \"\"\" . trimIndent () ) }, \"/my-login-page\" bind POST to server . authenticationComplete ) } fun oAuthClientApp ( tokenClient : HttpHandler ): RoutingHttpHandler { val persistence = InsecureCookieBasedOAuthPersistence ( \"oauthTest\" ) val authorizationServer = Uri . of ( \"http://localhost:9000\" ) val oauthProvider = OAuthProvider ( OAuthProviderConfig ( authorizationServer , \"/my-login-page\" , \"/oauth2/token\" , Credentials ( \"my-app\" , \"somepassword\" ) ), tokenClient , Uri . of ( \"http://localhost:8000/my-callback\" ), listOf ( \"name\" , \"age\" ), persistence ) return routes ( \"/my-callback\" bind GET to oauthProvider . callback , \"/a-protected-resource\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"user's protected resource\" ) } ) } oAuthClientApp ( OkHttp ()). asServer ( Jetty ( 8000 )). start () authorizationServer (). asServer ( Jetty ( 9000 )). start (). block () // Go to http://localhost:8000/a-protected-resource to start the authorization flow } // This class allow you to make extra checks about the oauth client during the flow class InsecureClientValidator : ClientValidator { // the client id should be a registered one override fun validateClientId ( request : Request , clientId : ClientId ): Boolean = true // one should only redirect to URLs registered against a particular client override fun validateRedirection ( request : Request , clientId : ClientId , redirectionUri : Uri ): Boolean = true // one should validate the scopes are correct for that client override fun validateScopes ( request : Request , clientId : ClientId , scopes : List < String > ): Boolean = true // certain operations can only be performed by fully authenticated clients // e.g. generate access tokens override fun validateCredentials ( request : Request , clientId : ClientId , clientSecret : String ): Boolean = true } class InsecureAuthorizationCodes : AuthorizationCodes { private val clock = Clock . systemUTC () private val codes = mutableMapOf < AuthorizationCode , AuthorizationCodeDetails > () override fun detailsFor ( code : AuthorizationCode ) = codes [ code ] ?: error ( \"code not stored\" ) // Authorization codes should be associated // to a particular user (who can be identified in the Response) // so they can be checked in various stages of the authorization flow override fun create ( request : Request , authRequest : AuthRequest , response : Response ) = Success ( AuthorizationCode ( UUID . randomUUID (). toString ()). also { codes [ it ] = AuthorizationCodeDetails ( authRequest . client , authRequest . redirectUri !! , clock . instant (). plus ( 1 , DAYS ), authRequest . state , authRequest . isOIDC () ) }) } class InsecureAccessTokens : AccessTokens { override fun create ( clientId : ClientId , tokenRequest : TokenRequest ) = Failure ( UnsupportedGrantType ( \"client_credentials\" )) // an access token should be associated with a particular authorization flow // (i.e. limited to the requested scopes), and contain an expiration date override fun create ( clientId : ClientId , tokenRequest : AuthorizationCodeAccessTokenRequest ) = Success ( AccessToken ( UUID . randomUUID (). toString ())) }","title":"Configure an OAuth Server"},{"location":"guide/howto/configure_an_oauth_server/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-security-oauth\" ) } For this example, you need to configure OAuthServer instance with the correct implementations of your login pages, generation of authentication codes and access tokens.","title":"Gradle setup"},{"location":"guide/howto/configure_an_oauth_server/#code","text":"import dev.forkhandles.result4k.Failure import dev.forkhandles.result4k.Success import org.http4k.client.OkHttp import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.format.Jackson import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessToken import org.http4k.security.InsecureCookieBasedOAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.OAuthProviderConfig import org.http4k.security.oauth.server.AccessTokens import org.http4k.security.oauth.server.AuthRequest import org.http4k.security.oauth.server.AuthorizationCode import org.http4k.security.oauth.server.AuthorizationCodeDetails import org.http4k.security.oauth.server.AuthorizationCodes import org.http4k.security.oauth.server.ClientId import org.http4k.security.oauth.server.ClientValidator import org.http4k.security.oauth.server.InsecureCookieBasedAuthRequestTracking import org.http4k.security.oauth.server.OAuthServer import org.http4k.security.oauth.server.TokenRequest import org.http4k.security.oauth.server.UnsupportedGrantType import org.http4k.security.oauth.server.accesstoken.AuthorizationCodeAccessTokenRequest import org.http4k.server.Jetty import org.http4k.server.asServer import java.time.Clock import java.time.temporal.ChronoUnit.DAYS import java.util.UUID fun main () { fun authorizationServer (): RoutingHttpHandler { val server = OAuthServer ( tokenPath = \"/oauth2/token\" , authRequestTracking = InsecureCookieBasedAuthRequestTracking (), clientValidator = InsecureClientValidator (), authorizationCodes = InsecureAuthorizationCodes (), accessTokens = InsecureAccessTokens (), json = Jackson , clock = Clock . systemUTC (), documentationUri = \"See the full API docs at https://example.com/docs/access_token\" ) return routes ( server . tokenRoute , \"/my-login-page\" bind GET to server . authenticationStart . then { Response ( OK ). body ( \"\"\" \"\"\" . trimIndent () ) }, \"/my-login-page\" bind POST to server . authenticationComplete ) } fun oAuthClientApp ( tokenClient : HttpHandler ): RoutingHttpHandler { val persistence = InsecureCookieBasedOAuthPersistence ( \"oauthTest\" ) val authorizationServer = Uri . of ( \"http://localhost:9000\" ) val oauthProvider = OAuthProvider ( OAuthProviderConfig ( authorizationServer , \"/my-login-page\" , \"/oauth2/token\" , Credentials ( \"my-app\" , \"somepassword\" ) ), tokenClient , Uri . of ( \"http://localhost:8000/my-callback\" ), listOf ( \"name\" , \"age\" ), persistence ) return routes ( \"/my-callback\" bind GET to oauthProvider . callback , \"/a-protected-resource\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"user's protected resource\" ) } ) } oAuthClientApp ( OkHttp ()). asServer ( Jetty ( 8000 )). start () authorizationServer (). asServer ( Jetty ( 9000 )). start (). block () // Go to http://localhost:8000/a-protected-resource to start the authorization flow } // This class allow you to make extra checks about the oauth client during the flow class InsecureClientValidator : ClientValidator { // the client id should be a registered one override fun validateClientId ( request : Request , clientId : ClientId ): Boolean = true // one should only redirect to URLs registered against a particular client override fun validateRedirection ( request : Request , clientId : ClientId , redirectionUri : Uri ): Boolean = true // one should validate the scopes are correct for that client override fun validateScopes ( request : Request , clientId : ClientId , scopes : List < String > ): Boolean = true // certain operations can only be performed by fully authenticated clients // e.g. generate access tokens override fun validateCredentials ( request : Request , clientId : ClientId , clientSecret : String ): Boolean = true } class InsecureAuthorizationCodes : AuthorizationCodes { private val clock = Clock . systemUTC () private val codes = mutableMapOf < AuthorizationCode , AuthorizationCodeDetails > () override fun detailsFor ( code : AuthorizationCode ) = codes [ code ] ?: error ( \"code not stored\" ) // Authorization codes should be associated // to a particular user (who can be identified in the Response) // so they can be checked in various stages of the authorization flow override fun create ( request : Request , authRequest : AuthRequest , response : Response ) = Success ( AuthorizationCode ( UUID . randomUUID (). toString ()). also { codes [ it ] = AuthorizationCodeDetails ( authRequest . client , authRequest . redirectUri !! , clock . instant (). plus ( 1 , DAYS ), authRequest . state , authRequest . isOIDC () ) }) } class InsecureAccessTokens : AccessTokens { override fun create ( clientId : ClientId , tokenRequest : TokenRequest ) = Failure ( UnsupportedGrantType ( \"client_credentials\" )) // an access token should be associated with a particular authorization flow // (i.e. limited to the requested scopes), and contain an expiration date override fun create ( clientId : ClientId , tokenRequest : AuthorizationCodeAccessTokenRequest ) = Success ( AccessToken ( UUID . randomUUID (). toString ())) }","title":"Code"},{"location":"guide/howto/create_a_custom_json_marshaller/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) } Custom auto-mapping JSON configurations \u00b6 http4k declares an extended set of \"primitive\" types which it can marshall out of the box - this includes the various http4k primitives (Uri, Status), as well as a bunch of common types from the JDK such as the DateTime classes and Exceptions. These primitives types cannot be marshalled as top-level JSON structures on their own so should be contained in a custom wrapper class before transmission. You can declare your own custom marshaller by reimplementing the Json instance and adding mappings for your own types - either uni or bi-directional. This ability to render custom types through different JSON marshallers allows API users to provide different \"views\" for different purposes - for example we may wish to hide the values of some fields in the output, as below: Example - Representing MicroTypes/TinyTypes as Strings in JSON \u00b6 MicroTypes (aka Tiny Types) are popular in Kotlin providing type-safety throughout a codebase, ensuring amongst other things that method parameters are not accidentally permuted. An example of a simple microtype might be: data class CustomerName ( val value : String ) data class Customer ( val name : CustomerName ) Using the standard mapper, a Customer \"Bob\", would be represented as the json Customer ( name = CustomerName ( \"Bob\" )) { \"name\" : { \"value\" : \"Bob\" } } However, it might be preferable to represent CustomerName as a plain string: { \"name\" : \"Bob\" } To achieve this, there are a few simple steps - this example uses Jackson, but there are equivalent configuration schemes for the other supported JSON libraries Use the http4k ConfigurableJackson to get a base configuration object MyJackson : ConfigurableJackson ( // to be filled in ) Modify it to meet your needs, registering type adapters for your types object MyJackson : ConfigurableJackson ( KotlinModule . Builder . Build () // register kotlin types . asConfigurable () . withStandardMappings () // http4k out-of-the box extras . text ( :: CustomerName , CustomerName :: value ) // here is the registration of custom type // .text(...) - repeat the registration for each type . done () . deactivateDefaultTyping () // other Jackson config... . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) Reference this configuration in your code - particularly where using the Body.auto pattern import guide.howto.create_a_custom_json_marshaller.MyJackson.auto val lens = Body . auto < Customer > (). toLens () // ... continue as before Example - Representing MicroTypes using Values4k as Strings in JSON \u00b6 This example uses value types from Values4k Firstly, define a value type using the standard values4k mechanism - note that the companion object extends ValueFactory - this will be referenced in the type adapter later. The ValueFactory also provides a number of convenience methods CustomerName.of() , parse() , unwrap() , and a mechanism to validate the format of strings - very convenient to ensure that values are semantically valid throughout the entire system. class CustomerName ( value : String ) : StringValue ( value ) { companion object : StringValueFactory < CustomerName > ( :: CustomerName ) } Then, define a ConfigurableJackson (Moshi...) with a type adaptor for your type object MyJackson : ConfigurableJackson ( KotlinModule . Builder . Build () . asConfigurable () . withStandardMappings () . value ( CustomerName ) // this references the CustomerName companion object // .value(...) - repeat the registration for each type . done () . deactivateDefaultTyping () // other Jackson config... . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) A full worked example is shown below. Code \u00b6 package guide.howto.create_a_custom_json_marshaller import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.KotlinModule // this import is important so you don't pick up the standard auto method! import guide.howto.create_a_custom_json_marshaller.MyJackson.auto import org.http4k.core.Body import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.ConfigurableJackson import org.http4k.format.asConfigurable import org.http4k.format.text import org.http4k.format.withStandardMappings object MyJackson : ConfigurableJackson ( KotlinModule . Builder (). build () . asConfigurable () . withStandardMappings () // declare custom mapping for our own types - this one represents our type as a // simple String . text ( :: PublicType , PublicType :: value ) // ... and this one shows a masked value and cannot be deserialised // (as the mapping is only one way) . text ( SecretType :: toString ) . done () . deactivateDefaultTyping () . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) data class PublicType ( val value : String ) data class SecretType ( val value : String ) { override fun toString (): String { return \"****\" } } data class MyType ( val public : PublicType , val hidden : SecretType ) fun main () { println ( Response ( OK ). with ( Body . auto < MyType > (). toLens () of MyType ( PublicType ( \"hello\" ), SecretType ( \"secret\" ) ) ) ) /** Prints: HTTP/1.1 200 OK content-type: application/json; charset=utf-8 {\"public\":\"hello\",\"hidden\":\"****\"} */ }","title":"Create a custom JSON marshaller"},{"location":"guide/howto/create_a_custom_json_marshaller/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) }","title":"Gradle setup"},{"location":"guide/howto/create_a_custom_json_marshaller/#custom_auto-mapping_json_configurations","text":"http4k declares an extended set of \"primitive\" types which it can marshall out of the box - this includes the various http4k primitives (Uri, Status), as well as a bunch of common types from the JDK such as the DateTime classes and Exceptions. These primitives types cannot be marshalled as top-level JSON structures on their own so should be contained in a custom wrapper class before transmission. You can declare your own custom marshaller by reimplementing the Json instance and adding mappings for your own types - either uni or bi-directional. This ability to render custom types through different JSON marshallers allows API users to provide different \"views\" for different purposes - for example we may wish to hide the values of some fields in the output, as below:","title":"Custom auto-mapping JSON configurations"},{"location":"guide/howto/create_a_custom_json_marshaller/#example_-_representing_microtypestinytypes_as_strings_in_json","text":"MicroTypes (aka Tiny Types) are popular in Kotlin providing type-safety throughout a codebase, ensuring amongst other things that method parameters are not accidentally permuted. An example of a simple microtype might be: data class CustomerName ( val value : String ) data class Customer ( val name : CustomerName ) Using the standard mapper, a Customer \"Bob\", would be represented as the json Customer ( name = CustomerName ( \"Bob\" )) { \"name\" : { \"value\" : \"Bob\" } } However, it might be preferable to represent CustomerName as a plain string: { \"name\" : \"Bob\" } To achieve this, there are a few simple steps - this example uses Jackson, but there are equivalent configuration schemes for the other supported JSON libraries Use the http4k ConfigurableJackson to get a base configuration object MyJackson : ConfigurableJackson ( // to be filled in ) Modify it to meet your needs, registering type adapters for your types object MyJackson : ConfigurableJackson ( KotlinModule . Builder . Build () // register kotlin types . asConfigurable () . withStandardMappings () // http4k out-of-the box extras . text ( :: CustomerName , CustomerName :: value ) // here is the registration of custom type // .text(...) - repeat the registration for each type . done () . deactivateDefaultTyping () // other Jackson config... . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) Reference this configuration in your code - particularly where using the Body.auto pattern import guide.howto.create_a_custom_json_marshaller.MyJackson.auto val lens = Body . auto < Customer > (). toLens () // ... continue as before","title":"Example - Representing MicroTypes/TinyTypes as Strings in JSON"},{"location":"guide/howto/create_a_custom_json_marshaller/#example_-_representing_microtypes_using_values4k_as_strings_in_json","text":"This example uses value types from Values4k Firstly, define a value type using the standard values4k mechanism - note that the companion object extends ValueFactory - this will be referenced in the type adapter later. The ValueFactory also provides a number of convenience methods CustomerName.of() , parse() , unwrap() , and a mechanism to validate the format of strings - very convenient to ensure that values are semantically valid throughout the entire system. class CustomerName ( value : String ) : StringValue ( value ) { companion object : StringValueFactory < CustomerName > ( :: CustomerName ) } Then, define a ConfigurableJackson (Moshi...) with a type adaptor for your type object MyJackson : ConfigurableJackson ( KotlinModule . Builder . Build () . asConfigurable () . withStandardMappings () . value ( CustomerName ) // this references the CustomerName companion object // .value(...) - repeat the registration for each type . done () . deactivateDefaultTyping () // other Jackson config... . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) A full worked example is shown below.","title":"Example - Representing MicroTypes using Values4k as Strings in JSON"},{"location":"guide/howto/create_a_custom_json_marshaller/#code","text":"package guide.howto.create_a_custom_json_marshaller import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.module.kotlin.KotlinModule // this import is important so you don't pick up the standard auto method! import guide.howto.create_a_custom_json_marshaller.MyJackson.auto import org.http4k.core.Body import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.ConfigurableJackson import org.http4k.format.asConfigurable import org.http4k.format.text import org.http4k.format.withStandardMappings object MyJackson : ConfigurableJackson ( KotlinModule . Builder (). build () . asConfigurable () . withStandardMappings () // declare custom mapping for our own types - this one represents our type as a // simple String . text ( :: PublicType , PublicType :: value ) // ... and this one shows a masked value and cannot be deserialised // (as the mapping is only one way) . text ( SecretType :: toString ) . done () . deactivateDefaultTyping () . configure ( DeserializationFeature . FAIL_ON_UNKNOWN_PROPERTIES , false ) ) data class PublicType ( val value : String ) data class SecretType ( val value : String ) { override fun toString (): String { return \"****\" } } data class MyType ( val public : PublicType , val hidden : SecretType ) fun main () { println ( Response ( OK ). with ( Body . auto < MyType > (). toLens () of MyType ( PublicType ( \"hello\" ), SecretType ( \"secret\" ) ) ) ) /** Prints: HTTP/1.1 200 OK content-type: application/json; charset=utf-8 {\"public\":\"hello\",\"hidden\":\"****\"} */ }","title":"Code"},{"location":"guide/howto/create_a_swagger_ui/","text":"Http4k makes it easy to include Swagger UI or Redoc in your application. These UIs can often replace traditional hand-written documentation for API consumers to learn your API, and can even serve as useful debugging tools. Build the OpenAPI spec \u00b6 Swagger UI and Redoc both require an OpenApi v2 or v3 description to function. Http4k can generate a description for your API with the http4k-contract module, but any hand-crafted or external description can be used as well. For more detail on generating OpenAPI descriptions, see: Http4k Reference: Contracts http4k How-to: Integrate with OpenAPI Example \u00b6 This simple description will be used for all examples in this guide: package guide.howto.create_a_swagger_ui import org.http4k.contract.ContractRoutingHttpHandler import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.with import org.http4k.lens.string fun createContractHandler ( descriptionPath : String ): ContractRoutingHttpHandler { val greetingLens = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Define a single http route for our contract val helloHandler = \"/v1/hello\" meta { operationId = \"v1Hello\" summary = \"Say Hello\" returning ( Status . OK , greetingLens to \"Sample Greeting\" ) } bindContract Method . GET to { _ : Request -> Response ( Status . OK ). with ( greetingLens of \"HI!\" ) } // Define a contract, and render an OpenApi 3 spec at the given path return contract { routes += helloHandler renderer = OpenApi3 ( ApiInfo ( \"Hello Server - Developer UI\" , \"99.3.4\" ) ) this . descriptionPath = descriptionPath } } Build the UI \u00b6 The http4-contract module includes functions to configure and serve Swagger UI, Redoc, or both. These \"lite\" UIs are thin; meaning most of the assets are pulled from an external Public CDN. Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) } Example \u00b6 package guide.howto.create_a_swagger_ui import org.http4k.contract.ui.redocLite import org.http4k.contract.ui.swaggerUiLite import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val http = routes ( // bind the API and OpenApi description off of root createContractHandler ( \"/openapi.json\" ), // bind Swagger UI to the root path swaggerUiLite { url = \"/openapi.json\" pageTitle = \"Hello Server - Swagger UI\" persistAuthorization = true }, // Bind Redoc to another path \"/redoc\" bind redocLite { url = \"/openapi.json\" pageTitle = \"Hello Server - Redoc\" options [ \"disable-search\" ] = \"true\" } ) // run the server. The default UI is available at http://localhost:8080 http . asServer ( SunHttp ( 8080 )) . start () . block () } Bundle the UI with Webjars \u00b6 The \"lite\" UIs included in the http4k-contract module are great for serverless APIs, where binary size is a major concern. For more control over the assets, http4k has optional modules to bundle the assets into your application. Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract-ui-swagger\" ) implementation ( \"org.http4k:http4k-contract-ui-redoc\" ) } You can pick and choose whether you want Redoc, Swagger UI, or both bundled with your application. Example \u00b6 package guide.howto.create_a_swagger_ui import org.http4k.contract.ui.redoc.redocWebjar import org.http4k.contract.ui.swagger.swaggerUiWebjar import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val http = routes ( // bind the API and OpenApi description off of root createContractHandler ( \"/openapi.json\" ), // Bind Redoc to the root path redocWebjar { url = \"/openapi.json\" pageTitle = \"Hello Server - WebJar\" options [ \"disable-search\" ] = \"true\" }, // Bind Swagger UI to another path \"/swagger\" bind swaggerUiWebjar { url = \"/openapi.json\" pageTitle = \"Hello Server\" displayOperationId = true } ) // run the server. The default UI is available at http://localhost:8080 http . asServer ( SunHttp ( 8080 )) . start () . block () }","title":"Redoc and Swagger UI"},{"location":"guide/howto/create_a_swagger_ui/#build_the_openapi_spec","text":"Swagger UI and Redoc both require an OpenApi v2 or v3 description to function. Http4k can generate a description for your API with the http4k-contract module, but any hand-crafted or external description can be used as well. For more detail on generating OpenAPI descriptions, see: Http4k Reference: Contracts http4k How-to: Integrate with OpenAPI","title":"Build the OpenAPI spec"},{"location":"guide/howto/create_a_swagger_ui/#example","text":"This simple description will be used for all examples in this guide: package guide.howto.create_a_swagger_ui import org.http4k.contract.ContractRoutingHttpHandler import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.with import org.http4k.lens.string fun createContractHandler ( descriptionPath : String ): ContractRoutingHttpHandler { val greetingLens = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Define a single http route for our contract val helloHandler = \"/v1/hello\" meta { operationId = \"v1Hello\" summary = \"Say Hello\" returning ( Status . OK , greetingLens to \"Sample Greeting\" ) } bindContract Method . GET to { _ : Request -> Response ( Status . OK ). with ( greetingLens of \"HI!\" ) } // Define a contract, and render an OpenApi 3 spec at the given path return contract { routes += helloHandler renderer = OpenApi3 ( ApiInfo ( \"Hello Server - Developer UI\" , \"99.3.4\" ) ) this . descriptionPath = descriptionPath } }","title":"Example"},{"location":"guide/howto/create_a_swagger_ui/#build_the_ui","text":"The http4-contract module includes functions to configure and serve Swagger UI, Redoc, or both. These \"lite\" UIs are thin; meaning most of the assets are pulled from an external Public CDN.","title":"Build the UI"},{"location":"guide/howto/create_a_swagger_ui/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) }","title":"Installation (Gradle)"},{"location":"guide/howto/create_a_swagger_ui/#example_1","text":"package guide.howto.create_a_swagger_ui import org.http4k.contract.ui.redocLite import org.http4k.contract.ui.swaggerUiLite import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val http = routes ( // bind the API and OpenApi description off of root createContractHandler ( \"/openapi.json\" ), // bind Swagger UI to the root path swaggerUiLite { url = \"/openapi.json\" pageTitle = \"Hello Server - Swagger UI\" persistAuthorization = true }, // Bind Redoc to another path \"/redoc\" bind redocLite { url = \"/openapi.json\" pageTitle = \"Hello Server - Redoc\" options [ \"disable-search\" ] = \"true\" } ) // run the server. The default UI is available at http://localhost:8080 http . asServer ( SunHttp ( 8080 )) . start () . block () }","title":"Example"},{"location":"guide/howto/create_a_swagger_ui/#bundle_the_ui_with_webjars","text":"The \"lite\" UIs included in the http4k-contract module are great for serverless APIs, where binary size is a major concern. For more control over the assets, http4k has optional modules to bundle the assets into your application.","title":"Bundle the UI with Webjars"},{"location":"guide/howto/create_a_swagger_ui/#installation_gradle_1","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract-ui-swagger\" ) implementation ( \"org.http4k:http4k-contract-ui-redoc\" ) } You can pick and choose whether you want Redoc, Swagger UI, or both bundled with your application.","title":"Installation (Gradle)"},{"location":"guide/howto/create_a_swagger_ui/#example_2","text":"package guide.howto.create_a_swagger_ui import org.http4k.contract.ui.redoc.redocWebjar import org.http4k.contract.ui.swagger.swaggerUiWebjar import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val http = routes ( // bind the API and OpenApi description off of root createContractHandler ( \"/openapi.json\" ), // Bind Redoc to the root path redocWebjar { url = \"/openapi.json\" pageTitle = \"Hello Server - WebJar\" options [ \"disable-search\" ] = \"true\" }, // Bind Swagger UI to another path \"/swagger\" bind swaggerUiWebjar { url = \"/openapi.json\" pageTitle = \"Hello Server\" displayOperationId = true } ) // run the server. The default UI is available at http://localhost:8080 http . asServer ( SunHttp ( 8080 )) . start () . block () }","title":"Example"},{"location":"guide/howto/customise_a_server_backend/","text":"How to write a custom server implmentation \u00b6 Whilst the http4k server modules ship with a sensibly configured standard server-backend setup, a lot of projects will require specialised implementations of the underlying server backend. http4k makes this easy with the ServerConfig interface. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) } The example below shows a customised Jetty setup which enables HTTPS traffic by reimplementing the ServerConfig interface. The idea is that this single class will encapsulate the usage of the Server platform API behind the http4k abstraction and provide a simple way to reuse it across different applications. Code \u00b6 package guide.howto.customise_a_server_backend import org.eclipse.jetty.server.HttpConfiguration import org.eclipse.jetty.server.HttpConnectionFactory import org.eclipse.jetty.server.SecureRequestCustomizer import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.SslConnectionFactory import org.eclipse.jetty.util.ssl.SslContextFactory import org.http4k.core.HttpHandler import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.server.Http4kServer import org.http4k.server.ServerConfig import org.http4k.server.asServer import org.http4k.server.toJettyHandler class SecureJetty ( private val sslPort : Int , private val localKeyStorePath : String , private val localKeystorePassword : String , private val localKeyManagerPassword : String ) : ServerConfig { override fun toServer ( http : HttpHandler ): Http4kServer { val server = Server (). apply { val https = HttpConfiguration (). apply { addCustomizer ( SecureRequestCustomizer ()) } val sslContextFactory = SslContextFactory . Server (). apply { keyStorePath = localKeyStorePath keyStorePassword = localKeystorePassword keyManagerPassword = localKeyManagerPassword } connectors = arrayOf ( ServerConnector ( server , SslConnectionFactory ( sslContextFactory , \"http/1.1\" ), HttpConnectionFactory ( https ) ). apply { port = sslPort } ) insertHandler ( http . toJettyHandler ()) } return object : Http4kServer { override fun start (): Http4kServer = apply { server . start () } override fun stop (): Http4kServer = apply { server . stop () } override fun port (): Int = if ( sslPort > 0 ) sslPort else server . uri . port } } } fun main () { PrintRequestAndResponse (). then { Response ( Status . OK ). body ( \"hello from secure jetty!\" ) } . asServer ( SecureJetty ( sslPort = 9000 , localKeyStorePath = \"keystore.jks\" , localKeystorePassword = \"password\" , localKeyManagerPassword = \"password\" ) ). start () }","title":"Customise a Server backend"},{"location":"guide/howto/customise_a_server_backend/#how_to_write_a_custom_server_implmentation","text":"Whilst the http4k server modules ship with a sensibly configured standard server-backend setup, a lot of projects will require specialised implementations of the underlying server backend. http4k makes this easy with the ServerConfig interface.","title":"How to write a custom server implmentation"},{"location":"guide/howto/customise_a_server_backend/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) } The example below shows a customised Jetty setup which enables HTTPS traffic by reimplementing the ServerConfig interface. The idea is that this single class will encapsulate the usage of the Server platform API behind the http4k abstraction and provide a simple way to reuse it across different applications.","title":"Gradle setup"},{"location":"guide/howto/customise_a_server_backend/#code","text":"package guide.howto.customise_a_server_backend import org.eclipse.jetty.server.HttpConfiguration import org.eclipse.jetty.server.HttpConnectionFactory import org.eclipse.jetty.server.SecureRequestCustomizer import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.SslConnectionFactory import org.eclipse.jetty.util.ssl.SslContextFactory import org.http4k.core.HttpHandler import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.server.Http4kServer import org.http4k.server.ServerConfig import org.http4k.server.asServer import org.http4k.server.toJettyHandler class SecureJetty ( private val sslPort : Int , private val localKeyStorePath : String , private val localKeystorePassword : String , private val localKeyManagerPassword : String ) : ServerConfig { override fun toServer ( http : HttpHandler ): Http4kServer { val server = Server (). apply { val https = HttpConfiguration (). apply { addCustomizer ( SecureRequestCustomizer ()) } val sslContextFactory = SslContextFactory . Server (). apply { keyStorePath = localKeyStorePath keyStorePassword = localKeystorePassword keyManagerPassword = localKeyManagerPassword } connectors = arrayOf ( ServerConnector ( server , SslConnectionFactory ( sslContextFactory , \"http/1.1\" ), HttpConnectionFactory ( https ) ). apply { port = sslPort } ) insertHandler ( http . toJettyHandler ()) } return object : Http4kServer { override fun start (): Http4kServer = apply { server . start () } override fun stop (): Http4kServer = apply { server . stop () } override fun port (): Int = if ( sslPort > 0 ) sslPort else server . uri . port } } } fun main () { PrintRequestAndResponse (). then { Response ( Status . OK ). body ( \"hello from secure jetty!\" ) } . asServer ( SecureJetty ( sslPort = 9000 , localKeyStorePath = \"keystore.jks\" , localKeystorePassword = \"password\" , localKeyManagerPassword = \"password\" ) ). start () }","title":"Code"},{"location":"guide/howto/deploy_webjars/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) // for the example... implementation ( \"org.webjars:swagger-ui\" , version = \"3.43.0\" ) } WebJars is a library to ship pre-packaged Web assets on your classpath by just adding the dependency. The assets are rehoused under the META-INF directory and end up with URLs such as: http://localhost:8080/webjars/swagger-ui/3.43.0/index.html http4k integrates this functionality into the core library and ships with the webJars() router plugin to activate. As the plugin is just an HttpHandler , the simplest example is just to launch WebJars directly as a Server: webJars (). asServer ( SunHttp ( 8080 )). start () ... or a more standard use-case is to mix it into your application routing as in the example below: Code \u00b6 package guide.howto.deploy_webjars import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.routing.webJars import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // mix the WebJars routing into your app... val app = routes ( \"/myGreatRoute\" bind GET to { _ : Request -> Response ( OK ) }, webJars () ) app . asServer ( SunHttp ( 8080 )). start () // then browse to: http://localhost:8080/webjars/swagger-ui/5.1.3/index.html }","title":"Deploy WebJars"},{"location":"guide/howto/deploy_webjars/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) // for the example... implementation ( \"org.webjars:swagger-ui\" , version = \"3.43.0\" ) } WebJars is a library to ship pre-packaged Web assets on your classpath by just adding the dependency. The assets are rehoused under the META-INF directory and end up with URLs such as: http://localhost:8080/webjars/swagger-ui/3.43.0/index.html http4k integrates this functionality into the core library and ships with the webJars() router plugin to activate. As the plugin is just an HttpHandler , the simplest example is just to launch WebJars directly as a Server: webJars (). asServer ( SunHttp ( 8080 )). start () ... or a more standard use-case is to mix it into your application routing as in the example below:","title":"Gradle setup"},{"location":"guide/howto/deploy_webjars/#code","text":"package guide.howto.deploy_webjars import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.routing.webJars import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // mix the WebJars routing into your app... val app = routes ( \"/myGreatRoute\" bind GET to { _ : Request -> Response ( OK ) }, webJars () ) app . asServer ( SunHttp ( 8080 )). start () // then browse to: http://localhost:8080/webjars/swagger-ui/5.1.3/index.html }","title":"Code"},{"location":"guide/howto/integrate_with_openapi/","text":"This contract example shows: 2 endpoints with typesafe contracts (marshalling of path parameters and bodies) Custom filters (reporting latency) API key security via a typesafe Query parameter (this can be a header or a body parameter as well) A parameter lens that provides metadata for the output OpenApi schema node OpenApi v3 documentation - Run this example and point a browser here Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-contract\" ) implementation ( \"org.http4k:http4k-format-argo\" ) } Note: although we use Argo here as our JSON API, you could also switch in any of the http4k-format-xxx JSON modules. Code \u00b6 package guide.howto.integrate_with_openapi import org.http4k.contract.bind import org.http4k.contract.contract import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.ApiServer import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.ApiKeySecurity import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.CachingFilters.CacheResponse.NoCache import org.http4k.filter.CorsPolicy import org.http4k.filter.ResponseFilters import org.http4k.filter.ServerFilters import org.http4k.format.Argo import org.http4k.format.Jackson import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.string import org.http4k.routing.ResourceLoader.Companion.Classpath import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.routing.static import org.http4k.server.Jetty import org.http4k.server.asServer import java.time.Clock fun main () { fun add ( value1 : Int , value2 : Int ): HttpHandler = { Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of ( value1 + value2 ). toString () ) } val ageQuery = Query . int (). required ( \"age\" , \"Your age\" , mapOf ( \"schema\" to mapOf ( \"minimum\" to 18 ))) fun echo ( name : String ): HttpHandler = { Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of \"hello $ name you are ${ ageQuery ( it ) } \" ) } val filter : Filter = ResponseFilters . ReportHttpTransaction ( Clock . systemUTC ()) { tx : HttpTransaction -> println ( tx . labels . toString () + \" took \" + tx . duration ) } val mySecurity = ApiKeySecurity ( Query . int (). required ( \"apiKey\" ), { it == 42 }) val contract = contract { renderer = OpenApi3 ( ApiInfo ( \"my great api\" , \"v1.0\" ), Argo , servers = listOf ( ApiServer ( Uri . of ( \"http://localhost:8000\" ), \"the greatest server\" )) ) descriptionPath = \"/docs/openapi.json\" security = mySecurity routes += \"/ping\" meta { summary = \"add\" description = \"Adds 2 numbers together\" returning ( OK to \"The result\" ) } bindContract GET to { _ -> Response ( OK ). body ( \"pong\" ) } routes += \"/add\" / Path . int (). of ( \"value1\" ) / Path . int (). of ( \"value2\" ) meta { summary = \"add\" description = \"Adds 2 numbers together\" returning ( OK to \"The result\" ) } bindContract GET to :: add // note here that the trailing parameter can be ignored - it would simply be the value \"divide\". routes += Path . int (). of ( \"value1\" ) / Path . int (). of ( \"value2\" ) / \"divide\" meta { summary = \"divide\" description = \"Divides 2 numbers\" returning ( OK to \"The result\" ) } bindContract GET to { first , second , _ -> { Response ( OK ). body (( first / second ). toString ()) } } routes += \"/echo\" / Path . of ( \"name\" ) meta { summary = \"echo\" queries += ageQuery } bindContract GET to :: echo } val handler = routes ( \"/context\" bind filter . then ( contract ), \"/static\" bind NoCache (). then ( static ( Classpath ( \"guide/howto/nestable_routes\" ))), \"/\" bind contract { renderer = OpenApi3 ( ApiInfo ( \"my great super api\" , \"v1.0\" ), Jackson ) routes += \"/echo\" / Path . of ( \"name\" ) meta { summary = \"echo\" queries += ageQuery } bindContract GET to :: echo } ) ServerFilters . Cors ( CorsPolicy . UnsafeGlobalPermissive ). then ( handler ). asServer ( Jetty ( 8000 )) . start () } // Ping! curl -v \"http://localhost:8000/context/ping?apiKey=42\" // Adding 2 numbers: curl -v \"http://localhost:8000/context/add/123/564?apiKey=42\" // Echo (fail): curl -v \"http://localhost:8000/context/echo/myName?age=notANumber&apiKey=42\" // API Key enforcement: curl -v \"http://localhost:8000/context/add/123/564?apiKey=444\" // Static content: curl -v \"http://localhost:8000/static/someStaticFile.txt\" // OpenApi/Swagger documentation: curl -v \"http://localhost:8000/context/docs/openapi.json\" // Echo endpoint (at root): curl -v \"http://localhost:8000/echo/hello?age=123\"","title":"Integrate with OpenAPI"},{"location":"guide/howto/integrate_with_openapi/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-contract\" ) implementation ( \"org.http4k:http4k-format-argo\" ) } Note: although we use Argo here as our JSON API, you could also switch in any of the http4k-format-xxx JSON modules.","title":"Gradle setup"},{"location":"guide/howto/integrate_with_openapi/#code","text":"package guide.howto.integrate_with_openapi import org.http4k.contract.bind import org.http4k.contract.contract import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.ApiServer import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.ApiKeySecurity import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.CachingFilters.CacheResponse.NoCache import org.http4k.filter.CorsPolicy import org.http4k.filter.ResponseFilters import org.http4k.filter.ServerFilters import org.http4k.format.Argo import org.http4k.format.Jackson import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.string import org.http4k.routing.ResourceLoader.Companion.Classpath import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.routing.static import org.http4k.server.Jetty import org.http4k.server.asServer import java.time.Clock fun main () { fun add ( value1 : Int , value2 : Int ): HttpHandler = { Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of ( value1 + value2 ). toString () ) } val ageQuery = Query . int (). required ( \"age\" , \"Your age\" , mapOf ( \"schema\" to mapOf ( \"minimum\" to 18 ))) fun echo ( name : String ): HttpHandler = { Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of \"hello $ name you are ${ ageQuery ( it ) } \" ) } val filter : Filter = ResponseFilters . ReportHttpTransaction ( Clock . systemUTC ()) { tx : HttpTransaction -> println ( tx . labels . toString () + \" took \" + tx . duration ) } val mySecurity = ApiKeySecurity ( Query . int (). required ( \"apiKey\" ), { it == 42 }) val contract = contract { renderer = OpenApi3 ( ApiInfo ( \"my great api\" , \"v1.0\" ), Argo , servers = listOf ( ApiServer ( Uri . of ( \"http://localhost:8000\" ), \"the greatest server\" )) ) descriptionPath = \"/docs/openapi.json\" security = mySecurity routes += \"/ping\" meta { summary = \"add\" description = \"Adds 2 numbers together\" returning ( OK to \"The result\" ) } bindContract GET to { _ -> Response ( OK ). body ( \"pong\" ) } routes += \"/add\" / Path . int (). of ( \"value1\" ) / Path . int (). of ( \"value2\" ) meta { summary = \"add\" description = \"Adds 2 numbers together\" returning ( OK to \"The result\" ) } bindContract GET to :: add // note here that the trailing parameter can be ignored - it would simply be the value \"divide\". routes += Path . int (). of ( \"value1\" ) / Path . int (). of ( \"value2\" ) / \"divide\" meta { summary = \"divide\" description = \"Divides 2 numbers\" returning ( OK to \"The result\" ) } bindContract GET to { first , second , _ -> { Response ( OK ). body (( first / second ). toString ()) } } routes += \"/echo\" / Path . of ( \"name\" ) meta { summary = \"echo\" queries += ageQuery } bindContract GET to :: echo } val handler = routes ( \"/context\" bind filter . then ( contract ), \"/static\" bind NoCache (). then ( static ( Classpath ( \"guide/howto/nestable_routes\" ))), \"/\" bind contract { renderer = OpenApi3 ( ApiInfo ( \"my great super api\" , \"v1.0\" ), Jackson ) routes += \"/echo\" / Path . of ( \"name\" ) meta { summary = \"echo\" queries += ageQuery } bindContract GET to :: echo } ) ServerFilters . Cors ( CorsPolicy . UnsafeGlobalPermissive ). then ( handler ). asServer ( Jetty ( 8000 )) . start () } // Ping! curl -v \"http://localhost:8000/context/ping?apiKey=42\" // Adding 2 numbers: curl -v \"http://localhost:8000/context/add/123/564?apiKey=42\" // Echo (fail): curl -v \"http://localhost:8000/context/echo/myName?age=notANumber&apiKey=42\" // API Key enforcement: curl -v \"http://localhost:8000/context/add/123/564?apiKey=444\" // Static content: curl -v \"http://localhost:8000/static/someStaticFile.txt\" // OpenApi/Swagger documentation: curl -v \"http://localhost:8000/context/docs/openapi.json\" // Echo endpoint (at root): curl -v \"http://localhost:8000/echo/hello?age=123\"","title":"Code"},{"location":"guide/howto/leverage_graphql/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-graphql\" ) } Code \u00b6 package guide.howto.leverage_graphql import com.expediagroup.graphql.generator.SchemaGeneratorConfig import com.expediagroup.graphql.generator.TopLevelObject import com.expediagroup.graphql.generator.toSchema import graphql.ExecutionInput.Builder import graphql.GraphQL.newGraphQL import org.dataloader.DataLoaderFactory.newDataLoader import org.dataloader.DataLoaderRegistry import org.http4k.client.JavaHttpClient import org.http4k.client.asGraphQLHandler import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.RequestContexts import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters.InitialiseRequestContext import org.http4k.graphql.GraphQLRequest import org.http4k.graphql.GraphQLResponse import org.http4k.graphql.GraphQLWithContextHandler import org.http4k.lens.RequestContextKey import org.http4k.lens.RequestContextLens import org.http4k.routing.bind import org.http4k.routing.graphQL import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import java.util.concurrent.CompletableFuture.supplyAsync object UserDb { private val userDb = mutableListOf ( User ( id = 1 , name = \"Jim\" ), User ( id = 2 , name = \"Bob\" ), User ( id = 3 , name = \"Sue\" ), User ( id = 4 , name = \"Rita\" ), User ( id = 5 , name = \"Charlie\" ) ) fun search ( ids : List < Int > ) = userDb . filter { ids . contains ( it . id ) } fun delete ( ids : List < Int > ) = userDb . removeIf { ids . contains ( it . id ) } } data class User ( val id : Int , val name : String ) class UserQueries { fun search ( params : Params ) = UserDb . search ( params . ids ) } class UserMutations { fun delete ( params : Params ) = UserDb . delete ( params . ids ) } data class Params ( val ids : List < Int > ) class UserDbHandler : GraphQLWithContextHandler < String > { private val graphQL = newGraphQL ( toSchema ( SchemaGeneratorConfig ( supportedPackages = listOf ( \"guide.howto.leverage_graphql\" )), listOf ( TopLevelObject ( UserQueries ())), listOf ( TopLevelObject ( UserMutations ())) ) ). build () private val dataLoaderRegistry = DataLoaderRegistry (). apply { register ( \"USER_LOADER\" , newDataLoader { ids : List < Int > -> supplyAsync { UserQueries (). search ( Params ( ids )) } }) } override fun invoke ( payload : GraphQLRequest , user : String ) = GraphQLResponse . from ( graphQL . execute ( Builder () . query ( payload . query ) . variables ( payload . variables . orEmpty ()) . dataLoaderRegistry ( dataLoaderRegistry ) . graphQLContext ( mapOf ( \"user\" to user )) ) ) } fun App (): HttpHandler { val contexts = RequestContexts () val user = RequestContextKey . required < String > ( contexts ) return InitialiseRequestContext ( contexts ) . then ( AddUserToContext ( user )) . then ( routes ( \"/graphql\" bind graphQL ( UserDbHandler (), user ))) } private fun AddUserToContext ( user : RequestContextLens < String > ) = Filter { next -> { next ( it . with ( user of it . method . toString ())) } } fun main () { App (). asServer ( SunHttp ( 6000 )). start () val graphQLClient = JavaHttpClient (). asGraphQLHandler ( Uri . of ( \"http://localhost:6000/graphql\" )) fun runAndDisplay ( query : String ) { println ( graphQLClient ( GraphQLRequest ( query )). data ) } runAndDisplay ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) runAndDisplay ( \"\"\" mutation { delete(params: { ids: [1]}) } \"\"\" ) runAndDisplay ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) }","title":"Leverage GraphQL"},{"location":"guide/howto/leverage_graphql/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-graphql\" ) }","title":"Gradle setup"},{"location":"guide/howto/leverage_graphql/#code","text":"package guide.howto.leverage_graphql import com.expediagroup.graphql.generator.SchemaGeneratorConfig import com.expediagroup.graphql.generator.TopLevelObject import com.expediagroup.graphql.generator.toSchema import graphql.ExecutionInput.Builder import graphql.GraphQL.newGraphQL import org.dataloader.DataLoaderFactory.newDataLoader import org.dataloader.DataLoaderRegistry import org.http4k.client.JavaHttpClient import org.http4k.client.asGraphQLHandler import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.RequestContexts import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters.InitialiseRequestContext import org.http4k.graphql.GraphQLRequest import org.http4k.graphql.GraphQLResponse import org.http4k.graphql.GraphQLWithContextHandler import org.http4k.lens.RequestContextKey import org.http4k.lens.RequestContextLens import org.http4k.routing.bind import org.http4k.routing.graphQL import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import java.util.concurrent.CompletableFuture.supplyAsync object UserDb { private val userDb = mutableListOf ( User ( id = 1 , name = \"Jim\" ), User ( id = 2 , name = \"Bob\" ), User ( id = 3 , name = \"Sue\" ), User ( id = 4 , name = \"Rita\" ), User ( id = 5 , name = \"Charlie\" ) ) fun search ( ids : List < Int > ) = userDb . filter { ids . contains ( it . id ) } fun delete ( ids : List < Int > ) = userDb . removeIf { ids . contains ( it . id ) } } data class User ( val id : Int , val name : String ) class UserQueries { fun search ( params : Params ) = UserDb . search ( params . ids ) } class UserMutations { fun delete ( params : Params ) = UserDb . delete ( params . ids ) } data class Params ( val ids : List < Int > ) class UserDbHandler : GraphQLWithContextHandler < String > { private val graphQL = newGraphQL ( toSchema ( SchemaGeneratorConfig ( supportedPackages = listOf ( \"guide.howto.leverage_graphql\" )), listOf ( TopLevelObject ( UserQueries ())), listOf ( TopLevelObject ( UserMutations ())) ) ). build () private val dataLoaderRegistry = DataLoaderRegistry (). apply { register ( \"USER_LOADER\" , newDataLoader { ids : List < Int > -> supplyAsync { UserQueries (). search ( Params ( ids )) } }) } override fun invoke ( payload : GraphQLRequest , user : String ) = GraphQLResponse . from ( graphQL . execute ( Builder () . query ( payload . query ) . variables ( payload . variables . orEmpty ()) . dataLoaderRegistry ( dataLoaderRegistry ) . graphQLContext ( mapOf ( \"user\" to user )) ) ) } fun App (): HttpHandler { val contexts = RequestContexts () val user = RequestContextKey . required < String > ( contexts ) return InitialiseRequestContext ( contexts ) . then ( AddUserToContext ( user )) . then ( routes ( \"/graphql\" bind graphQL ( UserDbHandler (), user ))) } private fun AddUserToContext ( user : RequestContextLens < String > ) = Filter { next -> { next ( it . with ( user of it . method . toString ())) } } fun main () { App (). asServer ( SunHttp ( 6000 )). start () val graphQLClient = JavaHttpClient (). asGraphQLHandler ( Uri . of ( \"http://localhost:6000/graphql\" )) fun runAndDisplay ( query : String ) { println ( graphQLClient ( GraphQLRequest ( query )). data ) } runAndDisplay ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) runAndDisplay ( \"\"\" mutation { delete(params: { ids: [1]}) } \"\"\" ) runAndDisplay ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) }","title":"Code"},{"location":"guide/howto/lookup_a_user_principal/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } When authorising requests, it is common to need to store some credentials or a user principal object to be accessible by a further Filter or the eventual HttpHandler. This can be easily achieved by combining the typesafe RequestContext functionality with one of the built-in authorisation Filters: Code \u00b6 package guide.howto.lookup_a_user_principal import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.BearerAuth import org.http4k.filter.ServerFilters.InitialiseRequestContext import org.http4k.lens.RequestContextKey fun main () { val contexts = RequestContexts () val credentials = RequestContextKey . required < Credentials > ( contexts ) val app = InitialiseRequestContext ( contexts ). then ( BearerAuth ( credentials ) { if ( it == \"42\" ) Credentials ( \"user\" , \"pass\" ) else null }). then { Response ( OK ). body ( credentials ( it ). toString ()) } println ( app ( Request ( GET , \"/\" ). header ( \"Authorization\" , \"Bearer 41\" ))) println ( app ( Request ( GET , \"/\" ). header ( \"Authorization\" , \"Bearer 42\" ))) }","title":"Lookup a User Principal"},{"location":"guide/howto/lookup_a_user_principal/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } When authorising requests, it is common to need to store some credentials or a user principal object to be accessible by a further Filter or the eventual HttpHandler. This can be easily achieved by combining the typesafe RequestContext functionality with one of the built-in authorisation Filters:","title":"Gradle setup"},{"location":"guide/howto/lookup_a_user_principal/#code","text":"package guide.howto.lookup_a_user_principal import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.RequestContexts import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.BearerAuth import org.http4k.filter.ServerFilters.InitialiseRequestContext import org.http4k.lens.RequestContextKey fun main () { val contexts = RequestContexts () val credentials = RequestContextKey . required < Credentials > ( contexts ) val app = InitialiseRequestContext ( contexts ). then ( BearerAuth ( credentials ) { if ( it == \"42\" ) Credentials ( \"user\" , \"pass\" ) else null }). then { Response ( OK ). body ( credentials ( it ). toString ()) } println ( app ( Request ( GET , \"/\" ). header ( \"Authorization\" , \"Bearer 41\" ))) println ( app ( Request ( GET , \"/\" ). header ( \"Authorization\" , \"Bearer 42\" ))) }","title":"Code"},{"location":"guide/howto/make_json_faster/","text":"The standard JSON format modules included in Http4k (like Jackson and Moshi) are good enough for most applications. However there are scenarios where you may want to further optimize them for runtime performance, or to achieve acceptable cold start times in serverless deployments. kotlinx.serialization \u00b6 kotlinx.serialization is a relatively simple and effective option. It's fully supported by Http4k, but we'll go over it first as a baseline to compare against the others. It generates the serialization adapters at compile time, which means it doesn't need reflection during runtime. kotlinx.serialization is a very fast and efficient module, but there are some caveats to consider: No support for many builtin JVM types (e.g. java.time, UUID) Custom serializers must be registered via annotation No support for values4k classes Gradle \u00b6 plugins { kotlin ( \"plugin.serialization\" ) version \"\" } dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-kotlinx-serialization\" ) } Example \u00b6 package guide.howto.make_json_faster import kotlinx.serialization.Serializable import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.KotlinxSerialization import java.util.UUID // You must annotate your class to generate the adapter @Serializable data class KotlinXCat ( val id : String , // UUID not supported val name : String ) // use the builtin http4k module private val json = KotlinxSerialization fun main () { val cat = KotlinXCat ( UUID . randomUUID (). toString (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < KotlinXCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < KotlinXCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) } Moshi Metadata Reflect \u00b6 moshi-metadata-reflect is a plugin for Moshi that replaces the heavyweight kotlin-reflect with the lighter kotlinx-metadata-jvm . While this module still uses reflection, eliminating kotlin-reflect from your classpath will reduce your binary size by several MB and make a respectable improvement in cold start time. Moshi Metadata Reflect is very simple, and doesn't require any gradle plugins or finicky code generation. However, it will bring the smallest performance benefit out of these options. Gradle \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-moshi\" ) { exclude ( \"org.jetbrains.kotlin\" , \"kotlin-reflect\" ) // Exclude kotlin-reflect } implementation ( \"dev.zacsweers.moshix:moshi-metadata-reflect:\" ) } Example \u00b6 package guide.howto.make_json_faster import com.squareup.moshi.Moshi import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.ConfigurableMoshi import org.http4k.format.EventAdapter import org.http4k.format.ListAdapter import org.http4k.format.MapAdapter import org.http4k.format.ThrowableAdapter import org.http4k.format.asConfigurable import org.http4k.format.withStandardMappings import se.ansman.kotshi.JsonSerializable import java.util.UUID private val json = ConfigurableMoshi ( Moshi . Builder () . addLast ( EventAdapter ) . addLast ( ThrowableAdapter ) . addLast ( ListAdapter ) . addLast ( MapAdapter ) . asConfigurable ( MetadataKotlinJsonAdapterFactory ()) // <-- moshi-metadata-reflect . withStandardMappings () . done () ) data class MoshiCat ( val id : UUID , val name : String ) fun main () { val cat = MoshiCat ( UUID . randomUUID (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < MoshiCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < MoshiCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) } Kotshi \u00b6 Kotshi is a plugin for Moshi. It's very similar to moshi-kotlin-codegen in that they both generate Moshi adapters at compile time. However, since moshi-kotlin-codegen still requires kotlin-reflect at runtime, kotshi can bring much greater cold start performance gains. Kotshi is a very fast and efficient module, but is the most difficult to set up. It requires a gradle plugin, several dependencies, and depends on code that only exists after compile. Gradle \u00b6 plugins { // The KSP plugin is required to generate the adapters at compile time id ( \"com.google.devtools.ksp\" ) version \"\" } dependencies { implementation ( \"org.http4k:http4k-format-moshi\" ) { exclude ( \"org.jetbrains.kotlin\" , \"kotlin-reflect\" ) // Exclude kotlin-reflect } // Get the Kotshi runtime library and configure KSP to generate the adapters implementation ( \"se.ansman.kotshi:api:\" ) ksp ( \"se.ansman.kotshi:compiler:\" ) } Example \u00b6 package guide.howto.make_json_faster import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.ConfigurableMoshi import org.http4k.format.EventAdapter import org.http4k.format.ListAdapter import org.http4k.format.MapAdapter import org.http4k.format.ThrowableAdapter import org.http4k.format.asConfigurable import org.http4k.format.withStandardMappings import se.ansman.kotshi.JsonSerializable import se.ansman.kotshi.KotshiJsonAdapterFactory import java.util.UUID // Annotation is required to generate the adapter @JsonSerializable data class KotshiCat ( val id : UUID , val name : String ) // Build the kotshi adapter @KotshiJsonAdapterFactory private object ExampleJsonAdapterFactory : JsonAdapter . Factory by KotshiExampleJsonAdapterFactory // this class will be generated during compile private val json = ConfigurableMoshi ( Moshi . Builder () . add ( ExampleJsonAdapterFactory ) // inject kotshi here . addLast ( EventAdapter ) . addLast ( ThrowableAdapter ) . addLast ( ListAdapter ) . addLast ( MapAdapter ) . asConfigurable () . withStandardMappings () . done () ) fun main () { val cat = KotshiCat ( UUID . randomUUID (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < KotshiCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < KotshiCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) }","title":"Make JSON Faster"},{"location":"guide/howto/make_json_faster/#kotlinxserialization","text":"kotlinx.serialization is a relatively simple and effective option. It's fully supported by Http4k, but we'll go over it first as a baseline to compare against the others. It generates the serialization adapters at compile time, which means it doesn't need reflection during runtime. kotlinx.serialization is a very fast and efficient module, but there are some caveats to consider: No support for many builtin JVM types (e.g. java.time, UUID) Custom serializers must be registered via annotation No support for values4k classes","title":"kotlinx.serialization"},{"location":"guide/howto/make_json_faster/#gradle","text":"plugins { kotlin ( \"plugin.serialization\" ) version \"\" } dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-kotlinx-serialization\" ) }","title":"Gradle"},{"location":"guide/howto/make_json_faster/#example","text":"package guide.howto.make_json_faster import kotlinx.serialization.Serializable import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.KotlinxSerialization import java.util.UUID // You must annotate your class to generate the adapter @Serializable data class KotlinXCat ( val id : String , // UUID not supported val name : String ) // use the builtin http4k module private val json = KotlinxSerialization fun main () { val cat = KotlinXCat ( UUID . randomUUID (). toString (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < KotlinXCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < KotlinXCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) }","title":"Example"},{"location":"guide/howto/make_json_faster/#moshi_metadata_reflect","text":"moshi-metadata-reflect is a plugin for Moshi that replaces the heavyweight kotlin-reflect with the lighter kotlinx-metadata-jvm . While this module still uses reflection, eliminating kotlin-reflect from your classpath will reduce your binary size by several MB and make a respectable improvement in cold start time. Moshi Metadata Reflect is very simple, and doesn't require any gradle plugins or finicky code generation. However, it will bring the smallest performance benefit out of these options.","title":"Moshi Metadata Reflect"},{"location":"guide/howto/make_json_faster/#gradle_1","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-moshi\" ) { exclude ( \"org.jetbrains.kotlin\" , \"kotlin-reflect\" ) // Exclude kotlin-reflect } implementation ( \"dev.zacsweers.moshix:moshi-metadata-reflect:\" ) }","title":"Gradle"},{"location":"guide/howto/make_json_faster/#example_1","text":"package guide.howto.make_json_faster import com.squareup.moshi.Moshi import dev.zacsweers.moshix.reflect.MetadataKotlinJsonAdapterFactory import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.ConfigurableMoshi import org.http4k.format.EventAdapter import org.http4k.format.ListAdapter import org.http4k.format.MapAdapter import org.http4k.format.ThrowableAdapter import org.http4k.format.asConfigurable import org.http4k.format.withStandardMappings import se.ansman.kotshi.JsonSerializable import java.util.UUID private val json = ConfigurableMoshi ( Moshi . Builder () . addLast ( EventAdapter ) . addLast ( ThrowableAdapter ) . addLast ( ListAdapter ) . addLast ( MapAdapter ) . asConfigurable ( MetadataKotlinJsonAdapterFactory ()) // <-- moshi-metadata-reflect . withStandardMappings () . done () ) data class MoshiCat ( val id : UUID , val name : String ) fun main () { val cat = MoshiCat ( UUID . randomUUID (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < MoshiCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < MoshiCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) }","title":"Example"},{"location":"guide/howto/make_json_faster/#kotshi","text":"Kotshi is a plugin for Moshi. It's very similar to moshi-kotlin-codegen in that they both generate Moshi adapters at compile time. However, since moshi-kotlin-codegen still requires kotlin-reflect at runtime, kotshi can bring much greater cold start performance gains. Kotshi is a very fast and efficient module, but is the most difficult to set up. It requires a gradle plugin, several dependencies, and depends on code that only exists after compile.","title":"Kotshi"},{"location":"guide/howto/make_json_faster/#gradle_2","text":"plugins { // The KSP plugin is required to generate the adapters at compile time id ( \"com.google.devtools.ksp\" ) version \"\" } dependencies { implementation ( \"org.http4k:http4k-format-moshi\" ) { exclude ( \"org.jetbrains.kotlin\" , \"kotlin-reflect\" ) // Exclude kotlin-reflect } // Get the Kotshi runtime library and configure KSP to generate the adapters implementation ( \"se.ansman.kotshi:api:\" ) ksp ( \"se.ansman.kotshi:compiler:\" ) }","title":"Gradle"},{"location":"guide/howto/make_json_faster/#example_2","text":"package guide.howto.make_json_faster import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.ConfigurableMoshi import org.http4k.format.EventAdapter import org.http4k.format.ListAdapter import org.http4k.format.MapAdapter import org.http4k.format.ThrowableAdapter import org.http4k.format.asConfigurable import org.http4k.format.withStandardMappings import se.ansman.kotshi.JsonSerializable import se.ansman.kotshi.KotshiJsonAdapterFactory import java.util.UUID // Annotation is required to generate the adapter @JsonSerializable data class KotshiCat ( val id : UUID , val name : String ) // Build the kotshi adapter @KotshiJsonAdapterFactory private object ExampleJsonAdapterFactory : JsonAdapter . Factory by KotshiExampleJsonAdapterFactory // this class will be generated during compile private val json = ConfigurableMoshi ( Moshi . Builder () . add ( ExampleJsonAdapterFactory ) // inject kotshi here . addLast ( EventAdapter ) . addLast ( ThrowableAdapter ) . addLast ( ListAdapter ) . addLast ( MapAdapter ) . asConfigurable () . withStandardMappings () . done () ) fun main () { val cat = KotshiCat ( UUID . randomUUID (), \"Kratos\" ) // serialize val string = json . asFormatString ( cat ) . also ( :: println ) // deserialize json . asA < KotshiCat > ( string ) . also ( :: println ) // make a lens val lens = json . autoBody < KotshiCat > (). toLens () Request ( Method . GET , \"foo\" ). with ( lens of cat ) }","title":"Example"},{"location":"guide/howto/make_parallel_calls/","text":"There are cases where an application needs to make multiple HTTP calls to other services as part of handling a particular request. As a general rule-of-thumb, we recommend people to avoid premature optimisation , however sometimes the quantity of calls or performance of other services demand those to be executed in parallel. In this example, we show how to use the extension function on [ExecutionService] to manage multiple HTTP calls in parallel, and synchronise their results to produce a single response. This recipe also covers how to make distributed tracing work when tracing information is consumed by multiple threads. package guide.howto.make_parallel_calls import org.http4k.client.OkHttp import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.NoOp import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.DebuggingFilters import org.http4k.filter.ServerFilters import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.util.withRequestTracing import java.util.concurrent.Callable import java.util.concurrent.ExecutorService import java.util.concurrent.LinkedBlockingDeque import java.util.concurrent.ThreadPoolExecutor import java.util.concurrent.TimeUnit.SECONDS import kotlin.system.measureTimeMillis private fun serverWithTracing ( handler : HttpHandler ) = Filter . NoOp . then ( ServerFilters . RequestTracing ()) . then ( DebuggingFilters . PrintRequestAndResponse ()) . then ( handler ) private fun clientWithTracing () = Filter . NoOp . then ( ClientFilters . RequestTracing ()) . then ( DebuggingFilters . PrintRequestAndResponse ()) . then ( OkHttp ()) private fun serverWithParallelCallsAndTracing ( dependencyServerPort : Int , executor : ExecutorService ): HttpHandler { val localClient = clientWithTracing () return serverWithTracing { _ : Request -> val overallStatus = ( 1. . 5 ). map { executor . submit ( Callable { localClient ( Request ( GET , \"http://localhost: $ dependencyServerPort /sub-request- ${ it } \" ) ) }) }. foldRight ( OK ) { future , overallStatus -> val nextResponse = future . get ( 5 , SECONDS ) if ( nextResponse . status . successful ) nextResponse . status else overallStatus } Response ( overallStatus ) } } fun main () { val originalExecutor = ThreadPoolExecutor ( 5 , 5 , 10 , SECONDS , LinkedBlockingDeque ()) // convert the execution service to one that propagates the trace information // optionally pass a custom ZipkinStorage val executor = originalExecutor . withRequestTracing () val subServer = serverWithTracing { _ : Request -> Thread . sleep ( 500 ); Response ( OK ) } val runningSubServer = subServer . asServer ( Undertow ( 0 )). start () val server = serverWithParallelCallsAndTracing ( runningSubServer . port (), executor ) val runningServer = server . asServer ( Undertow ( 0 )). start () val elapsedTime = measureTimeMillis { val client = clientWithTracing () client ( Request ( GET , \"http://localhost: ${ runningServer . port () } /main-request\" )) } println ( \"Elapsed time in millis = $ elapsedTime \" ) executor . close () runningSubServer . stop () runningServer . stop () }","title":"Make parallel HTTP calls"},{"location":"guide/howto/monitor_http4k/","text":"Measuring performance of application estate is crucial in today's microservice world - it is crucial that dev-ops enabled teams can monitor, react and scale dynamically to changes in the runtime environment. However, because of the plethora of monitoring tools on the market, and because http4k is a toolkit and not a complete \"batteries included\" framework, it provides a number of integration points to enable monitoring systems to be plugged in as required. Additionally, it is envisaged that users will probably want to provide their own implementations of the http4k ServerConfig classes ( Jetty , Undertow etc..) so that tweaking and tuning to their exact requirements is accessible, instead of http4k attempting to provide some generic configuration API to achieve it. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-metrics-micrometer\" ) } Metrics (Micrometer) \u00b6 http4k provides module support for monitoring application endpoints using the micrometer metrics abstraction library, which currently enables support for libraries such as Graphite, StatsD, Prometheus and Netflix Atlas. This also provides drop-in classes to record stats such as JVM performance, GC and thread usage. package guide.howto.monitor_http4k import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // this is a micrometer registry used mostly for testing - substitute the correct implementation. val registry = SimpleMeterRegistry () val server = routes ( \"/metrics/{name}\" bind GET to { Response ( OK ) }) // apply filters to a server... val app = ServerFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( server ) // ... or to a client val client = ClientFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ApacheClient ()) // make some calls ( 0. . 10 ). forEach { app ( Request ( GET , \"/metrics/ $ it \" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results registry . forEachMeter { println ( \" ${ it . id } ${ it . measure (). joinToString ( \" , \" ) } \" ) } } Metrics (other APIs) \u00b6 Alternatively, it's very easy to use a standard Filter to report on stats: package guide.howto.monitor_http4k import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.HttpTransactionLabeler import org.http4k.filter.ResponseFilters import org.http4k.routing.bind import org.http4k.routing.routes import java.time.Duration fun main () { val app = routes ( \"foo/{name}\" bind { _ : Request -> Response ( OK ) }) fun metricConsumer ( name : String , time : Duration ) = println ( \" $ name ${ time . toMillis () } ms\" ) // this is a general use filter for reporting on http transactions val standardFilter = ResponseFilters . ReportHttpTransaction { tx : HttpTransaction -> metricConsumer ( \"txLabels are: ${ tx . labels } \" , tx . duration ) metricConsumer ( \"uri is: ${ tx . request . uri } \" , tx . duration ) } val addCustomLabels : HttpTransactionLabeler = { tx : HttpTransaction -> tx . label ( \"status\" , tx . response . status . code . toString ()) } val withCustomLabels = ResponseFilters . ReportHttpTransaction ( transactionLabeler = addCustomLabels ) { tx : HttpTransaction -> // send metrics to some custom system here... println ( \"custom txLabels are: ${ tx . labels } ${ tx . duration } \" ) } // this filter provides an anonymous identifier of the route val identifiedRouteFilter = ResponseFilters . ReportRouteLatency { requestGroup : String , duration : Duration -> metricConsumer ( \"requestGroup is: $ requestGroup \" , duration ) } val monitoredApp : HttpHandler = standardFilter . then ( withCustomLabels ) . then ( identifiedRouteFilter ) . then ( app ) monitoredApp ( Request ( GET , \"/foo/bob\" )) // prints... // requestGroup is: GET.foo_{name}.2xx.200 7ms // custom txLabels are: {routingGroup=foo/{name}, status=200} PT0.05S // txLabels are: {routingGroup=foo/{name}} 51ms // uri is: /foo/bob 51ms } Logging \u00b6 This is trivial to achieve by using a Filter: package guide.howto.monitor_http4k import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResponseFilters import org.http4k.routing.bind import org.http4k.routing.routes import java.time.Clock fun main () { val app = routes ( \"/{name}\" bind { _ : Request -> Response ( OK ) }) fun logger ( message : String ) = println ( \" ${ Clock . systemUTC (). instant () } $ message \" ) val audit = ResponseFilters . ReportHttpTransaction { tx : HttpTransaction -> logger ( \"my call to ${ tx . request . uri } returned ${ tx . response . status } and took ${ tx . duration . toMillis () } \" ) } val monitoredApp : HttpHandler = audit . then ( app ) monitoredApp ( Request ( GET , \"/foo\" )) // prints... // 2017-12-04T08:38:27.499Z my call to /foo returned 200 OK and took 5 } Distributed tracing \u00b6 This allows a chain of application calls to be tied together and is generally done through the setting of HTTP headers on each call. http4k supports the OpenZipkin standard for achieving this and provides both Server-side and Client-side Filters for this purpose. This example shows a chain of two proxies and an endpoint - run it to observe the changes to the tracing headers as the request flows through the system: package guide.howto.monitor_http4k import org.http4k.client.ApacheClient import org.http4k.core.HttpHandler import org.http4k.core.HttpMessage import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.ResponseFilters import org.http4k.filter.ServerFilters import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { fun HttpMessage . logHeader ( name : String ) = \"\\n\\t\\t $ name = ${ header ( name ) } \" fun HttpMessage . traces () = logHeader ( \"x-b3-traceid\" ) + logHeader ( \"x-b3-spanid\" ) + logHeader ( \"x-b3-parentspanid\" ) fun audit ( name : String ) = ResponseFilters . ReportHttpTransaction { tx -> println ( \" $ name : ${ tx . request . uri } \\n\\trequest: ${ tx . request . traces () } \\n\\tresponse: ${ tx . response . traces () } \" ) } // a simple proxy to another app fun proxy ( name : String , port : Int ): HttpHandler { val proxyClient = ClientFilters . RequestTracing () . then ( ClientFilters . SetHostFrom ( Uri . of ( \"http://localhost: $ port \" ))) . then ( audit ( \" $ name -client\" )) . then ( ApacheClient ()) return ServerFilters . RequestTracing (). then ( audit ( \" $ name -server\" )) . then { proxyClient ( Request ( GET , it . uri )) } } // provides a simple ping fun ping (): HttpHandler = ServerFilters . RequestTracing (). then ( audit ( \"ping-server\" )) . then { Response ( OK ). body ( \"pong\" ) } val proxy1 = proxy ( \"proxy1\" , 8001 ). asServer ( SunHttp ( 8000 )). start () val proxy2 = proxy ( \"proxy2\" , 8002 ). asServer ( SunHttp ( 8001 )). start () val server3 = ping (). asServer ( SunHttp ( 8002 )). start () audit ( \"client\" ). then ( ApacheClient ())( Request ( GET , \"http://localhost:8000/ping\" )) proxy1 . stop () proxy2 . stop () server3 . stop () } Debugging \u00b6 Easily wrap an HttpHandler in a debugging filter to check out what is going on under the covers: package guide.howto.monitor_http4k import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DebuggingFilters fun main () { val app = { _ : Request -> Response ( OK ). body ( \"hello there you look nice today\" ) } val debuggedApp = DebuggingFilters . PrintRequestAndResponse (). then ( app ) debuggedApp ( Request ( GET , \"/foobar\" ). header ( \"Accepted\" , \"my-great-content/type\" )) }","title":"Monitor http4k"},{"location":"guide/howto/monitor_http4k/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-metrics-micrometer\" ) }","title":"Gradle setup"},{"location":"guide/howto/monitor_http4k/#metrics_micrometer","text":"http4k provides module support for monitoring application endpoints using the micrometer metrics abstraction library, which currently enables support for libraries such as Graphite, StatsD, Prometheus and Netflix Atlas. This also provides drop-in classes to record stats such as JVM performance, GC and thread usage. package guide.howto.monitor_http4k import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // this is a micrometer registry used mostly for testing - substitute the correct implementation. val registry = SimpleMeterRegistry () val server = routes ( \"/metrics/{name}\" bind GET to { Response ( OK ) }) // apply filters to a server... val app = ServerFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( server ) // ... or to a client val client = ClientFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ApacheClient ()) // make some calls ( 0. . 10 ). forEach { app ( Request ( GET , \"/metrics/ $ it \" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results registry . forEachMeter { println ( \" ${ it . id } ${ it . measure (). joinToString ( \" , \" ) } \" ) } }","title":"Metrics (Micrometer)"},{"location":"guide/howto/monitor_http4k/#metrics_other_apis","text":"Alternatively, it's very easy to use a standard Filter to report on stats: package guide.howto.monitor_http4k import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.HttpTransactionLabeler import org.http4k.filter.ResponseFilters import org.http4k.routing.bind import org.http4k.routing.routes import java.time.Duration fun main () { val app = routes ( \"foo/{name}\" bind { _ : Request -> Response ( OK ) }) fun metricConsumer ( name : String , time : Duration ) = println ( \" $ name ${ time . toMillis () } ms\" ) // this is a general use filter for reporting on http transactions val standardFilter = ResponseFilters . ReportHttpTransaction { tx : HttpTransaction -> metricConsumer ( \"txLabels are: ${ tx . labels } \" , tx . duration ) metricConsumer ( \"uri is: ${ tx . request . uri } \" , tx . duration ) } val addCustomLabels : HttpTransactionLabeler = { tx : HttpTransaction -> tx . label ( \"status\" , tx . response . status . code . toString ()) } val withCustomLabels = ResponseFilters . ReportHttpTransaction ( transactionLabeler = addCustomLabels ) { tx : HttpTransaction -> // send metrics to some custom system here... println ( \"custom txLabels are: ${ tx . labels } ${ tx . duration } \" ) } // this filter provides an anonymous identifier of the route val identifiedRouteFilter = ResponseFilters . ReportRouteLatency { requestGroup : String , duration : Duration -> metricConsumer ( \"requestGroup is: $ requestGroup \" , duration ) } val monitoredApp : HttpHandler = standardFilter . then ( withCustomLabels ) . then ( identifiedRouteFilter ) . then ( app ) monitoredApp ( Request ( GET , \"/foo/bob\" )) // prints... // requestGroup is: GET.foo_{name}.2xx.200 7ms // custom txLabels are: {routingGroup=foo/{name}, status=200} PT0.05S // txLabels are: {routingGroup=foo/{name}} 51ms // uri is: /foo/bob 51ms }","title":"Metrics (other APIs)"},{"location":"guide/howto/monitor_http4k/#logging","text":"This is trivial to achieve by using a Filter: package guide.howto.monitor_http4k import org.http4k.core.HttpHandler import org.http4k.core.HttpTransaction import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResponseFilters import org.http4k.routing.bind import org.http4k.routing.routes import java.time.Clock fun main () { val app = routes ( \"/{name}\" bind { _ : Request -> Response ( OK ) }) fun logger ( message : String ) = println ( \" ${ Clock . systemUTC (). instant () } $ message \" ) val audit = ResponseFilters . ReportHttpTransaction { tx : HttpTransaction -> logger ( \"my call to ${ tx . request . uri } returned ${ tx . response . status } and took ${ tx . duration . toMillis () } \" ) } val monitoredApp : HttpHandler = audit . then ( app ) monitoredApp ( Request ( GET , \"/foo\" )) // prints... // 2017-12-04T08:38:27.499Z my call to /foo returned 200 OK and took 5 }","title":"Logging"},{"location":"guide/howto/monitor_http4k/#distributed_tracing","text":"This allows a chain of application calls to be tied together and is generally done through the setting of HTTP headers on each call. http4k supports the OpenZipkin standard for achieving this and provides both Server-side and Client-side Filters for this purpose. This example shows a chain of two proxies and an endpoint - run it to observe the changes to the tracing headers as the request flows through the system: package guide.howto.monitor_http4k import org.http4k.client.ApacheClient import org.http4k.core.HttpHandler import org.http4k.core.HttpMessage import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.ResponseFilters import org.http4k.filter.ServerFilters import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { fun HttpMessage . logHeader ( name : String ) = \"\\n\\t\\t $ name = ${ header ( name ) } \" fun HttpMessage . traces () = logHeader ( \"x-b3-traceid\" ) + logHeader ( \"x-b3-spanid\" ) + logHeader ( \"x-b3-parentspanid\" ) fun audit ( name : String ) = ResponseFilters . ReportHttpTransaction { tx -> println ( \" $ name : ${ tx . request . uri } \\n\\trequest: ${ tx . request . traces () } \\n\\tresponse: ${ tx . response . traces () } \" ) } // a simple proxy to another app fun proxy ( name : String , port : Int ): HttpHandler { val proxyClient = ClientFilters . RequestTracing () . then ( ClientFilters . SetHostFrom ( Uri . of ( \"http://localhost: $ port \" ))) . then ( audit ( \" $ name -client\" )) . then ( ApacheClient ()) return ServerFilters . RequestTracing (). then ( audit ( \" $ name -server\" )) . then { proxyClient ( Request ( GET , it . uri )) } } // provides a simple ping fun ping (): HttpHandler = ServerFilters . RequestTracing (). then ( audit ( \"ping-server\" )) . then { Response ( OK ). body ( \"pong\" ) } val proxy1 = proxy ( \"proxy1\" , 8001 ). asServer ( SunHttp ( 8000 )). start () val proxy2 = proxy ( \"proxy2\" , 8002 ). asServer ( SunHttp ( 8001 )). start () val server3 = ping (). asServer ( SunHttp ( 8002 )). start () audit ( \"client\" ). then ( ApacheClient ())( Request ( GET , \"http://localhost:8000/ping\" )) proxy1 . stop () proxy2 . stop () server3 . stop () }","title":"Distributed tracing"},{"location":"guide/howto/monitor_http4k/#debugging","text":"Easily wrap an HttpHandler in a debugging filter to check out what is going on under the covers: package guide.howto.monitor_http4k import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DebuggingFilters fun main () { val app = { _ : Request -> Response ( OK ). body ( \"hello there you look nice today\" ) } val debuggedApp = DebuggingFilters . PrintRequestAndResponse (). then ( app ) debuggedApp ( Request ( GET , \"/foobar\" ). header ( \"Accepted\" , \"my-great-content/type\" )) }","title":"Debugging"},{"location":"guide/howto/nestable_routes/","text":"This is a fairly comprehensive example of the core-routing logic available: Individual HTTP endpoints are represented as HttpHandlers . Binding an HttpHandler to a path and HTTP verb yields a Route . Routes can be combined together into a RoutingHttpHandler , which is both an HttpHandler and a Router . A Router is a selective request handler, which attempts to match a request. If it cannot, processing falls through to the next Router in the list. Routers can be combined together to form another HttpHandler Usage of supplied core library Filters Serving of static content using a Classpath resource loader Support for Single Page Applications using a singlePageApp() block - resources loaded from here are loaded from the underlying ResourceLoader or fallback to / (and passed to the SPA code) Dynamic Paths / Path Variables \u00b6 As you would expect, http4k allows routes to include dynamic or variable elements in the matching path, and allows you to reference the variable in the Handler. For example: \"/book/{title}\" bind GET to { req -> Response . invoke ( Status . OK ). body ( GetBookDetails ( req . path ( \"title\" )) } \"/author/{name}/latest\" bind GET to { req -> Response . invoke ( Status . OK ). body ( GetAllBooks ( author = req . path ( \"name\" )). last ()) } By default, the variable(s) will match anything. However you can append the variable name with a RegEx expression to limit the matches. // will match /book/978-3-16-148410-0 (i.e. only digits and dashes) // /book/A%20Confederacy%20Of%20Dunces would return a 404 (Not Found) \"/book/{isbn:[\\\\d-]+}\" // will NOT match /sales/south or /sales/usa \"/sales/{region:(?:northeast|southeast|west|international)}\" There are no pre-defined types such as int or path for matching but these are easy to replicate with RegEx's: - string (excluding slashes) : [^\\\\/]+ (note that Kotlin requires backslashes to be escaped, so \\w in RegEx is expressed as \\\\w in Kotlin) - int : \\\\d+ - float : [+-]?([0-9]*[.])?[0-9]+ (this will match basic floats. Does not match exponents, or scientific notation) - path : .* Note that paths, not strings, will match by default. \"/news/{date}\" will match www.example.com/news/2018/05/26 , making request.path(\"date\") equal to 2018/05/26 . This may be exactly what you want, or it may produce unexpected results, depending on how your URLs are structured. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Code \u00b6 package guide.howto.nestable_routes import org.http4k.core.Method.DELETE import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.routing.ResourceLoader.Companion.Classpath import org.http4k.routing.and import org.http4k.routing.bind import org.http4k.routing.header import org.http4k.routing.headers import org.http4k.routing.path import org.http4k.routing.queries import org.http4k.routing.routes import org.http4k.routing.singlePageApp import org.http4k.routing.static fun main () { val routesWithFilter = PrintRequestAndResponse (). then ( routes ( \"/get/{name}\" bind GET to { req : Request -> Response ( OK ). body ( req . path ( \"name\" ) !! ) }, \"/post/{name}\" bind POST to { Response ( OK ) } ) ) println ( routesWithFilter ( Request ( GET , \"/get/value\" ))) val staticWithFilter = PrintRequestAndResponse () . then ( static ( Classpath ( \"guide/howto/nestable_routes\" ))) val app = routes ( \"/bob\" bind routesWithFilter , \"/static\" bind staticWithFilter , \"/pattern/{rest:.*}\" bind { req : Request -> Response ( OK ). body ( req . path ( \"rest\" ). orEmpty ()) }, \"/rita\" bind routes ( \"/delete/{name}\" bind DELETE to { Response ( OK ) }, \"/post/{name}\" bind POST to { Response ( OK ) } ), \"/matching\" bind GET to routes ( header ( \"requiredheader\" , \"somevalue\" ) . and ( queries ( \"requiredquery\" )) bind { Response ( OK ). body ( \"matched 2 parameters\" ) }, headers ( \"requiredheader\" ) bind { Response ( OK ). body ( \"matched 1 parameters\" ) } ), singlePageApp ( Classpath ( \"guide/howto/nestable_routes\" )) ) println ( app ( Request ( GET , \"/bob/get/value\" ))) println ( app ( Request ( GET , \"/static/someStaticFile.txt\" ))) println ( app ( Request ( GET , \"/pattern/some/entire/pattern/we/want/to/capture\" ))) println ( app ( Request ( GET , \"/matching\" ) . header ( \"requiredheader\" , \"somevalue\" ) . query ( \"requiredquery\" , \"somevalue\" ) ) ) println ( app ( Request ( GET , \"/matching\" ). header ( \"requiredheader\" , \"somevalue\" ))) println ( app ( Request ( GET , \"/someSpaResource\" ))) } For the typesafe contract-style routing, refer to this recipe instead,","title":"Basic: Nestable routes"},{"location":"guide/howto/nestable_routes/#dynamic_paths_path_variables","text":"As you would expect, http4k allows routes to include dynamic or variable elements in the matching path, and allows you to reference the variable in the Handler. For example: \"/book/{title}\" bind GET to { req -> Response . invoke ( Status . OK ). body ( GetBookDetails ( req . path ( \"title\" )) } \"/author/{name}/latest\" bind GET to { req -> Response . invoke ( Status . OK ). body ( GetAllBooks ( author = req . path ( \"name\" )). last ()) } By default, the variable(s) will match anything. However you can append the variable name with a RegEx expression to limit the matches. // will match /book/978-3-16-148410-0 (i.e. only digits and dashes) // /book/A%20Confederacy%20Of%20Dunces would return a 404 (Not Found) \"/book/{isbn:[\\\\d-]+}\" // will NOT match /sales/south or /sales/usa \"/sales/{region:(?:northeast|southeast|west|international)}\" There are no pre-defined types such as int or path for matching but these are easy to replicate with RegEx's: - string (excluding slashes) : [^\\\\/]+ (note that Kotlin requires backslashes to be escaped, so \\w in RegEx is expressed as \\\\w in Kotlin) - int : \\\\d+ - float : [+-]?([0-9]*[.])?[0-9]+ (this will match basic floats. Does not match exponents, or scientific notation) - path : .* Note that paths, not strings, will match by default. \"/news/{date}\" will match www.example.com/news/2018/05/26 , making request.path(\"date\") equal to 2018/05/26 . This may be exactly what you want, or it may produce unexpected results, depending on how your URLs are structured.","title":"Dynamic Paths / Path Variables"},{"location":"guide/howto/nestable_routes/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/nestable_routes/#code","text":"package guide.howto.nestable_routes import org.http4k.core.Method.DELETE import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.routing.ResourceLoader.Companion.Classpath import org.http4k.routing.and import org.http4k.routing.bind import org.http4k.routing.header import org.http4k.routing.headers import org.http4k.routing.path import org.http4k.routing.queries import org.http4k.routing.routes import org.http4k.routing.singlePageApp import org.http4k.routing.static fun main () { val routesWithFilter = PrintRequestAndResponse (). then ( routes ( \"/get/{name}\" bind GET to { req : Request -> Response ( OK ). body ( req . path ( \"name\" ) !! ) }, \"/post/{name}\" bind POST to { Response ( OK ) } ) ) println ( routesWithFilter ( Request ( GET , \"/get/value\" ))) val staticWithFilter = PrintRequestAndResponse () . then ( static ( Classpath ( \"guide/howto/nestable_routes\" ))) val app = routes ( \"/bob\" bind routesWithFilter , \"/static\" bind staticWithFilter , \"/pattern/{rest:.*}\" bind { req : Request -> Response ( OK ). body ( req . path ( \"rest\" ). orEmpty ()) }, \"/rita\" bind routes ( \"/delete/{name}\" bind DELETE to { Response ( OK ) }, \"/post/{name}\" bind POST to { Response ( OK ) } ), \"/matching\" bind GET to routes ( header ( \"requiredheader\" , \"somevalue\" ) . and ( queries ( \"requiredquery\" )) bind { Response ( OK ). body ( \"matched 2 parameters\" ) }, headers ( \"requiredheader\" ) bind { Response ( OK ). body ( \"matched 1 parameters\" ) } ), singlePageApp ( Classpath ( \"guide/howto/nestable_routes\" )) ) println ( app ( Request ( GET , \"/bob/get/value\" ))) println ( app ( Request ( GET , \"/static/someStaticFile.txt\" ))) println ( app ( Request ( GET , \"/pattern/some/entire/pattern/we/want/to/capture\" ))) println ( app ( Request ( GET , \"/matching\" ) . header ( \"requiredheader\" , \"somevalue\" ) . query ( \"requiredquery\" , \"somevalue\" ) ) ) println ( app ( Request ( GET , \"/matching\" ). header ( \"requiredheader\" , \"somevalue\" ))) println ( app ( Request ( GET , \"/someSpaResource\" ))) } For the typesafe contract-style routing, refer to this recipe instead,","title":"Code"},{"location":"guide/howto/record_and_replay_http_traffic/","text":"A set of classes to provide simple recording/replaying of HTTP traffic. This is perfect for testing purposes, or in short lived, low traffic environments where no proper caches are available. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Caching HTTP Traffic \u00b6 Using Filters it's possible to record traffic and then return recorded content instead of making repeated calls. Note that the provided storage implementations DO NOT have any facility for Cache Control or eviction, or respect any response headers around caching. Requests are indexed in a way optimised for retrieval. Code \u00b6 package guide.howto.record_and_replay_http_traffic import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.TrafficFilters import org.http4k.traffic.ReadWriteCache fun main () { // set up storage to cache a set of HTTP traffic. // Disk and Memory implementations are provided. val storage = ReadWriteCache . Disk () // wrap any HTTP Handler in a Recording Filter and play traffic through it val withCachedContent = TrafficFilters . ServeCachedFrom ( storage ) . then ( TrafficFilters . RecordTo ( storage )) . then { Response ( OK ). body ( \"hello world\" ) } val aRequest = Request ( GET , \"http://localhost:8000/\" ) println ( withCachedContent ( aRequest )) // repeated requests are intercepted by the cache and // the responses provided without hitting the original handler println ( withCachedContent ( Request ( GET , \"http://localhost:8000/\" ))) } Recording Streams of HTTP Traffic \u00b6 Using Filters it's possible to record a stream traffic and then replay recorded content instead. Requests are indexed in a way optimised for iteration. Code \u00b6 package guide.howto.record_and_replay_http_traffic import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.TrafficFilters import org.http4k.traffic.ReadWriteStream import org.http4k.traffic.Responder fun main () { // set up storage to stash a stream of HTTP traffic. // Disk and Memory implementations are provided. val storage = ReadWriteStream . Memory () // wrap any HTTP Handler in a Recording Filter and play traffic through it val recording = TrafficFilters . RecordTo ( storage ). then { Response ( OK ). body ( \"hello world\" ) } recording ( Request ( GET , \"http://localhost:8000/\" )) // now set up a responder val handler = Responder . from ( storage ) // the responder will replay the responses in order println ( handler ( Request ( GET , \"http://localhost:8000/\" ))) // we can also replay a series of requests through a real HTTP client val client = ApacheClient () storage . requests (). forEach { println ( client ( it )) } } Concepts \u00b6 The org.http4k.traffic package contains the interfaces which make up the core concepts for traffic capture and replay. These interfaces are: A Sink consumes request/response pairs for storage. A Source provides lookup of pre-stored Response based on an HTTP Request. Replay instances provide streams of HTTP messages as they were received. A ReadWriteCache combines Sink and Source to provide cache-like storage. A ReadWriteStream combines Sink and Replay to provide a stream of traffic which can be replayed. The API has been designed to be modular so API users can provide their own implementations (store in S3 etc..).","title":"Record & replay HTTP traffic"},{"location":"guide/howto/record_and_replay_http_traffic/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/record_and_replay_http_traffic/#caching_http_traffic","text":"Using Filters it's possible to record traffic and then return recorded content instead of making repeated calls. Note that the provided storage implementations DO NOT have any facility for Cache Control or eviction, or respect any response headers around caching. Requests are indexed in a way optimised for retrieval.","title":"Caching HTTP Traffic"},{"location":"guide/howto/record_and_replay_http_traffic/#code","text":"package guide.howto.record_and_replay_http_traffic import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.TrafficFilters import org.http4k.traffic.ReadWriteCache fun main () { // set up storage to cache a set of HTTP traffic. // Disk and Memory implementations are provided. val storage = ReadWriteCache . Disk () // wrap any HTTP Handler in a Recording Filter and play traffic through it val withCachedContent = TrafficFilters . ServeCachedFrom ( storage ) . then ( TrafficFilters . RecordTo ( storage )) . then { Response ( OK ). body ( \"hello world\" ) } val aRequest = Request ( GET , \"http://localhost:8000/\" ) println ( withCachedContent ( aRequest )) // repeated requests are intercepted by the cache and // the responses provided without hitting the original handler println ( withCachedContent ( Request ( GET , \"http://localhost:8000/\" ))) }","title":"Code"},{"location":"guide/howto/record_and_replay_http_traffic/#recording_streams_of_http_traffic","text":"Using Filters it's possible to record a stream traffic and then replay recorded content instead. Requests are indexed in a way optimised for iteration.","title":"Recording Streams of HTTP Traffic"},{"location":"guide/howto/record_and_replay_http_traffic/#code_1","text":"package guide.howto.record_and_replay_http_traffic import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.TrafficFilters import org.http4k.traffic.ReadWriteStream import org.http4k.traffic.Responder fun main () { // set up storage to stash a stream of HTTP traffic. // Disk and Memory implementations are provided. val storage = ReadWriteStream . Memory () // wrap any HTTP Handler in a Recording Filter and play traffic through it val recording = TrafficFilters . RecordTo ( storage ). then { Response ( OK ). body ( \"hello world\" ) } recording ( Request ( GET , \"http://localhost:8000/\" )) // now set up a responder val handler = Responder . from ( storage ) // the responder will replay the responses in order println ( handler ( Request ( GET , \"http://localhost:8000/\" ))) // we can also replay a series of requests through a real HTTP client val client = ApacheClient () storage . requests (). forEach { println ( client ( it )) } }","title":"Code"},{"location":"guide/howto/record_and_replay_http_traffic/#concepts","text":"The org.http4k.traffic package contains the interfaces which make up the core concepts for traffic capture and replay. These interfaces are: A Sink consumes request/response pairs for storage. A Source provides lookup of pre-stored Response based on an HTTP Request. Replay instances provide streams of HTTP messages as they were received. A ReadWriteCache combines Sink and Source to provide cache-like storage. A ReadWriteStream combines Sink and Replay to provide a stream of traffic which can be replayed. The API has been designed to be modular so API users can provide their own implementations (store in S3 etc..).","title":"Concepts"},{"location":"guide/howto/secure_and_auth_http/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) // for OAuth examples implementation ( \"org.http4k:http4k-security-oauth\" ) } http4k provides a set of Filters for authenticating into other HTTP services. Usage of these filters is shown below to authenticate into a service. Each authentication type is generally available using both dynamic and static credential provision and checking mechanisms. Code \u00b6 package guide.howto.secure_and_auth_http import org.http4k.client.OkHttp import org.http4k.core.Body import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ClientFilters import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessTokenResponse import org.http4k.security.CredentialsProvider import org.http4k.security.ExpiringCredentials import org.http4k.security.RefreshCredentials import org.http4k.security.Refreshing import org.http4k.security.oauth.client.OAuthClientCredentials import org.http4k.security.oauth.client.RefreshingOAuthToken import org.http4k.security.oauth.format.OAuthMoshi.auto import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer import java.time.Duration import java.time.Instant fun main () { val server = AuthServer (). start () val baseHttp = ClientFilters . SetBaseUriFrom ( Uri . of ( \"http://localhost: ${ server . port () } \" )) . then ( OkHttp ()) /** * simplest hard coded basic auth details */ println ( ClientFilters . BasicAuth ( \"username\" , \"password\" ) . then ( baseHttp )( Request ( GET , \"/basic\" )) ) /** * using a dynamically provided bearer token */ println ( ClientFilters . BearerAuth ( CredentialsProvider { \"bearerToken\" + System . currentTimeMillis () }) . then ( baseHttp )( Request ( GET , \"/bearer\" )) ) /** * using using auto-refreshed bearer token */ // this is the refresh function - it is called when the old token is null or expired... val refreshFn = RefreshCredentials < String > { oldToken -> println ( \"refreshing credentials (was $ oldToken )\" ) ExpiringCredentials ( \"bearerToken\" + System . currentTimeMillis (), Instant . now (). plusSeconds ( 5 ) ) } val refreshingClient = ClientFilters . BearerAuth ( CredentialsProvider . Refreshing ( gracePeriod = Duration . ofSeconds ( 1 ), refreshFn = refreshFn ) ). then ( baseHttp ) repeat ( 10 ) { println ( refreshingClient ( Request ( GET , \"/bearer\" )). bodyString ()) Thread . sleep ( 2000 ) } /** * auth against OAuth ClientCredentials flow using refreshing credentials */ val clientCredentials = Credentials ( \"id\" , \"secret\" ) val refreshingOAuthClient = ClientFilters . RefreshingOAuthToken ( oauthCredentials = clientCredentials , tokenUri = Uri . of ( \"/oauth\" ), backend = baseHttp , oAuthFlowFilter = ClientFilters . OAuthClientCredentials ( clientCredentials ), gracePeriod = Duration . ofSeconds ( 1 ) ). then ( baseHttp ) repeat ( 10 ) { println ( refreshingOAuthClient ( Request ( GET , \"/bearer\" )). bodyString ()) Thread . sleep ( 2000 ) } server . stop () } private fun AuthServer (): Http4kServer { val endpoint : HttpHandler = { Response ( OK ). body ( it . header ( \"Authorization\" ). toString ()) } return routes ( // statically check the user creds \"basic\" bind GET to ServerFilters . BasicAuth ( \"realm\" , \"username\" , \"password\" ). then ( endpoint ), // dynamically check the token \"bearer\" bind GET to ServerFilters . BearerAuth { it . startsWith ( \"bearerToken\" ) }. then ( endpoint ), // fake oauth token endpoint \"oauth\" bind POST to { println ( \"refreshing oauth token (was \" + it . bodyString () + \")\" ) Response ( OK ). with ( Body . auto < AccessTokenResponse > (). toLens () of AccessTokenResponse ( access_token = \"bearerTokenOAuth\" + System . currentTimeMillis (), expires_in = 5 , refresh_token = \"refreshToken\" + System . currentTimeMillis (), ) ) } ). asServer ( SunHttp ( 8000 )) }","title":"Secure and auth HTTP services"},{"location":"guide/howto/secure_and_auth_http/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) // for OAuth examples implementation ( \"org.http4k:http4k-security-oauth\" ) } http4k provides a set of Filters for authenticating into other HTTP services. Usage of these filters is shown below to authenticate into a service. Each authentication type is generally available using both dynamic and static credential provision and checking mechanisms.","title":"Gradle setup"},{"location":"guide/howto/secure_and_auth_http/#code","text":"package guide.howto.secure_and_auth_http import org.http4k.client.OkHttp import org.http4k.core.Body import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ClientFilters import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessTokenResponse import org.http4k.security.CredentialsProvider import org.http4k.security.ExpiringCredentials import org.http4k.security.RefreshCredentials import org.http4k.security.Refreshing import org.http4k.security.oauth.client.OAuthClientCredentials import org.http4k.security.oauth.client.RefreshingOAuthToken import org.http4k.security.oauth.format.OAuthMoshi.auto import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer import java.time.Duration import java.time.Instant fun main () { val server = AuthServer (). start () val baseHttp = ClientFilters . SetBaseUriFrom ( Uri . of ( \"http://localhost: ${ server . port () } \" )) . then ( OkHttp ()) /** * simplest hard coded basic auth details */ println ( ClientFilters . BasicAuth ( \"username\" , \"password\" ) . then ( baseHttp )( Request ( GET , \"/basic\" )) ) /** * using a dynamically provided bearer token */ println ( ClientFilters . BearerAuth ( CredentialsProvider { \"bearerToken\" + System . currentTimeMillis () }) . then ( baseHttp )( Request ( GET , \"/bearer\" )) ) /** * using using auto-refreshed bearer token */ // this is the refresh function - it is called when the old token is null or expired... val refreshFn = RefreshCredentials < String > { oldToken -> println ( \"refreshing credentials (was $ oldToken )\" ) ExpiringCredentials ( \"bearerToken\" + System . currentTimeMillis (), Instant . now (). plusSeconds ( 5 ) ) } val refreshingClient = ClientFilters . BearerAuth ( CredentialsProvider . Refreshing ( gracePeriod = Duration . ofSeconds ( 1 ), refreshFn = refreshFn ) ). then ( baseHttp ) repeat ( 10 ) { println ( refreshingClient ( Request ( GET , \"/bearer\" )). bodyString ()) Thread . sleep ( 2000 ) } /** * auth against OAuth ClientCredentials flow using refreshing credentials */ val clientCredentials = Credentials ( \"id\" , \"secret\" ) val refreshingOAuthClient = ClientFilters . RefreshingOAuthToken ( oauthCredentials = clientCredentials , tokenUri = Uri . of ( \"/oauth\" ), backend = baseHttp , oAuthFlowFilter = ClientFilters . OAuthClientCredentials ( clientCredentials ), gracePeriod = Duration . ofSeconds ( 1 ) ). then ( baseHttp ) repeat ( 10 ) { println ( refreshingOAuthClient ( Request ( GET , \"/bearer\" )). bodyString ()) Thread . sleep ( 2000 ) } server . stop () } private fun AuthServer (): Http4kServer { val endpoint : HttpHandler = { Response ( OK ). body ( it . header ( \"Authorization\" ). toString ()) } return routes ( // statically check the user creds \"basic\" bind GET to ServerFilters . BasicAuth ( \"realm\" , \"username\" , \"password\" ). then ( endpoint ), // dynamically check the token \"bearer\" bind GET to ServerFilters . BearerAuth { it . startsWith ( \"bearerToken\" ) }. then ( endpoint ), // fake oauth token endpoint \"oauth\" bind POST to { println ( \"refreshing oauth token (was \" + it . bodyString () + \")\" ) Response ( OK ). with ( Body . auto < AccessTokenResponse > (). toLens () of AccessTokenResponse ( access_token = \"bearerTokenOAuth\" + System . currentTimeMillis (), expires_in = 5 , refresh_token = \"refreshToken\" + System . currentTimeMillis (), ) ) } ). asServer ( SunHttp ( 8000 )) }","title":"Code"},{"location":"guide/howto/self_document_systems_with_tests/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) testImplementation ( \"org.http4k:http4k-testing-tracerbullet\" ) } When composing several http4k services together and talking to Fakes representing external systems, we can use a combination of request tracing filters (utilising distributed tracing headers) and the http4k event stream to capture and record events. This stream can be stored to disk or outputted to various rendered formats such as PlantUML or Mermaid . Code \u00b6 package guide.howto.self_document_systems_with_tests import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.events.EventFilters.AddServiceName import org.http4k.events.EventFilters.AddZipkinTraces import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.events.and import org.http4k.events.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.ResetRequestTracing import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters.RequestTracing import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.reverseProxy import org.http4k.routing.routes import org.http4k.tracing.Actor import org.http4k.tracing.ActorResolver import org.http4k.tracing.ActorType import org.http4k.tracing.TraceRenderPersistence import org.http4k.tracing.junit.TracerBulletEvents import org.http4k.tracing.persistence.FileSystem import org.http4k.tracing.renderer.PumlSequenceDiagram import org.http4k.tracing.tracer.HttpTracer import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import java.io.File // standardised events stack which records the service name and adds tracing fun TraceEvents ( actorName : String ) = AddZipkinTraces (). then ( AddServiceName ( actorName )) // standardised client filter stack which adds tracing and records traffic events fun ClientStack ( events : Events ) = ClientFilters . RequestTracing () . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) // standardised server filter stack which adds tracing and records traffic events fun ServerStack ( events : Events ) = RequestTracing (). then ( ReportHttpTransaction { events ( Incoming ( it )) }) // Our \"User\" object who will send a request to our system class User ( rawEvents : Events , rawHttp : HttpHandler ) { private val events = TraceEvents ( \"user\" ). then ( rawEvents ) // as the user is the initiator of requests, we need to reset the tracing for each call. private val http = ResetRequestTracing (). then ( ClientStack ( events )). then ( rawHttp ) fun initiateCall () = http ( Request ( GET , \"http://internal1/int1\" )) } // the first internal app fun Internal1 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal1\" ). then ( rawEvents ). and ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int1\" bind { _ : Request -> val first = http ( Request ( GET , \"http://external1/ext1\" )) when { first . status . successful -> http ( Request ( GET , \"http://internal2/int2\" )) else -> first } }) ) } // the second internal app fun Internal2 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal2\" ). then ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int2\" bind { _ : Request -> http ( Request ( GET , \"http://external2/ext2\" )) }) ) } // an external fake system fun FakeExternal1 (): HttpHandler = { Response ( OK ) } // another external fake system fun FakeExternal2 (): HttpHandler = { Response ( OK ) } private val actor = ActorResolver { Actor ( it . metadata [ \"service\" ] . toString (), ActorType . System ) } /** * Our test will capture the traffic and render it to the console */ class RenderingTest { @RegisterExtension // this events implementation will automatically capture the HTTP traffic val events = TracerBulletEvents ( listOf ( HttpTracer ( actor )), // A tracer to capture HTTP calls listOf ( PumlSequenceDiagram ), // Render the HTTP traffic as a PUML diagram TraceRenderPersistence . FileSystem ( File ( \".\" )) // Store the result ) @Test fun `render successful trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to FakeExternal1 (), \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } @Test @Disabled ( \"Remove this to run the failing test!\" ) fun `render failure trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to { _ : Request -> Response ( INTERNAL_SERVER_ERROR ) }, \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } }","title":"Self document systems with tests"},{"location":"guide/howto/self_document_systems_with_tests/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) testImplementation ( \"org.http4k:http4k-testing-tracerbullet\" ) } When composing several http4k services together and talking to Fakes representing external systems, we can use a combination of request tracing filters (utilising distributed tracing headers) and the http4k event stream to capture and record events. This stream can be stored to disk or outputted to various rendered formats such as PlantUML or Mermaid .","title":"Gradle setup"},{"location":"guide/howto/self_document_systems_with_tests/#code","text":"package guide.howto.self_document_systems_with_tests import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.events.EventFilters.AddServiceName import org.http4k.events.EventFilters.AddZipkinTraces import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.events.and import org.http4k.events.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.ResetRequestTracing import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters.RequestTracing import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.reverseProxy import org.http4k.routing.routes import org.http4k.tracing.Actor import org.http4k.tracing.ActorResolver import org.http4k.tracing.ActorType import org.http4k.tracing.TraceRenderPersistence import org.http4k.tracing.junit.TracerBulletEvents import org.http4k.tracing.persistence.FileSystem import org.http4k.tracing.renderer.PumlSequenceDiagram import org.http4k.tracing.tracer.HttpTracer import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import java.io.File // standardised events stack which records the service name and adds tracing fun TraceEvents ( actorName : String ) = AddZipkinTraces (). then ( AddServiceName ( actorName )) // standardised client filter stack which adds tracing and records traffic events fun ClientStack ( events : Events ) = ClientFilters . RequestTracing () . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) // standardised server filter stack which adds tracing and records traffic events fun ServerStack ( events : Events ) = RequestTracing (). then ( ReportHttpTransaction { events ( Incoming ( it )) }) // Our \"User\" object who will send a request to our system class User ( rawEvents : Events , rawHttp : HttpHandler ) { private val events = TraceEvents ( \"user\" ). then ( rawEvents ) // as the user is the initiator of requests, we need to reset the tracing for each call. private val http = ResetRequestTracing (). then ( ClientStack ( events )). then ( rawHttp ) fun initiateCall () = http ( Request ( GET , \"http://internal1/int1\" )) } // the first internal app fun Internal1 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal1\" ). then ( rawEvents ). and ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int1\" bind { _ : Request -> val first = http ( Request ( GET , \"http://external1/ext1\" )) when { first . status . successful -> http ( Request ( GET , \"http://internal2/int2\" )) else -> first } }) ) } // the second internal app fun Internal2 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal2\" ). then ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int2\" bind { _ : Request -> http ( Request ( GET , \"http://external2/ext2\" )) }) ) } // an external fake system fun FakeExternal1 (): HttpHandler = { Response ( OK ) } // another external fake system fun FakeExternal2 (): HttpHandler = { Response ( OK ) } private val actor = ActorResolver { Actor ( it . metadata [ \"service\" ] . toString (), ActorType . System ) } /** * Our test will capture the traffic and render it to the console */ class RenderingTest { @RegisterExtension // this events implementation will automatically capture the HTTP traffic val events = TracerBulletEvents ( listOf ( HttpTracer ( actor )), // A tracer to capture HTTP calls listOf ( PumlSequenceDiagram ), // Render the HTTP traffic as a PUML diagram TraceRenderPersistence . FileSystem ( File ( \".\" )) // Store the result ) @Test fun `render successful trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to FakeExternal1 (), \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } @Test @Disabled ( \"Remove this to run the failing test!\" ) fun `render failure trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to { _ : Request -> Response ( INTERNAL_SERVER_ERROR ) }, \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } }","title":"Code"},{"location":"guide/howto/serve_sse/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) } http4k provides SSE (Server Sent Events) support using a simple, consistent, typesafe, and testable API on supported server backends (see above). SSE communication consists of 3 main concepts: SseHandler - represented as a typealias: SseHandler = (Request) -> SseConsumer . This is responsible for matching an HTTP request to an SSE handler. SseConsumer - represented as a typealias: SseConsumer = (Sse) -> Unit . This function is called on connection of a Sse and allow the API user to receive to events coming from the connected SSE handler. SseMessage - a message which is sent from the SSE handler SseMessages are immutable data classes. SseFilter - represented as a interface: SseFilter = (SseConsumer) -> SseConsumer . This allows for the decoration of SseConsumers to add pre or post matching behaviour in the same way as a standard Filter . SSE as a Function \u00b6 The simplest possible SSE handler can be mounted as a SseConsumer function onto a server with: { sse : Sse -> sse . send ( SseMessage . Data ( \"hello\" )) }. asServer ( Undertow ( 9000 )). start () Mixing HTTP and SSE services \u00b6 Both SSE and Http handlers in http4k are routed using a similar path-based API. We combine them into a single PolyHandler . SSE handlers react to HTTP traffic which send an Accept header with text/event-stream value: import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.routing.sse import org.http4k.routing.sse.bind import org.http4k.server.PolyHandler import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.sse.Sse import org.http4k.sse.SseFilter import org.http4k.sse.SseMessage import org.http4k.sse.SseResponse import org.http4k.sse.then import kotlin.concurrent.thread fun main () { val namePath = Path . of ( \"name\" ) // a filter allows us to intercept the call to the sse and do logging etc... val sayHello = SseFilter { next -> { println ( \"Hello from the sse!\" ) next ( it ) } } val sse = sayHello . then ( sse ( \"/{name}\" bind { req -> SseResponse { sse : Sse -> val name = namePath ( req ) thread { repeat ( 10 ) { sse . send ( SseMessage . Data ( \"hello $ it \" )) Thread . sleep ( 100 ) } sse . close () } sse . onClose { println ( \" $ name is closing\" ) } } } ) ) val http = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } PolyHandler ( http , sse = sse ). asServer ( Undertow ( 9000 )). start () }","title":"Serve Server-Sent Events"},{"location":"guide/howto/serve_sse/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) } http4k provides SSE (Server Sent Events) support using a simple, consistent, typesafe, and testable API on supported server backends (see above). SSE communication consists of 3 main concepts: SseHandler - represented as a typealias: SseHandler = (Request) -> SseConsumer . This is responsible for matching an HTTP request to an SSE handler. SseConsumer - represented as a typealias: SseConsumer = (Sse) -> Unit . This function is called on connection of a Sse and allow the API user to receive to events coming from the connected SSE handler. SseMessage - a message which is sent from the SSE handler SseMessages are immutable data classes. SseFilter - represented as a interface: SseFilter = (SseConsumer) -> SseConsumer . This allows for the decoration of SseConsumers to add pre or post matching behaviour in the same way as a standard Filter .","title":"Gradle setup"},{"location":"guide/howto/serve_sse/#sse_as_a_function","text":"The simplest possible SSE handler can be mounted as a SseConsumer function onto a server with: { sse : Sse -> sse . send ( SseMessage . Data ( \"hello\" )) }. asServer ( Undertow ( 9000 )). start ()","title":"SSE as a Function"},{"location":"guide/howto/serve_sse/#mixing_http_and_sse_services","text":"Both SSE and Http handlers in http4k are routed using a similar path-based API. We combine them into a single PolyHandler . SSE handlers react to HTTP traffic which send an Accept header with text/event-stream value: import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.routing.sse import org.http4k.routing.sse.bind import org.http4k.server.PolyHandler import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.sse.Sse import org.http4k.sse.SseFilter import org.http4k.sse.SseMessage import org.http4k.sse.SseResponse import org.http4k.sse.then import kotlin.concurrent.thread fun main () { val namePath = Path . of ( \"name\" ) // a filter allows us to intercept the call to the sse and do logging etc... val sayHello = SseFilter { next -> { println ( \"Hello from the sse!\" ) next ( it ) } } val sse = sayHello . then ( sse ( \"/{name}\" bind { req -> SseResponse { sse : Sse -> val name = namePath ( req ) thread { repeat ( 10 ) { sse . send ( SseMessage . Data ( \"hello $ it \" )) Thread . sleep ( 100 ) } sse . close () } sse . onClose { println ( \" $ name is closing\" ) } } } ) ) val http = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } PolyHandler ( http , sse = sse ). asServer ( Undertow ( 9000 )). start () }","title":"Mixing HTTP and SSE services"},{"location":"guide/howto/serve_websockets/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-client-websocket\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) } http4k provides Websocket support using a simple, consistent, typesafe, and testable API on supported server backends (see above). Websocket communication consists of 4 main concepts: WsHandler - represented as a typealias: WsHandler = (Request) -> WsConsumer . This is responsible for matching an HTTP request to a websocket. WsConsumer - represented as a typealias: WsConsumer = (WebSocket) -> Unit . This function is called on connection of a websocket and allow the API user to react to events coming from the connected websocket. WsMessage - a message which is sent or received on a websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. Just like the http4k HTTP message model, WsMessages are immutable data classes. WsFilter - represented as a interface: WsFilter = (WsConsumer) -> WsConsumer . This allows for the decoration of WsConsumers to add pre or post matching behaviour in the same way as a standard Filter . Websocket as a Function \u00b6 The simplest possible Websocket can be mounted as a WsConsumer function onto a server with: { ws : Websocket -> ws . send ( WsMessage ( \"hello\" )) }. asServer ( Jetty ( 9000 )). start () Mixing HTTP and Websocket services \u00b6 Both Websockets and Http handlers in http4k are routed using a similar path-based API. We combine them into a single PolyHandler which can handle both http:// and ws:// , and then convert to a Server as usual: package guide.howto.serve_websockets import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse fun main () { val namePath = Path . of ( \"name\" ) val ws = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) ws . onMessage { ws . send ( WsMessage ( \" $ name is responding\" )) } ws . onClose { println ( \" $ name is closing\" ) } } } ) val http = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start () } Auto-marshalling Websockets messages \u00b6 Using the standard Lens API, we can auto-convert Websocket messages on and off the wire. This example uses the Jackson for the marshalling: package guide.howto.serve_websockets import org.http4k.client.WebsocketClient import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.format.Jackson.auto import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse data class Person ( val name : String , val age : Int ) fun main () { // a lens that will marshall the Person object on and off the wire val personLens = WsMessage . auto < Person > (). toLens () val server = websockets ( \"/ageMe\" bind { req : Request -> WsResponse { ws : Websocket -> ws . onMessage { val person = personLens ( it ) ws . send ( personLens . create ( person . copy ( age = person . age + 10 ))) ws . close () } } } ). asServer ( Jetty ( 8000 )). start () val client = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/ageMe\" )) // send a message in \"native form\" - we could also use the Lens here to auto-marshall client . send ( WsMessage ( \"\"\"{ \"name\":\"bob\", \"age\": 25 }\"\"\" )) // read all of the messages from the socket until it is closed (by the server). // we expect to get one message back before the stream is closed. client . received (). toList (). forEach ( :: println ) server . stop () } Testing Websockets \u00b6 http4k provides Websockets that are both typesafe (via the Lens API), and testable. Both WsHandlers and PolyHandlers are convertible to a WsClient which provides a synchronous API for testing reactions to Websocket events in an offline environment. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package guide.howto.serve_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsFilter import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.http4k.websocket.then import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // a filter allows us to intercept the call to the websocket and do logging etc... val sayHello = WsFilter { next -> { println ( \"Hello from the websocket!\" ) next ( it ) } } // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = sayHello . then ( websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - // it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"Serve Websockets"},{"location":"guide/howto/serve_websockets/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-client-websocket\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) } http4k provides Websocket support using a simple, consistent, typesafe, and testable API on supported server backends (see above). Websocket communication consists of 4 main concepts: WsHandler - represented as a typealias: WsHandler = (Request) -> WsConsumer . This is responsible for matching an HTTP request to a websocket. WsConsumer - represented as a typealias: WsConsumer = (WebSocket) -> Unit . This function is called on connection of a websocket and allow the API user to react to events coming from the connected websocket. WsMessage - a message which is sent or received on a websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. Just like the http4k HTTP message model, WsMessages are immutable data classes. WsFilter - represented as a interface: WsFilter = (WsConsumer) -> WsConsumer . This allows for the decoration of WsConsumers to add pre or post matching behaviour in the same way as a standard Filter .","title":"Gradle setup"},{"location":"guide/howto/serve_websockets/#websocket_as_a_function","text":"The simplest possible Websocket can be mounted as a WsConsumer function onto a server with: { ws : Websocket -> ws . send ( WsMessage ( \"hello\" )) }. asServer ( Jetty ( 9000 )). start ()","title":"Websocket as a Function"},{"location":"guide/howto/serve_websockets/#mixing_http_and_websocket_services","text":"Both Websockets and Http handlers in http4k are routed using a similar path-based API. We combine them into a single PolyHandler which can handle both http:// and ws:// , and then convert to a Server as usual: package guide.howto.serve_websockets import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse fun main () { val namePath = Path . of ( \"name\" ) val ws = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) ws . onMessage { ws . send ( WsMessage ( \" $ name is responding\" )) } ws . onClose { println ( \" $ name is closing\" ) } } } ) val http = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start () }","title":"Mixing HTTP and Websocket services"},{"location":"guide/howto/serve_websockets/#auto-marshalling_websockets_messages","text":"Using the standard Lens API, we can auto-convert Websocket messages on and off the wire. This example uses the Jackson for the marshalling: package guide.howto.serve_websockets import org.http4k.client.WebsocketClient import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.format.Jackson.auto import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse data class Person ( val name : String , val age : Int ) fun main () { // a lens that will marshall the Person object on and off the wire val personLens = WsMessage . auto < Person > (). toLens () val server = websockets ( \"/ageMe\" bind { req : Request -> WsResponse { ws : Websocket -> ws . onMessage { val person = personLens ( it ) ws . send ( personLens . create ( person . copy ( age = person . age + 10 ))) ws . close () } } } ). asServer ( Jetty ( 8000 )). start () val client = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/ageMe\" )) // send a message in \"native form\" - we could also use the Lens here to auto-marshall client . send ( WsMessage ( \"\"\"{ \"name\":\"bob\", \"age\": 25 }\"\"\" )) // read all of the messages from the socket until it is closed (by the server). // we expect to get one message back before the stream is closed. client . received (). toList (). forEach ( :: println ) server . stop () }","title":"Auto-marshalling Websockets messages"},{"location":"guide/howto/serve_websockets/#testing_websockets","text":"http4k provides Websockets that are both typesafe (via the Lens API), and testable. Both WsHandlers and PolyHandlers are convertible to a WsClient which provides a synchronous API for testing reactions to Websocket events in an offline environment. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package guide.howto.serve_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsFilter import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.http4k.websocket.then import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // a filter allows us to intercept the call to the websocket and do logging etc... val sayHello = WsFilter { next -> { println ( \"Hello from the websocket!\" ) next ( it ) } } // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = sayHello . then ( websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - // it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"Testing Websockets"},{"location":"guide/howto/server_as_a_function/","text":"This example is the simplest possible \"server\" implementation. Note that we are not spinning up a server-backend here - but the entire application(!) is testable by firing HTTP requests at it as if it were. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Code \u00b6 package guide.howto.server_as_a_function import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status fun main () { val app : HttpHandler = { request : Request -> Response ( Status . OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val request = Request ( Method . GET , \"/\" ). query ( \"name\" , \"John Doe\" ) val response = app ( request ) println ( response ) }","title":"Basic: Server as a function"},{"location":"guide/howto/server_as_a_function/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/server_as_a_function/#code","text":"package guide.howto.server_as_a_function import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status fun main () { val app : HttpHandler = { request : Request -> Response ( Status . OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val request = Request ( Method . GET , \"/\" ). query ( \"name\" , \"John Doe\" ) val response = app ( request ) println ( response ) }","title":"Code"},{"location":"guide/howto/simple_routing/","text":"This example shows how to use the simple routing functionality to bind several routes. For the typesafe contract-style routing, refer to this recipe instead, Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Code \u00b6 package guide.howto.simple_routing import org.http4k.core.Method.DELETE import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes fun main () { val app = routes ( \"bob\" bind GET to { Response ( OK ). body ( \"you GET bob\" ) }, \"rita\" bind POST to { Response ( OK ). body ( \"you POST rita\" ) }, \"sue\" bind DELETE to { Response ( OK ). body ( \"you DELETE sue\" ) } ) println ( app ( Request ( GET , \"/bob\" ))) println ( app ( Request ( POST , \"/rita\" ))) println ( app ( Request ( DELETE , \"/sue\" ))) }","title":"Basic: Simple routing"},{"location":"guide/howto/simple_routing/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/simple_routing/#code","text":"package guide.howto.simple_routing import org.http4k.core.Method.DELETE import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes fun main () { val app = routes ( \"bob\" bind GET to { Response ( OK ). body ( \"you GET bob\" ) }, \"rita\" bind POST to { Response ( OK ). body ( \"you POST rita\" ) }, \"sue\" bind DELETE to { Response ( OK ). body ( \"you DELETE sue\" ) } ) println ( app ( Request ( GET , \"/bob\" ))) println ( app ( Request ( POST , \"/rita\" ))) println ( app ( Request ( DELETE , \"/sue\" ))) }","title":"Code"},{"location":"guide/howto/structure_your_logs_with_events/","text":"Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) } In order to leverage modern log aggregation platforms, we should move away from logging arbitrary strings into the StdOut of our applications, and move towards Structured Logging instead, which allows us to treat logs as data which can be mined to give us better observability of our systems. This also encourages the move for developers to think about which events happening in your apps are actually important and what data is appropriate to be attached to each one. http4k supports Structured Logging using a simple yet powerful concept - an Event is simply a marker interface that can be attached to any class, which we then send to an instance of Events (a \"sink\" for sending Event instances to). As with the HttpHandler , Events is just a typealias of (Event) -> Unit , and similarly to the HttpHandler , an Event can be transformed or decorated with metadata using an EventFilter (modelled as (Events) -> Events )). Support for leveraging auto \"object to JSON\" transformational capabilities is included for the libraries that have it (eg. Jackson and GSON). This allows custom Json instances to be used (for instance) to avoid PII information being spat out to log aggregation platforms where they could be masked using the configuration of the JSON renderer. Attaching metadata to an Event results in (compactified) JSON similar to this: { \"event\" : { \"uri\" : \"/path1\" , \"status\" : 200 , \"duration\" : 16 }, \"metadata\" : { \"timestamp\" : \"2019-11-05T17:32:27.297448Z\" , \"name\" : \"IncomingHttpRequest\" , \"traces\" : { \"traceId\" : \"e35304c95b704c7d\" , \"spanId\" : \"0e46f7b3cb5bcf2e\" , \"parentSpanId\" : null , \"samplingDecision\" : \"1\" }, \"requestCount\" : 1234 } } In harmony with the ethos of http4k there is no need to bring in a custom logging library such as SL4J, although they would be very simple to integrate if required by implementing a custom Events instance. The example below shows a simple application that outputs structured logs to StdOut which can be analysed by an aggregator, along with the attachment of extra Event metadata via a custom EventFilter . Code \u00b6 package guide.howto.structure_your_logs_with_events import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.events.AutoMarshallingEvents import org.http4k.events.Event import org.http4k.events.EventFilter import org.http4k.events.EventFilters import org.http4k.events.plus import org.http4k.events.then import org.http4k.filter.ResponseFilters import org.http4k.format.Jackson fun main () { // Stack filters for Events in the same way as HttpHandlers to // transform or add metadata to the Events. // We use AutoMarshallingEvents (here with Jackson) to // handle the final serialisation process. val events = EventFilters . AddTimestamp () . then ( EventFilters . AddEventName ()) . then ( EventFilters . AddZipkinTraces ()) . then ( AddRequestCount ()) . then ( AutoMarshallingEvents ( Jackson )) val app : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hello\" ) } val appWithEvents = ResponseFilters . ReportHttpTransaction { // to \"emit\" an event, just invoke() the Events! events ( IncomingHttpRequest ( uri = it . request . uri , status = it . response . status . code , duration = it . duration . toMillis () ) ) }. then ( app ) appWithEvents ( Request ( GET , \"/path1\" )) appWithEvents ( Request ( GET , \"/path2\" )) } // this is our custom event which will be printed in a structured way data class IncomingHttpRequest ( val uri : Uri , val status : Int , val duration : Long ) : Event // here is a new EventFilter that adds custom metadata to the emitted events fun AddRequestCount (): EventFilter { var requestCount = 0 return EventFilter { next -> { next ( it + ( \"requestCount\" to requestCount ++ )) } } }","title":"Structure your logs with Events"},{"location":"guide/howto/structure_your_logs_with_events/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-format-jackson\" ) } In order to leverage modern log aggregation platforms, we should move away from logging arbitrary strings into the StdOut of our applications, and move towards Structured Logging instead, which allows us to treat logs as data which can be mined to give us better observability of our systems. This also encourages the move for developers to think about which events happening in your apps are actually important and what data is appropriate to be attached to each one. http4k supports Structured Logging using a simple yet powerful concept - an Event is simply a marker interface that can be attached to any class, which we then send to an instance of Events (a \"sink\" for sending Event instances to). As with the HttpHandler , Events is just a typealias of (Event) -> Unit , and similarly to the HttpHandler , an Event can be transformed or decorated with metadata using an EventFilter (modelled as (Events) -> Events )). Support for leveraging auto \"object to JSON\" transformational capabilities is included for the libraries that have it (eg. Jackson and GSON). This allows custom Json instances to be used (for instance) to avoid PII information being spat out to log aggregation platforms where they could be masked using the configuration of the JSON renderer. Attaching metadata to an Event results in (compactified) JSON similar to this: { \"event\" : { \"uri\" : \"/path1\" , \"status\" : 200 , \"duration\" : 16 }, \"metadata\" : { \"timestamp\" : \"2019-11-05T17:32:27.297448Z\" , \"name\" : \"IncomingHttpRequest\" , \"traces\" : { \"traceId\" : \"e35304c95b704c7d\" , \"spanId\" : \"0e46f7b3cb5bcf2e\" , \"parentSpanId\" : null , \"samplingDecision\" : \"1\" }, \"requestCount\" : 1234 } } In harmony with the ethos of http4k there is no need to bring in a custom logging library such as SL4J, although they would be very simple to integrate if required by implementing a custom Events instance. The example below shows a simple application that outputs structured logs to StdOut which can be analysed by an aggregator, along with the attachment of extra Event metadata via a custom EventFilter .","title":"Gradle setup"},{"location":"guide/howto/structure_your_logs_with_events/#code","text":"package guide.howto.structure_your_logs_with_events import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.events.AutoMarshallingEvents import org.http4k.events.Event import org.http4k.events.EventFilter import org.http4k.events.EventFilters import org.http4k.events.plus import org.http4k.events.then import org.http4k.filter.ResponseFilters import org.http4k.format.Jackson fun main () { // Stack filters for Events in the same way as HttpHandlers to // transform or add metadata to the Events. // We use AutoMarshallingEvents (here with Jackson) to // handle the final serialisation process. val events = EventFilters . AddTimestamp () . then ( EventFilters . AddEventName ()) . then ( EventFilters . AddZipkinTraces ()) . then ( AddRequestCount ()) . then ( AutoMarshallingEvents ( Jackson )) val app : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hello\" ) } val appWithEvents = ResponseFilters . ReportHttpTransaction { // to \"emit\" an event, just invoke() the Events! events ( IncomingHttpRequest ( uri = it . request . uri , status = it . response . status . code , duration = it . duration . toMillis () ) ) }. then ( app ) appWithEvents ( Request ( GET , \"/path1\" )) appWithEvents ( Request ( GET , \"/path2\" )) } // this is our custom event which will be printed in a structured way data class IncomingHttpRequest ( val uri : Uri , val status : Int , val duration : Long ) : Event // here is a new EventFilter that adds custom metadata to the emitted events fun AddRequestCount (): EventFilter { var requestCount = 0 return EventFilter { next -> { next ( it + ( \"requestCount\" to requestCount ++ )) } } }","title":"Code"},{"location":"guide/howto/test_using_service_virtualisation/","text":"Using the JUnit Extensions \u00b6 package guide.howto.test_using_service_virtualisation.junit import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.junit.ServirtiumRecording import org.http4k.junit.ServirtiumReplay import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.servirtium.GitHub import org.http4k.servirtium.InteractionStorage.Companion.Disk import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.extension.RegisterExtension import java.io.File import java.nio.file.Paths /** * This client wraps the calls to a remote WordCounter service */ class WordCounterClient ( private val http : HttpHandler ) { fun wordCount ( name : String ): Int = http ( Request ( POST , \"/count\" ). body ( name )). bodyString (). toInt () } /** * This is our producing app */ class WordCounterApp : HttpHandler { override fun invoke ( req : Request ) = Response ( OK ). body ( req . bodyString (). run { if ( isBlank ()) 0 else split ( \" \" ). size }. toString () ) } /** * Defines the test contract which will be recorded and replayed later. * The injected HttpHandler is provided by the implementations of this interface. */ interface WordCounterContract { @Test fun `count the number of words` ( handler : HttpHandler ) { assertThat ( WordCounterClient ( handler ). wordCount ( \"A random string with 6 words\" ), equalTo ( 6 ) ) } @Test fun `empty string has zero words` ( handler : HttpHandler ) { assertThat ( WordCounterClient ( handler ). wordCount ( \"\" ), equalTo ( 0 )) } } /** * For the traditional use-case of a CDC, we use a real Http client to * record the traffic against a running version of the producing service. */ @Disabled class RemoteHttpRecordingWordCounterTest : WordCounterContract { private val app = SetHostFrom ( Uri . of ( \"http://serverundertest:8080\" )) . then ( ApacheClient ()) @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) } /** * In cases where the producing service codebase: * 1. Has access to the wrapping Client and the ClientContract code (eg. monorepo with several services) * 2. Is also written in http4k * ... we can have the Producer implement the contract entirely in-memory without a MiTM. */ @Disabled class InMemoryRecordingWordCounterTest : WordCounterContract { private val app = WordCounterApp () @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) @AfterEach fun after ( handler : HttpHandler ) { val name = \"this traffic is not recorded\" println ( name + \": \" + WordCounterClient ( handler ). wordCount ( name )) } } /** * In cases where the producing service codebase: * 1. Has access to the wrapping Client and the ClientContract code (eg. monorepo with several services) * 2. Is *not* written in http4k * ... we can have the Producer implement the contract by starting up the server and with a MiTM. */ @TestInstance ( PER_CLASS ) @Disabled class PortBoundRecordingWordCounterTest : WordCounterContract { @BeforeAll fun start () { // pretend that this is not an http4k service.. :) WordCounterApp (). asServer ( SunHttp ( 8080 )). start () } private val app = SetHostFrom ( Uri . of ( \"http://localhost:8080\" )) . then ( ApacheClient ()) @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) } @Disabled class ReplayFromDiskTest : WordCounterContract { @JvmField @RegisterExtension val replay = ServirtiumReplay ( \"WordCounter\" , Disk ( File ( \".\" ))) } @Disabled class ReplayFromGitHubTest : WordCounterContract { @JvmField @RegisterExtension val replay = ServirtiumReplay ( \"WordCounter\" , GitHub ( \"http4k\" , \"http4k\" , Credentials ( \"\" , \"\" ), Paths . get ( \"src/test/resources/guide/howto/service_virtualisation\" ) ) ) } Using a MiTM Proxy \u00b6 package guide.howto.test_using_service_virtualisation.mitm import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.HandleRemoteRequestFailed import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.servirtium.GitHub import org.http4k.servirtium.InteractionOptions import org.http4k.servirtium.InteractionStorage.Companion.Disk import org.http4k.servirtium.ServirtiumServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInfo import java.io.File import java.nio.file.Paths /** * This is our producing app */ fun WordCounterApp ( port : Int ): Http4kServer { val app = routes ( \"/count\" bind POST to { req : Request -> Response ( OK ). body ( req . bodyString (). run { if ( isBlank ()) 0 else split ( \" \" ). size }. toString () ) }) return app . asServer ( SunHttp ( port )) } /** * This client wraps the calls to a remote WordCounter service */ class WordCounterClient ( baseUri : Uri ) { private val http = SetHostFrom ( baseUri ) . then ( ClientFilters . HandleRemoteRequestFailed ()) . then ( ApacheClient ()) fun wordCount ( name : String ): Int = http ( Request ( POST , \"/count\" ). body ( name )). bodyString (). toInt () } /** * Defines the test contract which will be recorded and replayed later. */ interface WordCounterContract { val uri : Uri @Test fun `count the number of words` () { assertThat ( WordCounterClient ( uri ). wordCount ( \"A random string with 6 words\" ), equalTo ( 6 ) ) } @Test fun `empty string has zero words` () { assertThat ( WordCounterClient ( uri ). wordCount ( \"\" ), equalTo ( 0 )) } } /** * This calls the server directly */ @Disabled class DirectHttpWordCounterTest : WordCounterContract { override val uri = Uri . of ( \"http://serverundertest:8080\" ) } /** * Proxies traffic to the real service and records it to disk. Both MiTM and Producer start on a random port. */ @Disabled class MiTMRecordingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private val app = WordCounterApp ( 0 ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { val appPort = app . start (). port () servirtium = ServirtiumServer . Recording ( info . displayName . removeSuffix ( \"()\" ), Uri . of ( \"http://localhost: $ appPort \" ), Disk ( File ( \".\" )), object : InteractionOptions { override fun modify ( request : Request ) = request . removeHeader ( \"Host\" ). removeHeader ( \"User-agent\" ) override fun modify ( response : Response ) = response . removeHeader ( \"Date\" ) } ). start () } @AfterEach fun stop () { app . stop () servirtium . stop () } } /** * Replays incoming traffic from disk. MiTM starts on a random port. */ @Disabled class MiTMReplayingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { servirtium = ServirtiumServer . Replay ( info . displayName . removeSuffix ( \"()\" ), Disk ( File ( \".\" )), object : InteractionOptions { override fun modify ( request : Request ) = request . header ( \"Date\" , \"some overridden date\" ) } ). start () } @AfterEach fun stop () { servirtium . stop () } } /** * Replays incoming traffic from GitHub. MiTM starts on a random port. Requires a github username * and personal access token. */ @Disabled class GitHubReplayingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { servirtium = ServirtiumServer . Replay ( name = \"WordCounter.\" + info . displayName . removeSuffix ( \"()\" ), GitHub ( \"http4k\" , \"http4k\" , Credentials ( \"\" , \"\" ), Paths . get ( \"src/test/resources/guide/howto/service_virtualisation\" ) ), object : InteractionOptions { override fun modify ( request : Request ) = request . removeHeader ( \"Accept-encoding\" ) . removeHeader ( \"Connection\" ) . removeHeader ( \"Host\" ) . removeHeader ( \"User-agent\" ) . removeHeader ( \"Content-length\" ) } ). start () } @AfterEach fun stop () { servirtium . stop () } }","title":"Test using Service Virtualisation"},{"location":"guide/howto/test_using_service_virtualisation/#using_the_junit_extensions","text":"package guide.howto.test_using_service_virtualisation.junit import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.junit.ServirtiumRecording import org.http4k.junit.ServirtiumReplay import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.servirtium.GitHub import org.http4k.servirtium.InteractionStorage.Companion.Disk import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInstance import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.extension.RegisterExtension import java.io.File import java.nio.file.Paths /** * This client wraps the calls to a remote WordCounter service */ class WordCounterClient ( private val http : HttpHandler ) { fun wordCount ( name : String ): Int = http ( Request ( POST , \"/count\" ). body ( name )). bodyString (). toInt () } /** * This is our producing app */ class WordCounterApp : HttpHandler { override fun invoke ( req : Request ) = Response ( OK ). body ( req . bodyString (). run { if ( isBlank ()) 0 else split ( \" \" ). size }. toString () ) } /** * Defines the test contract which will be recorded and replayed later. * The injected HttpHandler is provided by the implementations of this interface. */ interface WordCounterContract { @Test fun `count the number of words` ( handler : HttpHandler ) { assertThat ( WordCounterClient ( handler ). wordCount ( \"A random string with 6 words\" ), equalTo ( 6 ) ) } @Test fun `empty string has zero words` ( handler : HttpHandler ) { assertThat ( WordCounterClient ( handler ). wordCount ( \"\" ), equalTo ( 0 )) } } /** * For the traditional use-case of a CDC, we use a real Http client to * record the traffic against a running version of the producing service. */ @Disabled class RemoteHttpRecordingWordCounterTest : WordCounterContract { private val app = SetHostFrom ( Uri . of ( \"http://serverundertest:8080\" )) . then ( ApacheClient ()) @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) } /** * In cases where the producing service codebase: * 1. Has access to the wrapping Client and the ClientContract code (eg. monorepo with several services) * 2. Is also written in http4k * ... we can have the Producer implement the contract entirely in-memory without a MiTM. */ @Disabled class InMemoryRecordingWordCounterTest : WordCounterContract { private val app = WordCounterApp () @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) @AfterEach fun after ( handler : HttpHandler ) { val name = \"this traffic is not recorded\" println ( name + \": \" + WordCounterClient ( handler ). wordCount ( name )) } } /** * In cases where the producing service codebase: * 1. Has access to the wrapping Client and the ClientContract code (eg. monorepo with several services) * 2. Is *not* written in http4k * ... we can have the Producer implement the contract by starting up the server and with a MiTM. */ @TestInstance ( PER_CLASS ) @Disabled class PortBoundRecordingWordCounterTest : WordCounterContract { @BeforeAll fun start () { // pretend that this is not an http4k service.. :) WordCounterApp (). asServer ( SunHttp ( 8080 )). start () } private val app = SetHostFrom ( Uri . of ( \"http://localhost:8080\" )) . then ( ApacheClient ()) @JvmField @RegisterExtension val record = ServirtiumRecording ( \"WordCounter\" , app , Disk ( File ( \".\" ))) } @Disabled class ReplayFromDiskTest : WordCounterContract { @JvmField @RegisterExtension val replay = ServirtiumReplay ( \"WordCounter\" , Disk ( File ( \".\" ))) } @Disabled class ReplayFromGitHubTest : WordCounterContract { @JvmField @RegisterExtension val replay = ServirtiumReplay ( \"WordCounter\" , GitHub ( \"http4k\" , \"http4k\" , Credentials ( \"\" , \"\" ), Paths . get ( \"src/test/resources/guide/howto/service_virtualisation\" ) ) ) }","title":"Using the JUnit Extensions"},{"location":"guide/howto/test_using_service_virtualisation/#using_a_mitm_proxy","text":"package guide.howto.test_using_service_virtualisation.mitm import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.HandleRemoteRequestFailed import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.servirtium.GitHub import org.http4k.servirtium.InteractionOptions import org.http4k.servirtium.InteractionStorage.Companion.Disk import org.http4k.servirtium.ServirtiumServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.TestInfo import java.io.File import java.nio.file.Paths /** * This is our producing app */ fun WordCounterApp ( port : Int ): Http4kServer { val app = routes ( \"/count\" bind POST to { req : Request -> Response ( OK ). body ( req . bodyString (). run { if ( isBlank ()) 0 else split ( \" \" ). size }. toString () ) }) return app . asServer ( SunHttp ( port )) } /** * This client wraps the calls to a remote WordCounter service */ class WordCounterClient ( baseUri : Uri ) { private val http = SetHostFrom ( baseUri ) . then ( ClientFilters . HandleRemoteRequestFailed ()) . then ( ApacheClient ()) fun wordCount ( name : String ): Int = http ( Request ( POST , \"/count\" ). body ( name )). bodyString (). toInt () } /** * Defines the test contract which will be recorded and replayed later. */ interface WordCounterContract { val uri : Uri @Test fun `count the number of words` () { assertThat ( WordCounterClient ( uri ). wordCount ( \"A random string with 6 words\" ), equalTo ( 6 ) ) } @Test fun `empty string has zero words` () { assertThat ( WordCounterClient ( uri ). wordCount ( \"\" ), equalTo ( 0 )) } } /** * This calls the server directly */ @Disabled class DirectHttpWordCounterTest : WordCounterContract { override val uri = Uri . of ( \"http://serverundertest:8080\" ) } /** * Proxies traffic to the real service and records it to disk. Both MiTM and Producer start on a random port. */ @Disabled class MiTMRecordingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private val app = WordCounterApp ( 0 ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { val appPort = app . start (). port () servirtium = ServirtiumServer . Recording ( info . displayName . removeSuffix ( \"()\" ), Uri . of ( \"http://localhost: $ appPort \" ), Disk ( File ( \".\" )), object : InteractionOptions { override fun modify ( request : Request ) = request . removeHeader ( \"Host\" ). removeHeader ( \"User-agent\" ) override fun modify ( response : Response ) = response . removeHeader ( \"Date\" ) } ). start () } @AfterEach fun stop () { app . stop () servirtium . stop () } } /** * Replays incoming traffic from disk. MiTM starts on a random port. */ @Disabled class MiTMReplayingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { servirtium = ServirtiumServer . Replay ( info . displayName . removeSuffix ( \"()\" ), Disk ( File ( \".\" )), object : InteractionOptions { override fun modify ( request : Request ) = request . header ( \"Date\" , \"some overridden date\" ) } ). start () } @AfterEach fun stop () { servirtium . stop () } } /** * Replays incoming traffic from GitHub. MiTM starts on a random port. Requires a github username * and personal access token. */ @Disabled class GitHubReplayingWordCounterTest : WordCounterContract { override val uri get () = Uri . of ( \"http://localhost: ${ servirtium . port () } \" ) private lateinit var servirtium : Http4kServer @BeforeEach fun start ( info : TestInfo ) { servirtium = ServirtiumServer . Replay ( name = \"WordCounter.\" + info . displayName . removeSuffix ( \"()\" ), GitHub ( \"http4k\" , \"http4k\" , Credentials ( \"\" , \"\" ), Paths . get ( \"src/test/resources/guide/howto/service_virtualisation\" ) ), object : InteractionOptions { override fun modify ( request : Request ) = request . removeHeader ( \"Accept-encoding\" ) . removeHeader ( \"Connection\" ) . removeHeader ( \"Host\" ) . removeHeader ( \"User-agent\" ) . removeHeader ( \"Content-length\" ) } ). start () } @AfterEach fun stop () { servirtium . stop () } }","title":"Using a MiTM Proxy"},{"location":"guide/howto/typesafe_your_api_with_lenses/","text":"Example showing how to create and apply lenses to requests and responses to both extract and inject typesafe values out of and into HTTP messages. Note that since the http4k Request/Response objects are immutable, all injection occurs via copy. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Standard (exception based) approach \u00b6 Errors in extracting Lenses are propagated as exceptions which are caught and handled by the CatchLensFailure Filter. package guide.howto.typesafe_your_api_with_lenses import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Query import org.http4k.lens.boolean import org.http4k.lens.composite import org.http4k.lens.int import org.http4k.lens.string fun main () { data class Child ( val name : String ) data class Pageable ( val sortAscending : Boolean , val page : Int , val maxResults : Int ) val nameHeader = Header . required ( \"name\" ) val ageQuery = Query . int (). optional ( \"age\" ) val childrenBody = Body . string ( TEXT_PLAIN ) . map ({ it . split ( \",\" ). map ( :: Child ) }, { it . joinToString { it . name } }) . toLens () val pageable = Query . composite { Pageable ( boolean (). defaulted ( \"sortAscending\" , true )( it ), int (). defaulted ( \"page\" , 1 )( it ), int (). defaulted ( \"maxResults\" , 20 )( it ) ) } val endpoint = { request : Request -> val name : String = nameHeader ( request ) val age : Int? = ageQuery ( request ) val children : List < Child > = childrenBody ( request ) val pagination = pageable ( request ) val msg = \"\"\" $ name is ${ age ?: \" unknown \" } years old and has ${ children . size } children ( ${ children . joinToString { it . name } } ) Pagination: $ pagination \"\"\" Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of msg ) } val app = ServerFilters . CatchLensFailure . then ( endpoint ) val goodRequest = Request ( GET , \"http://localhost:9000\" ). with ( nameHeader of \"Jane Doe\" , ageQuery of 25 , childrenBody of listOf ( Child ( \"Rita\" ), Child ( \"Sue\" )) ) println ( listOf ( \"\" , \"Request:\" , goodRequest , app ( goodRequest )). joinToString ( \"\\n\" )) val badRequest = Request ( GET , \"http://localhost:9000\" ) . with ( nameHeader of \"Jane Doe\" ) . query ( \"age\" , \"some illegal age!\" ) println ( listOf ( \"\" , \"Request:\" , badRequest , app ( badRequest )). joinToString ( \"\\n\" )) } Using \"Result\" ADT \u00b6 An alternative approach to using Exceptions to automatically produce BadRequests is to use an Either-type structure, and this would be easy to implement - but the lack of an usable Result/Either type in the standard Kotlin library means that we have chosen to use Result4k as an optional dependency. If it is on the classpath you will gain support for it. Additionally, the lack of Higher Kinded Types in Kotlin means that we are unable to provide a generic method for converting standard lenses. However, it is easy to implement an extension method to use in specific use cases - you can follow the example in the http4k source to implement your own version of the one we supply for Result4k. Below is an example which uses that Result4k ADT: Code \u00b6 package guide.howto.typesafe_your_api_with_lenses import com.fasterxml.jackson.databind.JsonNode import dev.forkhandles.result4k.Result import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Jackson.json import org.http4k.lens.LensFailure import org.http4k.lens.Query import org.http4k.lens.asResult import org.http4k.lens.int fun main () { val queryResultLens = Query . int (). required ( \"foo\" ). asResult () val intResult : Result < Int , LensFailure > = queryResultLens ( Request ( GET , \"/?foo=123\" )) println ( intResult ) val jsonResultLens = Body . json (). toLens (). asResult () val jsonResult : Result < JsonNode , LensFailure > = jsonResultLens ( Request ( GET , \"/foo\" )) println ( jsonResult ) }","title":"Typesafe your API with lenses"},{"location":"guide/howto/typesafe_your_api_with_lenses/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/typesafe_your_api_with_lenses/#standard_exception_based_approach","text":"Errors in extracting Lenses are propagated as exceptions which are caught and handled by the CatchLensFailure Filter. package guide.howto.typesafe_your_api_with_lenses import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Query import org.http4k.lens.boolean import org.http4k.lens.composite import org.http4k.lens.int import org.http4k.lens.string fun main () { data class Child ( val name : String ) data class Pageable ( val sortAscending : Boolean , val page : Int , val maxResults : Int ) val nameHeader = Header . required ( \"name\" ) val ageQuery = Query . int (). optional ( \"age\" ) val childrenBody = Body . string ( TEXT_PLAIN ) . map ({ it . split ( \",\" ). map ( :: Child ) }, { it . joinToString { it . name } }) . toLens () val pageable = Query . composite { Pageable ( boolean (). defaulted ( \"sortAscending\" , true )( it ), int (). defaulted ( \"page\" , 1 )( it ), int (). defaulted ( \"maxResults\" , 20 )( it ) ) } val endpoint = { request : Request -> val name : String = nameHeader ( request ) val age : Int? = ageQuery ( request ) val children : List < Child > = childrenBody ( request ) val pagination = pageable ( request ) val msg = \"\"\" $ name is ${ age ?: \" unknown \" } years old and has ${ children . size } children ( ${ children . joinToString { it . name } } ) Pagination: $ pagination \"\"\" Response ( OK ). with ( Body . string ( TEXT_PLAIN ). toLens () of msg ) } val app = ServerFilters . CatchLensFailure . then ( endpoint ) val goodRequest = Request ( GET , \"http://localhost:9000\" ). with ( nameHeader of \"Jane Doe\" , ageQuery of 25 , childrenBody of listOf ( Child ( \"Rita\" ), Child ( \"Sue\" )) ) println ( listOf ( \"\" , \"Request:\" , goodRequest , app ( goodRequest )). joinToString ( \"\\n\" )) val badRequest = Request ( GET , \"http://localhost:9000\" ) . with ( nameHeader of \"Jane Doe\" ) . query ( \"age\" , \"some illegal age!\" ) println ( listOf ( \"\" , \"Request:\" , badRequest , app ( badRequest )). joinToString ( \"\\n\" )) }","title":"Standard (exception based) approach"},{"location":"guide/howto/typesafe_your_api_with_lenses/#using_result_adt","text":"An alternative approach to using Exceptions to automatically produce BadRequests is to use an Either-type structure, and this would be easy to implement - but the lack of an usable Result/Either type in the standard Kotlin library means that we have chosen to use Result4k as an optional dependency. If it is on the classpath you will gain support for it. Additionally, the lack of Higher Kinded Types in Kotlin means that we are unable to provide a generic method for converting standard lenses. However, it is easy to implement an extension method to use in specific use cases - you can follow the example in the http4k source to implement your own version of the one we supply for Result4k. Below is an example which uses that Result4k ADT:","title":"Using \"Result\" ADT"},{"location":"guide/howto/typesafe_your_api_with_lenses/#code","text":"package guide.howto.typesafe_your_api_with_lenses import com.fasterxml.jackson.databind.JsonNode import dev.forkhandles.result4k.Result import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Jackson.json import org.http4k.lens.LensFailure import org.http4k.lens.Query import org.http4k.lens.asResult import org.http4k.lens.int fun main () { val queryResultLens = Query . int (). required ( \"foo\" ). asResult () val intResult : Result < Int , LensFailure > = queryResultLens ( Request ( GET , \"/?foo=123\" )) println ( intResult ) val jsonResultLens = Body . json (). toLens (). asResult () val jsonResult : Result < JsonNode , LensFailure > = jsonResultLens ( Request ( GET , \"/foo\" )) println ( jsonResult ) }","title":"Code"},{"location":"guide/howto/use_a_custom_oauth_provider/","text":"It is very easy to configure http4k to integrate with any OAuth2 provider who supports the Authorisation Code Grant. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-security-oauth\" ) } For this example, simply reconfigure the OAuthProvider instance with the correct details, and provide custom logic for persisting and retrieving the CSRF and AccessToken. Code \u00b6 package guide.howto.use_a_custom_oauth_provider import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessToken import org.http4k.security.CrossSiteRequestForgeryToken import org.http4k.security.Nonce import org.http4k.security.OAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.OAuthProviderConfig import org.http4k.security.openid.IdToken import org.http4k.server.SunHttp import org.http4k.server.asServer // this example shows how to configure a custom provider for the OAuth2 Auth Code Grant flow fun main () { val port = 9000 // the callback uri which is configured in our OAuth provider val callbackUri = Uri . of ( \"http://localhost: $ port /callback\" ) // custom OAuth2 provider configuration val oauthProvider = OAuthProvider ( OAuthProviderConfig ( authBase = Uri . of ( \"https://auth.chatroulette.com\" ), authPath = \"/oauth2/auth\" , tokenPath = \"/oauth2/token\" , credentials = Credentials ( \"username\" , \"somepassword\" ), apiBase = Uri . of ( \"https://api.chatroulette.com\" ) ), ApacheClient (), callbackUri , listOf ( \"emailScope\" , \"nameScope\" , \"familyScope\" ), CustomOAuthPersistence () ) val app : HttpHandler = routes ( callbackUri . path bind GET to oauthProvider . callback , \"/\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"hello!\" ) } ) ServerFilters . CatchAll () . then ( app ) . asServer ( SunHttp ( port )). start (). block () } // This interface allows us to provide custom logic for storing and // verifying the CSRF and AccessTokens. // // To be maximally secure, never let the end-user see the access token! // // Also avoid allowing third parties set the original uri as this might // allow phishing attacks. // // One strategy might be to use an enum to map to a set of know uris // e.g. shoppingCart -> /cart class CustomOAuthPersistence : OAuthPersistence { var nonce : Nonce? = null var csrf : CrossSiteRequestForgeryToken? = null var accessToken : AccessToken? = null var originalUri : Uri? = null override fun retrieveCsrf ( request : Request ): CrossSiteRequestForgeryToken? = csrf override fun assignCsrf ( redirect : Response , csrf : CrossSiteRequestForgeryToken ): Response { this . csrf = csrf return redirect . header ( \"action\" , \"assignCsrf\" ) } override fun assignNonce ( redirect : Response , nonce : Nonce ): Response { this . nonce = nonce return redirect . header ( \"action\" , \"assignNonce\" ) } override fun retrieveNonce ( request : Request ): Nonce? = nonce override fun assignOriginalUri ( redirect : Response , originalUri : Uri ): Response { this . originalUri = originalUri return redirect . header ( \"action\" , \"assignOriginalUri\" ) } override fun retrieveOriginalUri ( request : Request ): Uri? = originalUri override fun retrieveToken ( request : Request ): AccessToken? = accessToken override fun assignToken ( request : Request , redirect : Response , accessToken : AccessToken , idToken : IdToken? ): Response { this . accessToken = accessToken return redirect . header ( \"action\" , \"assignToken\" ) } }","title":"Use a custom OAuth Provider"},{"location":"guide/howto/use_a_custom_oauth_provider/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-security-oauth\" ) } For this example, simply reconfigure the OAuthProvider instance with the correct details, and provide custom logic for persisting and retrieving the CSRF and AccessToken.","title":"Gradle setup"},{"location":"guide/howto/use_a_custom_oauth_provider/#code","text":"package guide.howto.use_a_custom_oauth_provider import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.AccessToken import org.http4k.security.CrossSiteRequestForgeryToken import org.http4k.security.Nonce import org.http4k.security.OAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.OAuthProviderConfig import org.http4k.security.openid.IdToken import org.http4k.server.SunHttp import org.http4k.server.asServer // this example shows how to configure a custom provider for the OAuth2 Auth Code Grant flow fun main () { val port = 9000 // the callback uri which is configured in our OAuth provider val callbackUri = Uri . of ( \"http://localhost: $ port /callback\" ) // custom OAuth2 provider configuration val oauthProvider = OAuthProvider ( OAuthProviderConfig ( authBase = Uri . of ( \"https://auth.chatroulette.com\" ), authPath = \"/oauth2/auth\" , tokenPath = \"/oauth2/token\" , credentials = Credentials ( \"username\" , \"somepassword\" ), apiBase = Uri . of ( \"https://api.chatroulette.com\" ) ), ApacheClient (), callbackUri , listOf ( \"emailScope\" , \"nameScope\" , \"familyScope\" ), CustomOAuthPersistence () ) val app : HttpHandler = routes ( callbackUri . path bind GET to oauthProvider . callback , \"/\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"hello!\" ) } ) ServerFilters . CatchAll () . then ( app ) . asServer ( SunHttp ( port )). start (). block () } // This interface allows us to provide custom logic for storing and // verifying the CSRF and AccessTokens. // // To be maximally secure, never let the end-user see the access token! // // Also avoid allowing third parties set the original uri as this might // allow phishing attacks. // // One strategy might be to use an enum to map to a set of know uris // e.g. shoppingCart -> /cart class CustomOAuthPersistence : OAuthPersistence { var nonce : Nonce? = null var csrf : CrossSiteRequestForgeryToken? = null var accessToken : AccessToken? = null var originalUri : Uri? = null override fun retrieveCsrf ( request : Request ): CrossSiteRequestForgeryToken? = csrf override fun assignCsrf ( redirect : Response , csrf : CrossSiteRequestForgeryToken ): Response { this . csrf = csrf return redirect . header ( \"action\" , \"assignCsrf\" ) } override fun assignNonce ( redirect : Response , nonce : Nonce ): Response { this . nonce = nonce return redirect . header ( \"action\" , \"assignNonce\" ) } override fun retrieveNonce ( request : Request ): Nonce? = nonce override fun assignOriginalUri ( redirect : Response , originalUri : Uri ): Response { this . originalUri = originalUri return redirect . header ( \"action\" , \"assignOriginalUri\" ) } override fun retrieveOriginalUri ( request : Request ): Uri? = originalUri override fun retrieveToken ( request : Request ): AccessToken? = accessToken override fun assignToken ( request : Request , redirect : Response , accessToken : AccessToken , idToken : IdToken? ): Response { this . accessToken = accessToken return redirect . header ( \"action\" , \"assignToken\" ) } }","title":"Code"},{"location":"guide/howto/use_a_server_backend/","text":"This example shows how to both serve an application HttpHandler using an embedded HTTP server and to query it using an HTTP client. All server-backend implementations are launched in an identical manner (in 1LOC) using implementations of the ServerConfig interface - and a base implementation of this interface is provided for each server backend. Alternatively, any http4k application can be mounted into any Servlet container using the asServlet() extension method. This is the mechanism used in the Jetty implementation. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-client-apache\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) } Code \u00b6 package guide.howto.use_a_server_backend import org.http4k.client.ApacheClient import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { val app = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val jettyServer = app . asServer ( Jetty ( 9000 )). start () val request = Request ( Method . GET , \"http://localhost:9000\" ). query ( \"name\" , \"John Doe\" ) val client = ApacheClient () println ( client ( request )) jettyServer . stop () }","title":"Basic: Use a Server backend"},{"location":"guide/howto/use_a_server_backend/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-client-apache\" ) implementation ( \"org.http4k:http4k-server-jetty\" ) }","title":"Gradle setup"},{"location":"guide/howto/use_a_server_backend/#code","text":"package guide.howto.use_a_server_backend import org.http4k.client.ApacheClient import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { val app = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val jettyServer = app . asServer ( Jetty ( 9000 )). start () val request = Request ( Method . GET , \"http://localhost:9000\" ). query ( \"name\" , \"John Doe\" ) val client = ApacheClient () println ( client ( request )) jettyServer . stop () }","title":"Code"},{"location":"guide/howto/use_a_templating_engine/","text":"Example showing how to use the Templating modules - in this case Handlebars, both by standard response manipulation and via a typesafe view lens. Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-template-handlebars\" ) } Code \u00b6 package guide.howto.use_a_templating_engine import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel // this view has the default template path of: guide/howto/using_templates/Person.hbs, // although that is overridable by setting the template property from ViewModel data class Person ( val name : String , val age : Int ) : ViewModel fun main () { val renderer = HandlebarsTemplates (). HotReload ( \"src/docs\" ) val view = Body . viewModel ( renderer , TEXT_HTML ). toLens () val app : HttpHandler = { val viewModel = Person ( \"Bob\" , 45 ) Response ( OK ). body ( renderer ( viewModel )) // OR: Response ( OK ). with ( view of viewModel ) } println ( app ( Request ( GET , \"/someUrl\" ))) }","title":"Use a templating engine"},{"location":"guide/howto/use_a_templating_engine/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-template-handlebars\" ) }","title":"Gradle setup"},{"location":"guide/howto/use_a_templating_engine/#code","text":"package guide.howto.use_a_templating_engine import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel // this view has the default template path of: guide/howto/using_templates/Person.hbs, // although that is overridable by setting the template property from ViewModel data class Person ( val name : String , val age : Int ) : ViewModel fun main () { val renderer = HandlebarsTemplates (). HotReload ( \"src/docs\" ) val view = Body . viewModel ( renderer , TEXT_HTML ). toLens () val app : HttpHandler = { val viewModel = Person ( \"Bob\" , 45 ) Response ( OK ). body ( renderer ( viewModel )) // OR: Response ( OK ). with ( view of viewModel ) } println ( app ( Request ( GET , \"/someUrl\" ))) }","title":"Code"},{"location":"guide/howto/use_auto_content_negotiation/","text":"Example showing how to combine multiple body Lenses into a single facade that will simplify content negotiation for inbound and outbound messages. Gradle setup \u00b6 Auto Content Negotiation is available in the core http4k module. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-core\" ) } But it also integrates with the contract module. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) } Rationale \u00b6 Standard Body lenses work great in APIs that use a single message format (such as the ubiquitous JSON API), but there are scenarios where you may want to offer the user their choice of format (e.g. XML, YAML, JSON). While it is possible to use the ACCEPT and CONTENT_TYPE lenses to manually select the inbound and outbound body lenses, the AutoContentNegotiator can do this for you. Using Auto Content Negotiation \u00b6 The AutoContentNegotiator starts with your selection of body lenses, and wraps them together. It can then be used to: - Unmarshall Request bodies based on the CONTENT_TYPE header - Select an outbound BodyLens , based on the ACCEPT header - Add all the request and body formats to your contract If the CONTENT_TYPE and ACCEPT headers are not present, or if there is no lens for the requested format, then a default lens is used. Example \u00b6 package guide.howto.use_auto_content_negotiation import org.http4k.contract.ContractRoute import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.JacksonXml import org.http4k.format.Moshi import org.http4k.format.MoshiYaml import org.http4k.format.auto import org.http4k.lens.ContentNegotiation import java.util.UUID // define a Person to be our request/response body private class Person ( val id : UUID , val name : String ) fun main () { val jsonLens = Moshi . autoBody < Person > (). toLens () val xmlLens = JacksonXml . autoBody < Person > (). toLens () val yamlLens = MoshiYaml . autoBody < Person > (). toLens () /* * Create a content negotiator, which will handle lens selection for request and * response bodies. * Since the json lens is first, it will be the fallback when no accepted lens is found. */ val negotiator = ContentNegotiation . auto ( jsonLens , xmlLens , yamlLens ) // This sample will be used to populate our contract val samplePerson = Person ( UUID . fromString ( \"7eb9a4b7-2e50-40d7-b4d2-16ce500a0245\" ), \"john\" ) // Create a contract route that will accept a person, and echo it back. val echoPerson : ContractRoute = \"/echo\" meta { summary = \"Echo Person\" receiving ( negotiator to samplePerson ) // add request bodies to contract returning ( OK , negotiator to samplePerson ) // add response bodies to contract } bindContract POST to { req : Request -> // Unmarshall the body based on the CONTENT-TYPE header val person = negotiator ( req ) // select the appropriate outbound lens based on the ACCEPT header val outboundLens = negotiator . outbound ( req ) Response ( OK ). with ( outboundLens of person ) } // Create an HttpHandler val handler : HttpHandler = contract { routes += echoPerson renderer = OpenApi3 ( ApiInfo ( \"Content Negotiator Sample API\" , \"v1.0\" ) ) } // Send a request with a json request body, accepting a YAML response body Request ( POST , \"/echo\" ) . with ( jsonLens of samplePerson ) . header ( \"ACCEPT\" , yamlLens . contentType . toHeaderValue ()) . let ( handler ) . also { println ( it . bodyString ()) } // Send a request with an xml request body, which will return the default json response body Request ( POST , \"/echo\" ) . with ( xmlLens of samplePerson ) . let ( handler ) . also { println ( it . bodyString ()) } }","title":"Use Auto Content Negotiation"},{"location":"guide/howto/use_auto_content_negotiation/#gradle_setup","text":"Auto Content Negotiation is available in the core http4k module. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-core\" ) } But it also integrates with the contract module. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) }","title":"Gradle setup"},{"location":"guide/howto/use_auto_content_negotiation/#rationale","text":"Standard Body lenses work great in APIs that use a single message format (such as the ubiquitous JSON API), but there are scenarios where you may want to offer the user their choice of format (e.g. XML, YAML, JSON). While it is possible to use the ACCEPT and CONTENT_TYPE lenses to manually select the inbound and outbound body lenses, the AutoContentNegotiator can do this for you.","title":"Rationale"},{"location":"guide/howto/use_auto_content_negotiation/#using_auto_content_negotiation","text":"The AutoContentNegotiator starts with your selection of body lenses, and wraps them together. It can then be used to: - Unmarshall Request bodies based on the CONTENT_TYPE header - Select an outbound BodyLens , based on the ACCEPT header - Add all the request and body formats to your contract If the CONTENT_TYPE and ACCEPT headers are not present, or if there is no lens for the requested format, then a default lens is used.","title":"Using Auto Content Negotiation"},{"location":"guide/howto/use_auto_content_negotiation/#example","text":"package guide.howto.use_auto_content_negotiation import org.http4k.contract.ContractRoute import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.JacksonXml import org.http4k.format.Moshi import org.http4k.format.MoshiYaml import org.http4k.format.auto import org.http4k.lens.ContentNegotiation import java.util.UUID // define a Person to be our request/response body private class Person ( val id : UUID , val name : String ) fun main () { val jsonLens = Moshi . autoBody < Person > (). toLens () val xmlLens = JacksonXml . autoBody < Person > (). toLens () val yamlLens = MoshiYaml . autoBody < Person > (). toLens () /* * Create a content negotiator, which will handle lens selection for request and * response bodies. * Since the json lens is first, it will be the fallback when no accepted lens is found. */ val negotiator = ContentNegotiation . auto ( jsonLens , xmlLens , yamlLens ) // This sample will be used to populate our contract val samplePerson = Person ( UUID . fromString ( \"7eb9a4b7-2e50-40d7-b4d2-16ce500a0245\" ), \"john\" ) // Create a contract route that will accept a person, and echo it back. val echoPerson : ContractRoute = \"/echo\" meta { summary = \"Echo Person\" receiving ( negotiator to samplePerson ) // add request bodies to contract returning ( OK , negotiator to samplePerson ) // add response bodies to contract } bindContract POST to { req : Request -> // Unmarshall the body based on the CONTENT-TYPE header val person = negotiator ( req ) // select the appropriate outbound lens based on the ACCEPT header val outboundLens = negotiator . outbound ( req ) Response ( OK ). with ( outboundLens of person ) } // Create an HttpHandler val handler : HttpHandler = contract { routes += echoPerson renderer = OpenApi3 ( ApiInfo ( \"Content Negotiator Sample API\" , \"v1.0\" ) ) } // Send a request with a json request body, accepting a YAML response body Request ( POST , \"/echo\" ) . with ( jsonLens of samplePerson ) . header ( \"ACCEPT\" , yamlLens . contentType . toHeaderValue ()) . let ( handler ) . also { println ( it . bodyString ()) } // Send a request with an xml request body, which will return the default json response body Request ( POST , \"/echo\" ) . with ( xmlLens of samplePerson ) . let ( handler ) . also { println ( it . bodyString ()) } }","title":"Example"},{"location":"guide/howto/use_html_forms/","text":"HTML form support is provided on 2 levels: Through the use of form() extension methods on Request to get/set String values. Using the Lens system, which adds the facility to define form fields in a typesafe way, and to validate form contents (in either a strict (400) or \"feedback\" mode). Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } Standard (non-typesafe) API \u00b6 package guide.howto.use_html_forms import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.body.form import org.http4k.core.getFirst import org.http4k.core.toParametersMap import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull fun main () { // form(name: String, value: String?) parses the request body on each invocation val request = Request ( GET , \"/\" ). form ( \"name\" , \"rita\" ). form ( \"age\" , \"55\" ) // form(vararg formData: Pair) allows you to add multiple form fields to // the request while only parsing the request body once val allInOneRequest = Request ( GET , \"/\" ). form ( \"name\" to \"rita\" , \"age\" to \"55\" ) // form(name: String) parses the request body on each invocation assertEquals ( \"rita\" , request . form ( \"name\" )) assertEquals ( \"55\" , request . form ( \"age\" )) assertNull ( request . form ( \"height\" )) assertEquals ( \"rita\" , allInOneRequest . form ( \"name\" )) assertEquals ( \"55\" , allInOneRequest . form ( \"age\" )) assertNull ( allInOneRequest . form ( \"height\" )) // toParametersMap() gives form as map val parameters : Map < String , List < String? >> = request . form (). toParametersMap () assertEquals ( \"rita\" , parameters . getFirst ( \"name\" )) assertEquals ( listOf ( \"55\" ), parameters [ \"age\" ] ) assertNull ( parameters [ \"height\" ] ) } Lens (typesafe, validating) API \u00b6 package guide.howto.use_html_forms import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.lens.FormField import org.http4k.lens.Header import org.http4k.lens.LensFailure import org.http4k.lens.Validator import org.http4k.lens.WebForm import org.http4k.lens.int import org.http4k.lens.webForm data class Name ( val value : String ) fun main () { // define fields using the standard lens syntax val ageField = FormField . int (). required ( \"age\" ) val nameField = FormField . map ( :: Name , Name :: value ). optional ( \"name\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . webForm ( Validator . Strict , nameField , ageField ). toLens () val feedbackFormBody = Body . webForm ( Validator . Feedback , nameField , ageField ). toLens () val invalidRequest = Request ( GET , \"/\" ) . with ( Header . CONTENT_TYPE of ContentType . APPLICATION_FORM_URLENCODED ) // the \"strict\" form rejects (throws a LensFailure) because \"age\" is required try { strictFormBody ( invalidRequest ) } catch ( e : LensFailure ) { println ( e . message ) } // the \"feedback\" form doesn't throw, but collects errors to be reported later val invalidForm = feedbackFormBody ( invalidRequest ) println ( invalidForm . errors ) // creating valid form using \"with()\" and setting it onto the request val webForm = WebForm (). with ( ageField of 55 , nameField of Name ( \"rita\" )) val validRequest = Request ( GET , \"/\" ). with ( strictFormBody of webForm ) // to extract the contents, we first extract the form and then extract the fields from it // using the lenses val validForm = strictFormBody ( validRequest ) val age = ageField ( validForm ) println ( age ) }","title":"Use HTML forms"},{"location":"guide/howto/use_html_forms/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Gradle setup"},{"location":"guide/howto/use_html_forms/#standard_non-typesafe_api","text":"package guide.howto.use_html_forms import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.body.form import org.http4k.core.getFirst import org.http4k.core.toParametersMap import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertNull fun main () { // form(name: String, value: String?) parses the request body on each invocation val request = Request ( GET , \"/\" ). form ( \"name\" , \"rita\" ). form ( \"age\" , \"55\" ) // form(vararg formData: Pair) allows you to add multiple form fields to // the request while only parsing the request body once val allInOneRequest = Request ( GET , \"/\" ). form ( \"name\" to \"rita\" , \"age\" to \"55\" ) // form(name: String) parses the request body on each invocation assertEquals ( \"rita\" , request . form ( \"name\" )) assertEquals ( \"55\" , request . form ( \"age\" )) assertNull ( request . form ( \"height\" )) assertEquals ( \"rita\" , allInOneRequest . form ( \"name\" )) assertEquals ( \"55\" , allInOneRequest . form ( \"age\" )) assertNull ( allInOneRequest . form ( \"height\" )) // toParametersMap() gives form as map val parameters : Map < String , List < String? >> = request . form (). toParametersMap () assertEquals ( \"rita\" , parameters . getFirst ( \"name\" )) assertEquals ( listOf ( \"55\" ), parameters [ \"age\" ] ) assertNull ( parameters [ \"height\" ] ) }","title":"Standard (non-typesafe) API"},{"location":"guide/howto/use_html_forms/#lens_typesafe_validating_api","text":"package guide.howto.use_html_forms import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.lens.FormField import org.http4k.lens.Header import org.http4k.lens.LensFailure import org.http4k.lens.Validator import org.http4k.lens.WebForm import org.http4k.lens.int import org.http4k.lens.webForm data class Name ( val value : String ) fun main () { // define fields using the standard lens syntax val ageField = FormField . int (). required ( \"age\" ) val nameField = FormField . map ( :: Name , Name :: value ). optional ( \"name\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . webForm ( Validator . Strict , nameField , ageField ). toLens () val feedbackFormBody = Body . webForm ( Validator . Feedback , nameField , ageField ). toLens () val invalidRequest = Request ( GET , \"/\" ) . with ( Header . CONTENT_TYPE of ContentType . APPLICATION_FORM_URLENCODED ) // the \"strict\" form rejects (throws a LensFailure) because \"age\" is required try { strictFormBody ( invalidRequest ) } catch ( e : LensFailure ) { println ( e . message ) } // the \"feedback\" form doesn't throw, but collects errors to be reported later val invalidForm = feedbackFormBody ( invalidRequest ) println ( invalidForm . errors ) // creating valid form using \"with()\" and setting it onto the request val webForm = WebForm (). with ( ageField of 55 , nameField of Name ( \"rita\" )) val validRequest = Request ( GET , \"/\" ). with ( strictFormBody of webForm ) // to extract the contents, we first extract the form and then extract the fields from it // using the lenses val validForm = strictFormBody ( validRequest ) val age = ageField ( validForm ) println ( age ) }","title":"Lens (typesafe, validating) API"},{"location":"guide/howto/use_multipart_forms/","text":"Multipart form support is provided on 2 levels: Through the creation of a MultipartFormBody which can be set on a Request . Using the Lens system, which adds the facility to define form fields in a typesafe way, and to validate form contents (in either a strict (400) or \"feedback\" mode). Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-multipart\" ) } Standard (non-typesafe) API \u00b6 package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartFormBody import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // extract the body from the request and then the fields/files from it val server = { r : Request -> val receivedForm = MultipartFormBody . from ( r ) println ( receivedForm . fieldValues ( \"field\" )) println ( receivedForm . field ( \"field2\" )) println ( receivedForm . files ( \"file\" )) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () // add fields and files to the multipart form body val body = MultipartFormBody () . plus ( \"field\" to \"my-value\" ) . plus ( \"field2\" to MultipartFormField ( \"my-value2\" , listOf ( \"my-header\" to \"my-value\" ))) . plus ( \"file\" to MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) // we need to set both the body AND the correct content type header on the the request val request = Request ( POST , \"http://localhost:8000\" ) . header ( \"content-type\" , \"multipart/form-data; boundary= ${ body . boundary } \" ) . body ( body ) println ( ApacheClient ()( request )) server . stop () } Lens (typesafe, validating) API - reads ALL contents onto disk/memory \u00b6 package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.server.SunHttp import org.http4k.server.asServer data class Name ( val value : String ) fun main () { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: Name , Name :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val server = ServerFilters . CatchAll (). then { r : Request -> // to extract the contents, we first extract the form and then extract the fields from // it using the lenses // NOTE: we are \"using\" the form body here because we want to close the underlying // file streams strictFormBody ( r ). use { println ( nameField ( it )) println ( imageFile ( it )) } Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () // creating valid form using \"with()\" and setting it onto the request. The content type // and boundary are taken care of automatically val multipartform = MultipartForm (). with ( nameField of Name ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) val validRequest = Request ( POST , \"http://localhost:8000\" ) . with ( strictFormBody of multipartform ) println ( ApacheClient ()( validRequest )) server . stop () } Streaming - iterate over Multiparts \u00b6 package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartEntity import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.multipartIterator import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val server = ServerFilters . CatchAll (). then { r : Request -> // here we are iterating over the multiparts as we read them out of the input val fields = r . multipartIterator (). asSequence () . fold ( emptyList < MultipartEntity . Field > ()) { memo , next -> when ( next ) { is MultipartEntity . File -> { // do something with the file right here... // like stream it to another server memo } is MultipartEntity . Field -> memo . plus ( next ) } } println ( fields ) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( buildMultipartRequest ())) server . stop () } private fun buildMultipartRequest (): Request { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: Name , Name :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val multipartform = MultipartForm (). with ( nameField of Name ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) return Request ( POST , \"http://localhost:8000\" ) . with ( strictFormBody of multipartform ) } Processing Files with a Filter and convert to standard form \u00b6 package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartEntity import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ProcessFiles import org.http4k.filter.ServerFilters import org.http4k.lens.FormField import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.lens.webForm import org.http4k.server.SunHttp import org.http4k.server.asServer data class AName ( val value : String ) fun main () { val server = ServerFilters . ProcessFiles { multipartFile : MultipartEntity . File -> // do something with the file right here... like stream it to another server and // return the guide.reference println ( String ( multipartFile . file . content . readBytes ())) multipartFile . file . filename }. then { req : Request -> // this is the web-form definition - it is DIFFERENT to the multipart form definition, // because the fields and content-type have been replaced in the ProcessFiles filter val nameField = FormField . map ( :: AName , AName :: value ). required ( \"name\" ) val imageFile = FormField . optional ( \"image\" ) val body = Body . webForm ( Validator . Strict , nameField , imageFile ). toLens () println ( body ( req )) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( buildValidMultipartRequest ())) server . stop () } private fun buildValidMultipartRequest (): Request { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: AName , AName :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val multipartform = MultipartForm (). with ( nameField of AName ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) return Request ( POST , \"http://localhost:8000\" ). with ( strictFormBody of multipartform ) } Multipart combined with typesafe contract (OpenApi) \u00b6 package guide.howto.use_multipart_forms import org.http4k.contract.PreFlightExtraction import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator.Strict import org.http4k.lens.instant import org.http4k.lens.multipartForm import org.http4k.server.ApacheServer import org.http4k.server.asServer fun main () { val documentPart = MultipartFormFile . required ( \"document\" ) val ownerPart = MultipartFormField . string (). required ( \"owner\" ) val signaturePart = MultipartFormField . string (). instant (). required ( \"signedAt\" ) val formLens = Body . multipartForm ( Strict , documentPart , ownerPart , signaturePart ). toLens () val handler = contract { renderer = OpenApi3 ( ApiInfo ( \"My great API\" , \"v1.0\" ), Jackson ) descriptionPath = \"/openapi.json\" routes += \"/api/document-upload\" meta { summary = \"Uploads a document including the owner name and when it was signed\" // required to avoid reading the multipart stream twice! preFlightExtraction = PreFlightExtraction . IgnoreBody receiving ( formLens ) returning ( OK ) } bindContract POST to { req -> formLens ( req ). use { val doc = documentPart ( it ) val owner = ownerPart ( it ) val signatureDate = signaturePart ( it ) //process file... Response ( OK ). body ( \" ${ doc . filename } by $ owner , signed at $ signatureDate \" ) } } } /** * example request: * curl -v -H 'Content-Type: multipart/form-data' \\ * -F owner=\"John Doe\" \\ * -F signedAt=\"2011-12-03T10:15:30Z\" \\ * -F document=@README.md \\ * http://localhost:8081/api/document-upload */ handler . asServer ( ApacheServer ( 8081 )). start () }","title":"Use Multipart forms"},{"location":"guide/howto/use_multipart_forms/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-multipart\" ) }","title":"Gradle setup"},{"location":"guide/howto/use_multipart_forms/#standard_non-typesafe_api","text":"package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartFormBody import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // extract the body from the request and then the fields/files from it val server = { r : Request -> val receivedForm = MultipartFormBody . from ( r ) println ( receivedForm . fieldValues ( \"field\" )) println ( receivedForm . field ( \"field2\" )) println ( receivedForm . files ( \"file\" )) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () // add fields and files to the multipart form body val body = MultipartFormBody () . plus ( \"field\" to \"my-value\" ) . plus ( \"field2\" to MultipartFormField ( \"my-value2\" , listOf ( \"my-header\" to \"my-value\" ))) . plus ( \"file\" to MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) // we need to set both the body AND the correct content type header on the the request val request = Request ( POST , \"http://localhost:8000\" ) . header ( \"content-type\" , \"multipart/form-data; boundary= ${ body . boundary } \" ) . body ( body ) println ( ApacheClient ()( request )) server . stop () }","title":"Standard (non-typesafe) API"},{"location":"guide/howto/use_multipart_forms/#lens_typesafe_validating_api_-_reads_all_contents_onto_diskmemory","text":"package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.server.SunHttp import org.http4k.server.asServer data class Name ( val value : String ) fun main () { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: Name , Name :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val server = ServerFilters . CatchAll (). then { r : Request -> // to extract the contents, we first extract the form and then extract the fields from // it using the lenses // NOTE: we are \"using\" the form body here because we want to close the underlying // file streams strictFormBody ( r ). use { println ( nameField ( it )) println ( imageFile ( it )) } Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () // creating valid form using \"with()\" and setting it onto the request. The content type // and boundary are taken care of automatically val multipartform = MultipartForm (). with ( nameField of Name ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) val validRequest = Request ( POST , \"http://localhost:8000\" ) . with ( strictFormBody of multipartform ) println ( ApacheClient ()( validRequest )) server . stop () }","title":"Lens (typesafe, validating) API - reads ALL contents onto disk/memory"},{"location":"guide/howto/use_multipart_forms/#streaming_-_iterate_over_multiparts","text":"package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartEntity import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.multipartIterator import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val server = ServerFilters . CatchAll (). then { r : Request -> // here we are iterating over the multiparts as we read them out of the input val fields = r . multipartIterator (). asSequence () . fold ( emptyList < MultipartEntity . Field > ()) { memo , next -> when ( next ) { is MultipartEntity . File -> { // do something with the file right here... // like stream it to another server memo } is MultipartEntity . Field -> memo . plus ( next ) } } println ( fields ) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( buildMultipartRequest ())) server . stop () } private fun buildMultipartRequest (): Request { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: Name , Name :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val multipartform = MultipartForm (). with ( nameField of Name ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) return Request ( POST , \"http://localhost:8000\" ) . with ( strictFormBody of multipartform ) }","title":"Streaming - iterate over Multiparts"},{"location":"guide/howto/use_multipart_forms/#processing_files_with_a_filter_and_convert_to_standard_form","text":"package guide.howto.use_multipart_forms import org.http4k.client.ApacheClient import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.MultipartEntity import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ProcessFiles import org.http4k.filter.ServerFilters import org.http4k.lens.FormField import org.http4k.lens.MultipartForm import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator import org.http4k.lens.multipartForm import org.http4k.lens.webForm import org.http4k.server.SunHttp import org.http4k.server.asServer data class AName ( val value : String ) fun main () { val server = ServerFilters . ProcessFiles { multipartFile : MultipartEntity . File -> // do something with the file right here... like stream it to another server and // return the guide.reference println ( String ( multipartFile . file . content . readBytes ())) multipartFile . file . filename }. then { req : Request -> // this is the web-form definition - it is DIFFERENT to the multipart form definition, // because the fields and content-type have been replaced in the ProcessFiles filter val nameField = FormField . map ( :: AName , AName :: value ). required ( \"name\" ) val imageFile = FormField . optional ( \"image\" ) val body = Body . webForm ( Validator . Strict , nameField , imageFile ). toLens () println ( body ( req )) Response ( OK ) }. asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( buildValidMultipartRequest ())) server . stop () } private fun buildValidMultipartRequest (): Request { // define fields using the standard lens syntax val nameField = MultipartFormField . string (). map ( :: AName , AName :: value ). required ( \"name\" ) val imageFile = MultipartFormFile . optional ( \"image\" ) // add fields to a form definition, along with a validator val strictFormBody = Body . multipartForm ( Validator . Strict , nameField , imageFile , diskThreshold = 5 ). toLens () val multipartform = MultipartForm (). with ( nameField of AName ( \"rita\" ), imageFile of MultipartFormFile ( \"image.txt\" , ContentType . OCTET_STREAM , \"somebinarycontent\" . byteInputStream () ) ) return Request ( POST , \"http://localhost:8000\" ). with ( strictFormBody of multipartform ) }","title":"Processing Files with a Filter and convert to standard form"},{"location":"guide/howto/use_multipart_forms/#multipart_combined_with_typesafe_contract_openapi","text":"package guide.howto.use_multipart_forms import org.http4k.contract.PreFlightExtraction import org.http4k.contract.contract import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson import org.http4k.lens.MultipartFormField import org.http4k.lens.MultipartFormFile import org.http4k.lens.Validator.Strict import org.http4k.lens.instant import org.http4k.lens.multipartForm import org.http4k.server.ApacheServer import org.http4k.server.asServer fun main () { val documentPart = MultipartFormFile . required ( \"document\" ) val ownerPart = MultipartFormField . string (). required ( \"owner\" ) val signaturePart = MultipartFormField . string (). instant (). required ( \"signedAt\" ) val formLens = Body . multipartForm ( Strict , documentPart , ownerPart , signaturePart ). toLens () val handler = contract { renderer = OpenApi3 ( ApiInfo ( \"My great API\" , \"v1.0\" ), Jackson ) descriptionPath = \"/openapi.json\" routes += \"/api/document-upload\" meta { summary = \"Uploads a document including the owner name and when it was signed\" // required to avoid reading the multipart stream twice! preFlightExtraction = PreFlightExtraction . IgnoreBody receiving ( formLens ) returning ( OK ) } bindContract POST to { req -> formLens ( req ). use { val doc = documentPart ( it ) val owner = ownerPart ( it ) val signatureDate = signaturePart ( it ) //process file... Response ( OK ). body ( \" ${ doc . filename } by $ owner , signed at $ signatureDate \" ) } } } /** * example request: * curl -v -H 'Content-Type: multipart/form-data' \\ * -F owner=\"John Doe\" \\ * -F signedAt=\"2011-12-03T10:15:30Z\" \\ * -F document=@README.md \\ * http://localhost:8081/api/document-upload */ handler . asServer ( ApacheServer ( 8081 )). start () }","title":"Multipart combined with typesafe contract (OpenApi)"},{"location":"guide/howto/write_different_test_types/","text":"This example shows the various styles of testing endpoints, and requires both the http4k-core and http4k-testing-hamkrest modules: Code \u00b6 package guide.howto.write_different_test_types import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class AnswerRecorder ( private val httpClient : HttpHandler ) : ( Int ) -> Unit { override fun invoke ( answer : Int ) { httpClient ( Request ( POST , \"/ $ answer \" )) } } fun myMathsEndpoint ( fn : ( Int , Int ) -> Int , recorder : ( Int ) -> Unit ): HttpHandler = { req -> val answer = fn ( req . query ( \"first\" ) !! . toInt (), req . query ( \"second\" ) !! . toInt ()) recorder ( answer ) Response ( OK ). body ( \"the answer is $ answer \" ) } class EndpointUnitTest { @Test fun `adds numbers and records answer` () { var answer : Int? = null val unit = myMathsEndpoint ({ first , second -> first + second }, { answer = it }) val response = unit ( Request ( GET , \"/\" ). query ( \"first\" , \"123\" ). query ( \"second\" , \"456\" )) assertThat ( answer , equalTo ( 579 )) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) } } fun MyMathsApp ( recorderHttp : HttpHandler ) = ServerFilters . CatchAll (). then ( routes ( \"/add\" bind GET to myMathsEndpoint ( { first , second -> first + second }, AnswerRecorder ( recorderHttp ) ) ) ) class FakeRecorderHttp : HttpHandler { val calls = mutableListOf < Int > () private val app = routes ( \"/{answer}\" bind POST to { request -> calls . add ( request . path ( \"answer\" ) !! . toInt () ); Response ( OK ) } ) override fun invoke ( request : Request ): Response = app ( request ) } class FunctionalTest { private val recorderHttp = FakeRecorderHttp () private val app = MyMathsApp ( recorderHttp ) @Test fun `adds numbers` () { val response = app ( Request ( GET , \"/add\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) assertThat ( recorderHttp . calls , equalTo ( listOf ( 579 ))) } @Test fun `not found` () { val response = app ( Request ( GET , \"/nothing\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) assertThat ( response , hasStatus ( NOT_FOUND )) } } fun MyMathServer ( port : Int , recorderUri : Uri ): Http4kServer { val recorderHttp = SetHostFrom ( recorderUri ). then ( OkHttp ()) return MyMathsApp ( recorderHttp ). asServer ( Jetty ( port )) } class EndToEndTest { private val client = OkHttp () private val recorderHttp = FakeRecorderHttp () private val recorder = recorderHttp . asServer ( Jetty ( 8001 )) private val server = MyMathServer ( 8000 , Uri . of ( \"http://localhost:8001\" )) @BeforeEach fun setup () { recorder . start () server . start () } @AfterEach fun teardown () { server . stop () recorder . stop () } @Test fun `adds numbers` () { val response = client ( Request ( GET , \"http://localhost:8000/add\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) println ( response ) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) assertThat ( recorderHttp . calls , equalTo ( listOf ( 579 ))) } }","title":"Basic: Different test-types"},{"location":"guide/howto/write_different_test_types/#code","text":"package guide.howto.write_different_test_types import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class AnswerRecorder ( private val httpClient : HttpHandler ) : ( Int ) -> Unit { override fun invoke ( answer : Int ) { httpClient ( Request ( POST , \"/ $ answer \" )) } } fun myMathsEndpoint ( fn : ( Int , Int ) -> Int , recorder : ( Int ) -> Unit ): HttpHandler = { req -> val answer = fn ( req . query ( \"first\" ) !! . toInt (), req . query ( \"second\" ) !! . toInt ()) recorder ( answer ) Response ( OK ). body ( \"the answer is $ answer \" ) } class EndpointUnitTest { @Test fun `adds numbers and records answer` () { var answer : Int? = null val unit = myMathsEndpoint ({ first , second -> first + second }, { answer = it }) val response = unit ( Request ( GET , \"/\" ). query ( \"first\" , \"123\" ). query ( \"second\" , \"456\" )) assertThat ( answer , equalTo ( 579 )) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) } } fun MyMathsApp ( recorderHttp : HttpHandler ) = ServerFilters . CatchAll (). then ( routes ( \"/add\" bind GET to myMathsEndpoint ( { first , second -> first + second }, AnswerRecorder ( recorderHttp ) ) ) ) class FakeRecorderHttp : HttpHandler { val calls = mutableListOf < Int > () private val app = routes ( \"/{answer}\" bind POST to { request -> calls . add ( request . path ( \"answer\" ) !! . toInt () ); Response ( OK ) } ) override fun invoke ( request : Request ): Response = app ( request ) } class FunctionalTest { private val recorderHttp = FakeRecorderHttp () private val app = MyMathsApp ( recorderHttp ) @Test fun `adds numbers` () { val response = app ( Request ( GET , \"/add\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) assertThat ( recorderHttp . calls , equalTo ( listOf ( 579 ))) } @Test fun `not found` () { val response = app ( Request ( GET , \"/nothing\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) assertThat ( response , hasStatus ( NOT_FOUND )) } } fun MyMathServer ( port : Int , recorderUri : Uri ): Http4kServer { val recorderHttp = SetHostFrom ( recorderUri ). then ( OkHttp ()) return MyMathsApp ( recorderHttp ). asServer ( Jetty ( port )) } class EndToEndTest { private val client = OkHttp () private val recorderHttp = FakeRecorderHttp () private val recorder = recorderHttp . asServer ( Jetty ( 8001 )) private val server = MyMathServer ( 8000 , Uri . of ( \"http://localhost:8001\" )) @BeforeEach fun setup () { recorder . start () server . start () } @AfterEach fun teardown () { server . stop () recorder . stop () } @Test fun `adds numbers` () { val response = client ( Request ( GET , \"http://localhost:8000/add\" ) . query ( \"first\" , \"123\" ) . query ( \"second\" , \"456\" ) ) println ( response ) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"the answer is 579\" ))) assertThat ( recorderHttp . calls , equalTo ( listOf ( 579 ))) } }","title":"Code"},{"location":"guide/reference/","text":"http4k Modules overview \u00b6 Core functionality \u00b6 Core OpenAPI Contracts Multipart Forms GraphQL HTTP Server Backends \u00b6 Apache 4 & 5 Java-WebSocket Jetty & JettyLoom Helidon (Loom) Ktor CIO & Netty Netty Ratpack SunHttp & SunHttpLoom Undertow HTTP Serverless Backends \u00b6 Alibaba Function Compute Apache OpenWhisk AWS Lambda Azure Functions Google Cloud Functions Tencent Serverless Cloud Functions HTTP Clients \u00b6 Apache4 Sync & Async Apache5 Sync & Async Java Fuel Helidon Jetty OkHttp Websocket Messaging formats \u00b6 Argo DataFrame Gson Gson XML Jackson Jackson XML Jackson YAML JSON RPC Klaxon KondorJson KotlinX Serialisation Moshi Templating libraries \u00b6 Freemarker Handlebars Pug4j Rocker Pebble Thymeleaf Cloud-Native/Observability/Misc \u00b6 AWS Typesafe Configuration Cloud Native extensions Cloud Events Digest Auth OAuth Micrometer OpenTelemetry Resilience4J FailSafe htmx Webhooks Testing \u00b6 Approval Testing Chaos Testing Hamkrest Kotest Playwright Strikt Servirtium WebDriver Tracer Bullet","title":"Overview"},{"location":"guide/reference/#http4k_modules_overview","text":"","title":"http4k Modules overview"},{"location":"guide/reference/#core_functionality","text":"Core OpenAPI Contracts Multipart Forms GraphQL","title":"Core functionality"},{"location":"guide/reference/#http_server_backends","text":"Apache 4 & 5 Java-WebSocket Jetty & JettyLoom Helidon (Loom) Ktor CIO & Netty Netty Ratpack SunHttp & SunHttpLoom Undertow","title":"HTTP Server Backends"},{"location":"guide/reference/#http_serverless_backends","text":"Alibaba Function Compute Apache OpenWhisk AWS Lambda Azure Functions Google Cloud Functions Tencent Serverless Cloud Functions","title":"HTTP Serverless Backends"},{"location":"guide/reference/#http_clients","text":"Apache4 Sync & Async Apache5 Sync & Async Java Fuel Helidon Jetty OkHttp Websocket","title":"HTTP Clients"},{"location":"guide/reference/#messaging_formats","text":"Argo DataFrame Gson Gson XML Jackson Jackson XML Jackson YAML JSON RPC Klaxon KondorJson KotlinX Serialisation Moshi","title":"Messaging formats"},{"location":"guide/reference/#templating_libraries","text":"Freemarker Handlebars Pug4j Rocker Pebble Thymeleaf","title":"Templating libraries"},{"location":"guide/reference/#cloud-nativeobservabilitymisc","text":"AWS Typesafe Configuration Cloud Native extensions Cloud Events Digest Auth OAuth Micrometer OpenTelemetry Resilience4J FailSafe htmx Webhooks","title":"Cloud-Native/Observability/Misc"},{"location":"guide/reference/#testing","text":"Approval Testing Chaos Testing Hamkrest Kotest Playwright Strikt Servirtium WebDriver Tracer Bullet","title":"Testing"},{"location":"guide/reference/api/","text":"","title":"http4k API"},{"location":"guide/reference/approvaltests/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-approval\" ) } About \u00b6 Approval testing is a form of testing which allows the expected output of a test to be specified in a non-code but still source-controlled format, such as a text file. This is a powerful alternative to traditional assertion-based tests for a number of reasons: It is often inconvenient and/or error prone to attempt to write assertions to cover the entirety of test output - examples of this include JSON, HTML or XML documents. Output may not always be in a format that can be created easily in a test. In case of a mismatch, output can be more efficiently diagnosed by the human eye. The output of a test may change significantly in a short period (this is especially true for HTML content), but we also want to tightly control the contract. The general idea for implementing this style of testing in http4k is based on the excellent okeydoke library, and is centered around the idea of comparing the output of an HTTP operation - this is generally the Response content, but it can also be the Request if we are interested in testing construction of request content. For each test-case, a named .approved file is committed (under the src/test/resources folder), against which the test output can be compared by an Approver object injected into the test method. In case of a mismatch, an equivalent .actual file is written. This file can then be verified and if ok, renamed to become the approved file. To make this operation easier in the IDE, we recommend the usage of the IntelliJ OkeyDoke plugin which adds a mouse and keyboard shortcut to rename the file. The http4k-testing-approval module implements this functionality as a JUnit5 extension that will inject the Approver automatically into test methods. Standard Approval tests \u00b6 By using the ApprovalTest extension, an instance of an Approver is injected into each test. Code \u00b6 package guide.reference.approvaltests import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasStatus import org.http4k.testing.ApprovalTest import org.http4k.testing.Approver import org.http4k.testing.assertApproved import org.http4k.testing.hasApprovedContent import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith ( ApprovalTest :: class ) class ExampleApprovalTest { private val app : HttpHandler = { Response ( OK ). body ( \"hello world\" ) } @Test fun `check response content` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" ))) } @Test fun `check response content with expected status` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" )), OK ) } @Test fun `check request content` ( approver : Approver ) { approver . assertApproved ( Request ( GET , \"/url\" ). body ( \"foobar\" )) } @Test fun `combine approval with hamkrest matcher` ( approver : Approver ) { assertThat ( app ( Request ( GET , \"/url\" )), hasStatus ( OK ). and ( approver . hasApprovedContent ())) } } Content-type specific Approval tests \u00b6 Because so many APIs are based around messages with a particular content type, the module also provides Junit 5 extensions that will: Check for the presence of the a particular content-type on the HttpMessage under test and fail if it is not valid. Validate that the HttpMessage actually contains valid content for the content type. Format and compare the approval output as pretty-printed version. Note that by default the http4k format modules use compact printing to conserve message space. The module also provides the following built-in extensions: HtmlApprovalTest JsonApprovalTest XmlApprovalTest Code \u00b6 package guide.reference.approvaltests import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.ContentType.Companion.APPLICATION_JSON import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.hamkrest.hasStatus import org.http4k.lens.Header.CONTENT_TYPE import org.http4k.testing.Approver import org.http4k.testing.JsonApprovalTest import org.http4k.testing.assertApproved import org.http4k.testing.hasApprovedContent import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith ( JsonApprovalTest :: class ) class ExampleJsonApprovalTest { private val app : HttpHandler = { Response ( OK ) . with ( CONTENT_TYPE of APPLICATION_JSON ) . body ( \"\"\"{\"message\":\"value\"}\"\"\" ) } @Test fun `check response content` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" ))) } @Test fun `check response content with expected status` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" )), OK ) } @Test fun `check request content` ( approver : Approver ) { approver . assertApproved ( Request ( GET , \"/url\" ). with ( CONTENT_TYPE of APPLICATION_JSON ) . body ( \"\"\"{\"message\":\"value\"}\"\"\" ) ) } @Test fun `combine approval with hamkrest matcher` ( approver : Approver ) { assertThat ( app ( Request ( GET , \"/url\" )), hasStatus ( OK ). and ( approver . hasApprovedContent ())) } } Implementing custom JUnit Extensions \u00b6 As with the rest of http4k, a base implementation, BaseApprovalTest of the Junit5 Extension is provided, allowing API users to implement custom approval schemes or non-FS based approaches for storing the approval files.","title":"Approval Testing"},{"location":"guide/reference/approvaltests/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-approval\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/approvaltests/#about","text":"Approval testing is a form of testing which allows the expected output of a test to be specified in a non-code but still source-controlled format, such as a text file. This is a powerful alternative to traditional assertion-based tests for a number of reasons: It is often inconvenient and/or error prone to attempt to write assertions to cover the entirety of test output - examples of this include JSON, HTML or XML documents. Output may not always be in a format that can be created easily in a test. In case of a mismatch, output can be more efficiently diagnosed by the human eye. The output of a test may change significantly in a short period (this is especially true for HTML content), but we also want to tightly control the contract. The general idea for implementing this style of testing in http4k is based on the excellent okeydoke library, and is centered around the idea of comparing the output of an HTTP operation - this is generally the Response content, but it can also be the Request if we are interested in testing construction of request content. For each test-case, a named .approved file is committed (under the src/test/resources folder), against which the test output can be compared by an Approver object injected into the test method. In case of a mismatch, an equivalent .actual file is written. This file can then be verified and if ok, renamed to become the approved file. To make this operation easier in the IDE, we recommend the usage of the IntelliJ OkeyDoke plugin which adds a mouse and keyboard shortcut to rename the file. The http4k-testing-approval module implements this functionality as a JUnit5 extension that will inject the Approver automatically into test methods.","title":"About"},{"location":"guide/reference/approvaltests/#standard_approval_tests","text":"By using the ApprovalTest extension, an instance of an Approver is injected into each test.","title":"Standard Approval tests"},{"location":"guide/reference/approvaltests/#code","text":"package guide.reference.approvaltests import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasStatus import org.http4k.testing.ApprovalTest import org.http4k.testing.Approver import org.http4k.testing.assertApproved import org.http4k.testing.hasApprovedContent import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith ( ApprovalTest :: class ) class ExampleApprovalTest { private val app : HttpHandler = { Response ( OK ). body ( \"hello world\" ) } @Test fun `check response content` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" ))) } @Test fun `check response content with expected status` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" )), OK ) } @Test fun `check request content` ( approver : Approver ) { approver . assertApproved ( Request ( GET , \"/url\" ). body ( \"foobar\" )) } @Test fun `combine approval with hamkrest matcher` ( approver : Approver ) { assertThat ( app ( Request ( GET , \"/url\" )), hasStatus ( OK ). and ( approver . hasApprovedContent ())) } }","title":"Code"},{"location":"guide/reference/approvaltests/#content-type_specific_approval_tests","text":"Because so many APIs are based around messages with a particular content type, the module also provides Junit 5 extensions that will: Check for the presence of the a particular content-type on the HttpMessage under test and fail if it is not valid. Validate that the HttpMessage actually contains valid content for the content type. Format and compare the approval output as pretty-printed version. Note that by default the http4k format modules use compact printing to conserve message space. The module also provides the following built-in extensions: HtmlApprovalTest JsonApprovalTest XmlApprovalTest","title":"Content-type specific Approval tests"},{"location":"guide/reference/approvaltests/#code_1","text":"package guide.reference.approvaltests import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.ContentType.Companion.APPLICATION_JSON import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.hamkrest.hasStatus import org.http4k.lens.Header.CONTENT_TYPE import org.http4k.testing.Approver import org.http4k.testing.JsonApprovalTest import org.http4k.testing.assertApproved import org.http4k.testing.hasApprovedContent import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith @ExtendWith ( JsonApprovalTest :: class ) class ExampleJsonApprovalTest { private val app : HttpHandler = { Response ( OK ) . with ( CONTENT_TYPE of APPLICATION_JSON ) . body ( \"\"\"{\"message\":\"value\"}\"\"\" ) } @Test fun `check response content` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" ))) } @Test fun `check response content with expected status` ( approver : Approver ) { approver . assertApproved ( app ( Request ( GET , \"/url\" )), OK ) } @Test fun `check request content` ( approver : Approver ) { approver . assertApproved ( Request ( GET , \"/url\" ). with ( CONTENT_TYPE of APPLICATION_JSON ) . body ( \"\"\"{\"message\":\"value\"}\"\"\" ) ) } @Test fun `combine approval with hamkrest matcher` ( approver : Approver ) { assertThat ( app ( Request ( GET , \"/url\" )), hasStatus ( OK ). and ( approver . hasApprovedContent ())) } }","title":"Code"},{"location":"guide/reference/approvaltests/#implementing_custom_junit_extensions","text":"As with the rest of http4k, a base implementation, BaseApprovalTest of the Junit5 Extension is provided, allowing API users to implement custom approval schemes or non-FS based approaches for storing the approval files.","title":"Implementing custom JUnit Extensions"},{"location":"guide/reference/aws/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-aws\" ) } About \u00b6 This module provides 2 things: a http4k compatible SdkHttpClient and a super-simple AWS request signing functionality for talking to AWS services. With the SdkHttpClient you can use the standard Amazon SDKs libraries by plugging in a standard HttpHandler . This simplifies fault testing and means that you can print out the exact traffic which is going to AWS - which is brilliant for both debugging and writing Fakes. :) Code \u00b6 package guide.reference.aws import org.http4k.aws.AwsSdkClient import org.http4k.client.OkHttp import org.http4k.core.then import org.http4k.filter.DebuggingFilters import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.regions.Region.EU_WEST_1 import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.CreateBucketRequest fun main () { val fakeS3 = DebuggingFilters . PrintRequestAndResponse (). then ( OkHttp ()) val s3 = S3Client . builder () . region ( EU_WEST_1 ) . credentialsProvider { AwsBasicCredentials . create ( \"accessKey\" , \"secret\" ) } . httpClient ( AwsSdkClient ( fakeS3 )) . build () s3 . createBucket ( CreateBucketRequest . builder (). bucket ( \"hello\" ). build ()) } With the request signing functionality, once configured with the correct keys, the various AWS services are actually really simple to integrate with. They're just RESTy-type HTTPS services - the main difficulty is that all requests need to have their contents digitally signed with the AWS credentials to be authorised. http4k provides a Filter which does this request signing process. Just decorate a standard HTTP client and then make the relevant calls: Code \u00b6 package guide.reference.aws import org.http4k.aws.AwsCredentialScope import org.http4k.aws.AwsCredentials import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Method.PUT import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.AwsAuth import org.http4k.filter.ClientFilters import java.util.UUID fun main () { val region = \"us-east-1\" val service = \"s3\" val accessKey = \"myGreatAwsAccessKey\" val secretKey = \"myGreatAwsSecretKey\" val client = ClientFilters . AwsAuth ( AwsCredentialScope ( region , service ), AwsCredentials ( accessKey , secretKey ) ) . then ( ApacheClient ()) // create a bucket val bucketName = UUID . randomUUID (). toString () val bucketUri = Uri . of ( \"https:// $ bucketName .s3.amazonaws.com/\" ) println ( client ( Request ( PUT , bucketUri ))) // get list of buckets with the new bucket in it println ( client ( Request ( GET , Uri . of ( \"https://s3.amazonaws.com/\" ))). bodyString ()) // create a key into the bucket val key = UUID . randomUUID (). toString () val keyUri = Uri . of ( \"https:// $ bucketName .s3.amazonaws.com/ $ key \" ) println ( client ( Request ( PUT , keyUri ). body ( \"some amazing content that I want stored on S3\" ))) // get the keys in the bucket println ( client ( Request ( GET , bucketUri ))) // get the contents of the key in the bucket println ( client ( Request ( GET , keyUri ))) // delete the key in the bucket println ( client ( Request ( GET , keyUri ))) // delete the bucket println ( client ( Request ( GET , bucketUri ))) }","title":"AWS"},{"location":"guide/reference/aws/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-aws\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/aws/#about","text":"This module provides 2 things: a http4k compatible SdkHttpClient and a super-simple AWS request signing functionality for talking to AWS services. With the SdkHttpClient you can use the standard Amazon SDKs libraries by plugging in a standard HttpHandler . This simplifies fault testing and means that you can print out the exact traffic which is going to AWS - which is brilliant for both debugging and writing Fakes. :)","title":"About"},{"location":"guide/reference/aws/#code","text":"package guide.reference.aws import org.http4k.aws.AwsSdkClient import org.http4k.client.OkHttp import org.http4k.core.then import org.http4k.filter.DebuggingFilters import software.amazon.awssdk.auth.credentials.AwsBasicCredentials import software.amazon.awssdk.regions.Region.EU_WEST_1 import software.amazon.awssdk.services.s3.S3Client import software.amazon.awssdk.services.s3.model.CreateBucketRequest fun main () { val fakeS3 = DebuggingFilters . PrintRequestAndResponse (). then ( OkHttp ()) val s3 = S3Client . builder () . region ( EU_WEST_1 ) . credentialsProvider { AwsBasicCredentials . create ( \"accessKey\" , \"secret\" ) } . httpClient ( AwsSdkClient ( fakeS3 )) . build () s3 . createBucket ( CreateBucketRequest . builder (). bucket ( \"hello\" ). build ()) } With the request signing functionality, once configured with the correct keys, the various AWS services are actually really simple to integrate with. They're just RESTy-type HTTPS services - the main difficulty is that all requests need to have their contents digitally signed with the AWS credentials to be authorised. http4k provides a Filter which does this request signing process. Just decorate a standard HTTP client and then make the relevant calls:","title":"Code"},{"location":"guide/reference/aws/#code_1","text":"package guide.reference.aws import org.http4k.aws.AwsCredentialScope import org.http4k.aws.AwsCredentials import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Method.PUT import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.AwsAuth import org.http4k.filter.ClientFilters import java.util.UUID fun main () { val region = \"us-east-1\" val service = \"s3\" val accessKey = \"myGreatAwsAccessKey\" val secretKey = \"myGreatAwsSecretKey\" val client = ClientFilters . AwsAuth ( AwsCredentialScope ( region , service ), AwsCredentials ( accessKey , secretKey ) ) . then ( ApacheClient ()) // create a bucket val bucketName = UUID . randomUUID (). toString () val bucketUri = Uri . of ( \"https:// $ bucketName .s3.amazonaws.com/\" ) println ( client ( Request ( PUT , bucketUri ))) // get list of buckets with the new bucket in it println ( client ( Request ( GET , Uri . of ( \"https://s3.amazonaws.com/\" ))). bodyString ()) // create a key into the bucket val key = UUID . randomUUID (). toString () val keyUri = Uri . of ( \"https:// $ bucketName .s3.amazonaws.com/ $ key \" ) println ( client ( Request ( PUT , keyUri ). body ( \"some amazing content that I want stored on S3\" ))) // get the keys in the bucket println ( client ( Request ( GET , bucketUri ))) // get the contents of the key in the bucket println ( client ( Request ( GET , keyUri ))) // delete the key in the bucket println ( client ( Request ( GET , keyUri ))) // delete the bucket println ( client ( Request ( GET , bucketUri ))) }","title":"Code"},{"location":"guide/reference/chaos/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-chaos\" ) } About \u00b6 The http4k Chaos module provides the facility to statically or dynamically inject failure modes into http4k applications, such as random HTTP failures, killing of processes, and extra latency injection. By modelling these modes, it is possible to plan for mitigation of particular scenarios on a wider scale, resulting either from failures within your system boundary, or those caused by dependent remote HTTP services. The Principles of Chaos Engineering approach was made prominent by Netflix open-sourcing the Simian Army libraries. API concepts \u00b6 To understand the API, these domain-language concepts are important, all modelled as simple Kotlin typealiases and interfaces in order that API users can create their own: Behaviours: typealias Behaviour = Filter \u00b6 A Behaviour applies the failure mode to the HTTP call. This could involve blocking a thread permanently, introducing extra latency into an HTTP service, or even causing a Stack Overflow or Killing the running process. Behaviour function Effect as JSON Latency Adds random latency to a call between the min and max durations {\"type\":\"latency\",\"min\":\"PT0.1S\",\"max\":\"PT0.3S\"} ThrowException Throws an uncaught Exception with the supplied message {\"type\":\"throw\",\"message\":\"foo\"} ReturnStatus Returns an HTTP response with the specified HTTP status code {\"type\":\"status\",\"status\":404} NoBody Completes the call normally, but strips the body content from the response {\"type\":\"body\"} EatMemory Forces an OOM exception {\"type\":\"memory\"} KillProcess Kills the Java process with a 1 error code {\"type\":\"kill\"} StackOverflow Generates a StackOverflow {\"type\":\"overflow\"} BlockThread Permanently blocks the request thread {\"type\":\"block\"} None Requests complete normally {\"type\":\"none\"} Triggers: typealias Trigger = (req: Request) -> Boolean \u00b6 A Trigger is just a predicate which determines if an HTTP call should have an Behaviour applied to it. Triggers can be stateless, based on the request content, or stateful - deadlines or countdowns. Trigger function Activation condition as JSON Deadline After an instant in time {\"type\":\"deadline\",\"endTime\":\"1970-01-01T00:00:00Z\"} Delay After a specified period (since construction) {\"type\":\"delay\",\"period\":\"PT0.1S\"} Countdown For the first n requests only {\"type\":\"countdown\",\"count\":\"1\"} Request If the request meets the criteria set out in the specification. All but method are Regex patterns, and all are optional {\"type\":\"request\",\"method\":\"get\",\"path\":\".*bob\",\"queries\":{\"query\":\".*query\"},\"headers\":{\"header\":\".*header\"},\"body\":\".*body\"} Once For the first request only {\"type\":\"once\"} PercentageBased Applies to a certain (randomly decided) percentage of requests {\"type\":\"percentage\", \"percentage\":100} Always For all requests {\"type\":\"always\"} Stages: interface Stage: (Request) -> Filter? \u00b6 A Stage provides the lifecycle for applying a behaviour, and applies until a Trigger indicates that the stage is complete. Stages can be chained with then() , or can be produced by combining a Behaviour and a Trigger using appliedWhen() . Stage function Lifecycle notes as JSON Wait Does nothing while active {\"type\":\"wait\",\"until\":} Repeat Loops through the stages and then repeats {\"type\":\"repeat\",\"stages\":[],\"until\":} (Triggered) Combines a Trigger and a Behaviour {\"type\":\"trigger\",\"behaviour\":{\"type\":\"body\"},\"trigger\":,\"until\":}} Manually injecting Chaos \u00b6 For use in automated test suites, it is simple to define the Chaos behaviour programmatically using the API and then use the ChaosEngine to add it onto an existing application. Code \u00b6 package guide.reference.chaos import org.http4k.chaos.ChaosBehaviours.ReturnStatus import org.http4k.chaos.ChaosEngine import org.http4k.chaos.ChaosStages.Wait import org.http4k.chaos.ChaosTriggers.PercentageBased import org.http4k.chaos.appliedWhen import org.http4k.chaos.then import org.http4k.chaos.until import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.server.SunHttp import org.http4k.server.asServer val client = OkHttp () fun main () { // chaos is split into \"stages\", which can be triggered by specific request or time-based criteria val doNothingStage = Wait . until { tx : Request -> tx . method == POST } val errorStage = ReturnStatus ( INTERNAL_SERVER_ERROR ). appliedWhen ( PercentageBased ( 50 )) // chain the stages together with then() and create the Chaos Engine (activated) val engine = ChaosEngine ( doNothingStage . then ( errorStage )). enable () val svc : HttpHandler = { Response ( OK ). body ( \"A normal response\" ) } engine . then ( svc ). asServer ( SunHttp ( 9000 )). start (). use { repeat ( 10 ) { performA ( GET ) } // this triggers the change in behaviour performA ( POST ) repeat ( 10 ) { performA ( GET ) } // disable the chaos engine . disable () repeat ( 10 ) { performA ( GET ) } } } fun performA ( method : Method ) = println ( method . name + \" got a \" + client ( Request ( method , \"http://localhost:9000\" )). status ) Dynamic behaviour injection using Chaos Controls \u00b6 For use in deployed environments or when experimenting with the reaction of systems to failure, there is the need to vary (and otherwise control) the Chaos behaviour that an application or downstream fake exhibits, in order to simulate periods of failures and then observe the after-effects. The module contains a simple extension method HttpHandler.withChaosEngine() that decorates an existing http4k application with the ability to dynamically inject Chaos behaviour using a set of RPC-style endpoints. This API is presented via an OpenAPI specification, which allows it to be controlled by a simple OpenApi client. Apart from being able to turn the Chaos on/off and check the status, the most powerful endpoint in ChaosEngine lives at /activate/new . By POSTing a JSON definition of the required behaviour, this JSON is deserialised into actual Chaos behaviours which can be then activated in the application. The supported JSON formats of the various Chaos concepts are defined above, but by way of an example, POSTing this piece of JSON would: Wait for 100 seconds Always return an HTTP 404 (Not Found) status for 10 requests Repeat the above until Big Ben strikes in the New Year 2020. [ { \"type\" : \"repeat\" , \"stages\" : [ { \"type\" : \"wait\" , \"until\" : { \"type\" : \"delay\" , \"period\" : \"PT100S\" } }, { \"type\" : \"trigger\" , \"behaviour\" : { \"type\" : \"status\" , \"status\" : 404 }, \"trigger\" : { \"type\" : \"always\" }, \"until\" : { \"type\" : \"countdown\" , \"count\" : \"10\" } } ], \"until\" : { \"type\" : \"deadline\" , \"endTime\" : \"2020-01-01T00:00:00Z\" } } ] Code \u00b6 package guide.reference.chaos import org.http4k.chaos.withChaosApi import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.CorsPolicy.Companion.UnsafeGlobalPermissive import org.http4k.filter.ServerFilters import org.http4k.filter.ServerFilters.Cors import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { Cors ( UnsafeGlobalPermissive ) . then ( ServerFilters . CatchAll ()) . then { Response ( OK ). body ( \"A normal response\" ) } . withChaosApi () . asServer ( SunHttp ( 9000 )) . start () . also { println ( \"Visit the app at http://localhost:9000 or see the OpenApi at https://www.http4k.org/openapi3/?url=http://localhost:9000/chaos\" ) } } Interacting with ChaosEngine using an HTTP client \u00b6 Code \u00b6 package guide.reference.chaos import org.http4k.chaos.ChaosBehaviours.ReturnStatus import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes fun main () { val app = routes ( \"/\" bind routes ( \"/\" bind GET to { Response ( OK ). body ( \"hello!\" ) })) val appWithChaos = app . withChaosApi ( ChaosEngine ( ReturnStatus ( NOT_FOUND ))) println ( \">>chaos is deactivated by default\" ) println ( appWithChaos ( Request ( GET , \"/chaos/status\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>activate the default chaos\" ) println ( appWithChaos ( Request ( POST , \"/chaos/activate\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>deactivate the default chaos\" ) println ( appWithChaos ( Request ( POST , \"/chaos/deactivate\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>set the chaos dynamically\" ) val alwaysReturn418 = \"\"\"[ { \"type\": \"trigger\", \"behaviour\": { \"type\": \"status\", \"status\": 418 }, \"trigger\": { \"type\": \"always\" } }] \"\"\" . trimIndent () println ( appWithChaos ( Request ( POST , \"/chaos/activate/new\" ). body ( alwaysReturn418 ) ). bodyString () ) println ( appWithChaos ( Request ( GET , \"/\" )). status ) }","title":"Chaos Testing"},{"location":"guide/reference/chaos/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-chaos\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/chaos/#about","text":"The http4k Chaos module provides the facility to statically or dynamically inject failure modes into http4k applications, such as random HTTP failures, killing of processes, and extra latency injection. By modelling these modes, it is possible to plan for mitigation of particular scenarios on a wider scale, resulting either from failures within your system boundary, or those caused by dependent remote HTTP services. The Principles of Chaos Engineering approach was made prominent by Netflix open-sourcing the Simian Army libraries.","title":"About"},{"location":"guide/reference/chaos/#api_concepts","text":"To understand the API, these domain-language concepts are important, all modelled as simple Kotlin typealiases and interfaces in order that API users can create their own:","title":"API concepts"},{"location":"guide/reference/chaos/#behaviours_typealias_behaviour_filter","text":"A Behaviour applies the failure mode to the HTTP call. This could involve blocking a thread permanently, introducing extra latency into an HTTP service, or even causing a Stack Overflow or Killing the running process. Behaviour function Effect as JSON Latency Adds random latency to a call between the min and max durations {\"type\":\"latency\",\"min\":\"PT0.1S\",\"max\":\"PT0.3S\"} ThrowException Throws an uncaught Exception with the supplied message {\"type\":\"throw\",\"message\":\"foo\"} ReturnStatus Returns an HTTP response with the specified HTTP status code {\"type\":\"status\",\"status\":404} NoBody Completes the call normally, but strips the body content from the response {\"type\":\"body\"} EatMemory Forces an OOM exception {\"type\":\"memory\"} KillProcess Kills the Java process with a 1 error code {\"type\":\"kill\"} StackOverflow Generates a StackOverflow {\"type\":\"overflow\"} BlockThread Permanently blocks the request thread {\"type\":\"block\"} None Requests complete normally {\"type\":\"none\"}","title":"Behaviours: typealias Behaviour = Filter"},{"location":"guide/reference/chaos/#triggers_typealias_trigger_req_request_-_boolean","text":"A Trigger is just a predicate which determines if an HTTP call should have an Behaviour applied to it. Triggers can be stateless, based on the request content, or stateful - deadlines or countdowns. Trigger function Activation condition as JSON Deadline After an instant in time {\"type\":\"deadline\",\"endTime\":\"1970-01-01T00:00:00Z\"} Delay After a specified period (since construction) {\"type\":\"delay\",\"period\":\"PT0.1S\"} Countdown For the first n requests only {\"type\":\"countdown\",\"count\":\"1\"} Request If the request meets the criteria set out in the specification. All but method are Regex patterns, and all are optional {\"type\":\"request\",\"method\":\"get\",\"path\":\".*bob\",\"queries\":{\"query\":\".*query\"},\"headers\":{\"header\":\".*header\"},\"body\":\".*body\"} Once For the first request only {\"type\":\"once\"} PercentageBased Applies to a certain (randomly decided) percentage of requests {\"type\":\"percentage\", \"percentage\":100} Always For all requests {\"type\":\"always\"}","title":"Triggers: typealias Trigger = (req: Request) -> Boolean"},{"location":"guide/reference/chaos/#stages_interface_stage_request_-_filter","text":"A Stage provides the lifecycle for applying a behaviour, and applies until a Trigger indicates that the stage is complete. Stages can be chained with then() , or can be produced by combining a Behaviour and a Trigger using appliedWhen() . Stage function Lifecycle notes as JSON Wait Does nothing while active {\"type\":\"wait\",\"until\":} Repeat Loops through the stages and then repeats {\"type\":\"repeat\",\"stages\":[],\"until\":} (Triggered) Combines a Trigger and a Behaviour {\"type\":\"trigger\",\"behaviour\":{\"type\":\"body\"},\"trigger\":,\"until\":}}","title":"Stages: interface Stage: (Request) -> Filter?"},{"location":"guide/reference/chaos/#manually_injecting_chaos","text":"For use in automated test suites, it is simple to define the Chaos behaviour programmatically using the API and then use the ChaosEngine to add it onto an existing application.","title":"Manually injecting Chaos"},{"location":"guide/reference/chaos/#code","text":"package guide.reference.chaos import org.http4k.chaos.ChaosBehaviours.ReturnStatus import org.http4k.chaos.ChaosEngine import org.http4k.chaos.ChaosStages.Wait import org.http4k.chaos.ChaosTriggers.PercentageBased import org.http4k.chaos.appliedWhen import org.http4k.chaos.then import org.http4k.chaos.until import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.server.SunHttp import org.http4k.server.asServer val client = OkHttp () fun main () { // chaos is split into \"stages\", which can be triggered by specific request or time-based criteria val doNothingStage = Wait . until { tx : Request -> tx . method == POST } val errorStage = ReturnStatus ( INTERNAL_SERVER_ERROR ). appliedWhen ( PercentageBased ( 50 )) // chain the stages together with then() and create the Chaos Engine (activated) val engine = ChaosEngine ( doNothingStage . then ( errorStage )). enable () val svc : HttpHandler = { Response ( OK ). body ( \"A normal response\" ) } engine . then ( svc ). asServer ( SunHttp ( 9000 )). start (). use { repeat ( 10 ) { performA ( GET ) } // this triggers the change in behaviour performA ( POST ) repeat ( 10 ) { performA ( GET ) } // disable the chaos engine . disable () repeat ( 10 ) { performA ( GET ) } } } fun performA ( method : Method ) = println ( method . name + \" got a \" + client ( Request ( method , \"http://localhost:9000\" )). status )","title":"Code"},{"location":"guide/reference/chaos/#dynamic_behaviour_injection_using_chaos_controls","text":"For use in deployed environments or when experimenting with the reaction of systems to failure, there is the need to vary (and otherwise control) the Chaos behaviour that an application or downstream fake exhibits, in order to simulate periods of failures and then observe the after-effects. The module contains a simple extension method HttpHandler.withChaosEngine() that decorates an existing http4k application with the ability to dynamically inject Chaos behaviour using a set of RPC-style endpoints. This API is presented via an OpenAPI specification, which allows it to be controlled by a simple OpenApi client. Apart from being able to turn the Chaos on/off and check the status, the most powerful endpoint in ChaosEngine lives at /activate/new . By POSTing a JSON definition of the required behaviour, this JSON is deserialised into actual Chaos behaviours which can be then activated in the application. The supported JSON formats of the various Chaos concepts are defined above, but by way of an example, POSTing this piece of JSON would: Wait for 100 seconds Always return an HTTP 404 (Not Found) status for 10 requests Repeat the above until Big Ben strikes in the New Year 2020. [ { \"type\" : \"repeat\" , \"stages\" : [ { \"type\" : \"wait\" , \"until\" : { \"type\" : \"delay\" , \"period\" : \"PT100S\" } }, { \"type\" : \"trigger\" , \"behaviour\" : { \"type\" : \"status\" , \"status\" : 404 }, \"trigger\" : { \"type\" : \"always\" }, \"until\" : { \"type\" : \"countdown\" , \"count\" : \"10\" } } ], \"until\" : { \"type\" : \"deadline\" , \"endTime\" : \"2020-01-01T00:00:00Z\" } } ]","title":"Dynamic behaviour injection using Chaos Controls"},{"location":"guide/reference/chaos/#code_1","text":"package guide.reference.chaos import org.http4k.chaos.withChaosApi import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.CorsPolicy.Companion.UnsafeGlobalPermissive import org.http4k.filter.ServerFilters import org.http4k.filter.ServerFilters.Cors import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { Cors ( UnsafeGlobalPermissive ) . then ( ServerFilters . CatchAll ()) . then { Response ( OK ). body ( \"A normal response\" ) } . withChaosApi () . asServer ( SunHttp ( 9000 )) . start () . also { println ( \"Visit the app at http://localhost:9000 or see the OpenApi at https://www.http4k.org/openapi3/?url=http://localhost:9000/chaos\" ) } }","title":"Code"},{"location":"guide/reference/chaos/#interacting_with_chaosengine_using_an_http_client","text":"","title":"Interacting with ChaosEngine using an HTTP client"},{"location":"guide/reference/chaos/#code_2","text":"package guide.reference.chaos import org.http4k.chaos.ChaosBehaviours.ReturnStatus import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes fun main () { val app = routes ( \"/\" bind routes ( \"/\" bind GET to { Response ( OK ). body ( \"hello!\" ) })) val appWithChaos = app . withChaosApi ( ChaosEngine ( ReturnStatus ( NOT_FOUND ))) println ( \">>chaos is deactivated by default\" ) println ( appWithChaos ( Request ( GET , \"/chaos/status\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>activate the default chaos\" ) println ( appWithChaos ( Request ( POST , \"/chaos/activate\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>deactivate the default chaos\" ) println ( appWithChaos ( Request ( POST , \"/chaos/deactivate\" )). bodyString ()) println ( appWithChaos ( Request ( GET , \"/\" )). status ) println ( \">>set the chaos dynamically\" ) val alwaysReturn418 = \"\"\"[ { \"type\": \"trigger\", \"behaviour\": { \"type\": \"status\", \"status\": 418 }, \"trigger\": { \"type\": \"always\" } }] \"\"\" . trimIndent () println ( appWithChaos ( Request ( POST , \"/chaos/activate/new\" ). body ( alwaysReturn418 ) ). bodyString () ) println ( appWithChaos ( Request ( GET , \"/\" )). status ) }","title":"Code"},{"location":"guide/reference/clients/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Java (for development only): implementation ( \"org.http4k:http4k-core\" ) // Apache v5 (Sync): implementation ( \"org.http4k:http4k-client-apache\" ) // Apache v4 (Sync): implementation ( \"org.http4k:http4k-client-apache4\" ) // Apache v5 (Async): implementation ( \"org.http4k:http4k-client-apache-async\" ) // Apache v4 (Async): implementation ( \"org.http4k:http4k-client-apache4-async\" ) // Fuel (Sync + Async): implementation ( \"org.http4k:http4k-client-fuel\" ) // Helidon (Loom): implementation ( \"org.http4k:http4k-client-helidon\" ) // Jetty (Sync + Async + WebSocket): implementation ( \"org.http4k:http4k-client-jetty\" ) // OkHttp (Sync + Async): implementation ( \"org.http4k:http4k-client-okhttp\" ) // Websocket: implementation ( \"org.http4k:http4k-client-websocket\" ) } HTTP \u00b6 Supported HTTP client adapter APIs are wrapped to provide an HttpHandler interface in 1 LOC. Since each client acts as an HttpHandler , it can be decorated with various Filter implementations, such as those available in ClientFilters . This allows handling cross-cutting concerns independently of a specific client implementation, greatly facilitating testing. Activate streaming mode by passing a BodyMode (default is non-streaming). ClientFilters offers a collection of filters that can be applied to an HttpHandler to manage common cross-cutting concernsas a chain of filters. This chain allows for the easy configuration and management of complex processing sequences. ClientFilters includes specific filters that enable frequently needed functionalities like authentication, caching, or compression with minimal configuration. These examples are for the Apache HTTP client, but the API is similar for the others: Code \u00b6 package guide.reference.clients import org.apache.hc.client5.http.config.RequestConfig import org.apache.hc.client5.http.cookie.StandardCookieSpec import org.apache.hc.client5.http.impl.classic.HttpClients import org.http4k.client.ApacheAsyncClient import org.http4k.client.ApacheClient import org.http4k.core.BodyMode import org.http4k.core.Method.GET import org.http4k.core.Request import kotlin.concurrent.thread fun main () { // standard client val client = ApacheClient () val request = Request ( GET , \"http://httpbin.org/get\" ). query ( \"location\" , \"John Doe\" ) val response = client ( request ) println ( \"SYNC\" ) println ( response . status ) println ( response . bodyString ()) // streaming client val streamingClient = ApacheClient ( responseBodyMode = BodyMode . Stream ) val streamingRequest = Request ( GET , \"http://httpbin.org/stream/100\" ) println ( \"STREAM\" ) println ( streamingClient ( streamingRequest ). bodyString ()) // async supporting clients can be passed a callback... val asyncClient = ApacheAsyncClient () asyncClient ( Request ( GET , \"http://httpbin.org/stream/5\" )) { println ( \"ASYNC\" ) println ( it . status ) println ( it . bodyString ()) } // ... but must be closed thread { Thread . sleep ( 500 ) asyncClient . close () } // custom configured client val customClient = ApacheClient ( client = HttpClients . custom (). setDefaultRequestConfig ( RequestConfig . custom () . setRedirectsEnabled ( false ) . setCookieSpec ( StandardCookieSpec . IGNORE ) . build () ) . build () ) } Additionally, all HTTP client adapter modules allow for custom configuration of the relevant underlying client. Async-supporting clients implement the AsyncHttpClient interface can be passed a callback. Websocket \u00b6 http4k supplies both blocking and non-blocking Websocket clients. The former is perfect for integration testing purposes, and as it uses the same interface WsClient as the in-memory test client ( WsHandler.testWsClient() ) it is simple to write unit tests which can then be reused as system tests by virtue of swapping out the client. Code \u00b6 package guide.reference.clients import org.http4k.client.WebsocketClient import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse fun main () { // a standard websocket app val server = websockets ( \"/bob\" bind { _ : Request -> WsResponse { ws -> ws . send ( WsMessage ( \"bob\" )) ws . onMessage { println ( \"server received: $ it \" ) ws . send ( it ) } } } ). asServer ( Jetty ( 8000 )). start () // blocking client - connection is done on construction val blockingClient = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) blockingClient . send ( WsMessage ( \"server sent on connection\" )) blockingClient . received (). take ( 2 ) . forEach { println ( \"blocking client received: $ it \" ) } blockingClient . close () // non-blocking client - exposes a Websocket interface for attaching listeners, // and connection is done on construction, but doesn't block - the (optional) handler // passed to the construction is called on connection. val nonBlockingClient = WebsocketClient . nonBlocking ( Uri . of ( \"ws://localhost:8000/bob\" )) { it . run { send ( WsMessage ( \"client sent on connection\" )) } } nonBlockingClient . onMessage { println ( \"non-blocking client received: $ it \" ) } nonBlockingClient . onClose { println ( \"non-blocking client closing\" ) } Thread . sleep ( 100 ) server . stop () } Testing Websockets with offline and online clients \u00b6 package guide.reference.clients import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - // it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"HTTP & Websocket clients"},{"location":"guide/reference/clients/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Java (for development only): implementation ( \"org.http4k:http4k-core\" ) // Apache v5 (Sync): implementation ( \"org.http4k:http4k-client-apache\" ) // Apache v4 (Sync): implementation ( \"org.http4k:http4k-client-apache4\" ) // Apache v5 (Async): implementation ( \"org.http4k:http4k-client-apache-async\" ) // Apache v4 (Async): implementation ( \"org.http4k:http4k-client-apache4-async\" ) // Fuel (Sync + Async): implementation ( \"org.http4k:http4k-client-fuel\" ) // Helidon (Loom): implementation ( \"org.http4k:http4k-client-helidon\" ) // Jetty (Sync + Async + WebSocket): implementation ( \"org.http4k:http4k-client-jetty\" ) // OkHttp (Sync + Async): implementation ( \"org.http4k:http4k-client-okhttp\" ) // Websocket: implementation ( \"org.http4k:http4k-client-websocket\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/clients/#http","text":"Supported HTTP client adapter APIs are wrapped to provide an HttpHandler interface in 1 LOC. Since each client acts as an HttpHandler , it can be decorated with various Filter implementations, such as those available in ClientFilters . This allows handling cross-cutting concerns independently of a specific client implementation, greatly facilitating testing. Activate streaming mode by passing a BodyMode (default is non-streaming). ClientFilters offers a collection of filters that can be applied to an HttpHandler to manage common cross-cutting concernsas a chain of filters. This chain allows for the easy configuration and management of complex processing sequences. ClientFilters includes specific filters that enable frequently needed functionalities like authentication, caching, or compression with minimal configuration. These examples are for the Apache HTTP client, but the API is similar for the others:","title":"HTTP"},{"location":"guide/reference/clients/#code","text":"package guide.reference.clients import org.apache.hc.client5.http.config.RequestConfig import org.apache.hc.client5.http.cookie.StandardCookieSpec import org.apache.hc.client5.http.impl.classic.HttpClients import org.http4k.client.ApacheAsyncClient import org.http4k.client.ApacheClient import org.http4k.core.BodyMode import org.http4k.core.Method.GET import org.http4k.core.Request import kotlin.concurrent.thread fun main () { // standard client val client = ApacheClient () val request = Request ( GET , \"http://httpbin.org/get\" ). query ( \"location\" , \"John Doe\" ) val response = client ( request ) println ( \"SYNC\" ) println ( response . status ) println ( response . bodyString ()) // streaming client val streamingClient = ApacheClient ( responseBodyMode = BodyMode . Stream ) val streamingRequest = Request ( GET , \"http://httpbin.org/stream/100\" ) println ( \"STREAM\" ) println ( streamingClient ( streamingRequest ). bodyString ()) // async supporting clients can be passed a callback... val asyncClient = ApacheAsyncClient () asyncClient ( Request ( GET , \"http://httpbin.org/stream/5\" )) { println ( \"ASYNC\" ) println ( it . status ) println ( it . bodyString ()) } // ... but must be closed thread { Thread . sleep ( 500 ) asyncClient . close () } // custom configured client val customClient = ApacheClient ( client = HttpClients . custom (). setDefaultRequestConfig ( RequestConfig . custom () . setRedirectsEnabled ( false ) . setCookieSpec ( StandardCookieSpec . IGNORE ) . build () ) . build () ) } Additionally, all HTTP client adapter modules allow for custom configuration of the relevant underlying client. Async-supporting clients implement the AsyncHttpClient interface can be passed a callback.","title":"Code"},{"location":"guide/reference/clients/#websocket","text":"http4k supplies both blocking and non-blocking Websocket clients. The former is perfect for integration testing purposes, and as it uses the same interface WsClient as the in-memory test client ( WsHandler.testWsClient() ) it is simple to write unit tests which can then be reused as system tests by virtue of swapping out the client.","title":"Websocket"},{"location":"guide/reference/clients/#code_1","text":"package guide.reference.clients import org.http4k.client.WebsocketClient import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse fun main () { // a standard websocket app val server = websockets ( \"/bob\" bind { _ : Request -> WsResponse { ws -> ws . send ( WsMessage ( \"bob\" )) ws . onMessage { println ( \"server received: $ it \" ) ws . send ( it ) } } } ). asServer ( Jetty ( 8000 )). start () // blocking client - connection is done on construction val blockingClient = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) blockingClient . send ( WsMessage ( \"server sent on connection\" )) blockingClient . received (). take ( 2 ) . forEach { println ( \"blocking client received: $ it \" ) } blockingClient . close () // non-blocking client - exposes a Websocket interface for attaching listeners, // and connection is done on construction, but doesn't block - the (optional) handler // passed to the construction is called on connection. val nonBlockingClient = WebsocketClient . nonBlocking ( Uri . of ( \"ws://localhost:8000/bob\" )) { it . run { send ( WsMessage ( \"client sent on connection\" )) } } nonBlockingClient . onMessage { println ( \"non-blocking client received: $ it \" ) } nonBlockingClient . onClose { println ( \"non-blocking client closing\" ) } Thread . sleep ( 100 ) server . stop () }","title":"Code"},{"location":"guide/reference/clients/#testing_websockets_with_offline_and_online_clients","text":"package guide.reference.clients import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - // it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"Testing Websockets with offline and online clients"},{"location":"guide/reference/cloud_events/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-cloudevents\" ) } The Cloud Events spec defines a common format for Events produced by Cloud services. http4k provides simple pluggability into the CloudEvents Java SDKs and custom event format libraries via the Lens system - making it trivial to both receive or send CloudEvents in the standard way. Example \u00b6 In this example we are using the Jackson JSONFormat which is included by default with the http4k-cloudevents module. If you want to also use the lenses to access typed EventData, you will also need this in your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // to access the lenses in the Jackson module implementation ( \"org.http4k:http4k-format-jackson\" ) } Code \u00b6 package guide.reference.cloud_events import io.cloudevents.core.builder.CloudEventBuilder import io.cloudevents.core.builder.withSource import io.cloudevents.core.provider.EventFormatProvider import io.cloudevents.http4k.cloudEventsFormat import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.filter.debug import org.http4k.format.Jackson import org.http4k.format.Jackson.withData import org.http4k.format.cloudEventDataLens import org.http4k.lens.cloudEvent import org.http4k.routing.bind import org.http4k.routing.routes import java.time.OffsetDateTime import java.util.UUID fun main () { // Events formats must be registered into a singleton provided by the CloudEvents SDK. // Here we are using the format with any http4k Jackson mappings. EventFormatProvider . getInstance (). registerFormat ( Jackson . cloudEventsFormat ()) // We use one lens to get the event envelope and another to get the typed data from the Event val eventLens = Body . cloudEvent (). toLens () val dataLens = Jackson . cloudEventDataLens < MyCloudEventData > () val app = CatchLensFailure () . then ( routes ( \"/foo/bar\" bind POST to { // Our app uses lenses in the normal way to extract the event from the request.. val cloudEvent = eventLens ( it ) // ... and then the typed event data from the event envelope val eventData = dataLens ( cloudEvent ) println ( \"Event: $ cloudEvent \" ) println ( \"Event Data: $ eventData \" ) Response ( OK ) } )). debug () // Create the base CloudEvent without the data... // then inject the data into the Event - this sets the content type of the event val cloudEvent = CloudEventBuilder . v1 () . withId ( UUID . randomUUID (). toString ()) . withSource ( Uri . of ( \"localhost\" )) . withTime ( OffsetDateTime . now ()) . withType ( \"myEventType\" ) // this is a custom extension function from Jackson (needs to be imported) . withData ( MyCloudEventData ( 10 , Uri . of ( \"foobar\" ))) . build () // ...lastly inject the event into the request and send it to the server app ( Request ( POST , \"/foo/bar\" ). with ( eventLens of cloudEvent )) } // define a custom event which will be sent/received in the \"data\" field of the CloudEvent data class MyCloudEventData ( val value : Int , val uri : Uri )","title":"Cloud Events"},{"location":"guide/reference/cloud_events/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-cloudevents\" ) } The Cloud Events spec defines a common format for Events produced by Cloud services. http4k provides simple pluggability into the CloudEvents Java SDKs and custom event format libraries via the Lens system - making it trivial to both receive or send CloudEvents in the standard way.","title":"Installation (Gradle)"},{"location":"guide/reference/cloud_events/#example","text":"In this example we are using the Jackson JSONFormat which is included by default with the http4k-cloudevents module. If you want to also use the lenses to access typed EventData, you will also need this in your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // to access the lenses in the Jackson module implementation ( \"org.http4k:http4k-format-jackson\" ) }","title":"Example"},{"location":"guide/reference/cloud_events/#code","text":"package guide.reference.cloud_events import io.cloudevents.core.builder.CloudEventBuilder import io.cloudevents.core.builder.withSource import io.cloudevents.core.provider.EventFormatProvider import io.cloudevents.http4k.cloudEventsFormat import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.filter.debug import org.http4k.format.Jackson import org.http4k.format.Jackson.withData import org.http4k.format.cloudEventDataLens import org.http4k.lens.cloudEvent import org.http4k.routing.bind import org.http4k.routing.routes import java.time.OffsetDateTime import java.util.UUID fun main () { // Events formats must be registered into a singleton provided by the CloudEvents SDK. // Here we are using the format with any http4k Jackson mappings. EventFormatProvider . getInstance (). registerFormat ( Jackson . cloudEventsFormat ()) // We use one lens to get the event envelope and another to get the typed data from the Event val eventLens = Body . cloudEvent (). toLens () val dataLens = Jackson . cloudEventDataLens < MyCloudEventData > () val app = CatchLensFailure () . then ( routes ( \"/foo/bar\" bind POST to { // Our app uses lenses in the normal way to extract the event from the request.. val cloudEvent = eventLens ( it ) // ... and then the typed event data from the event envelope val eventData = dataLens ( cloudEvent ) println ( \"Event: $ cloudEvent \" ) println ( \"Event Data: $ eventData \" ) Response ( OK ) } )). debug () // Create the base CloudEvent without the data... // then inject the data into the Event - this sets the content type of the event val cloudEvent = CloudEventBuilder . v1 () . withId ( UUID . randomUUID (). toString ()) . withSource ( Uri . of ( \"localhost\" )) . withTime ( OffsetDateTime . now ()) . withType ( \"myEventType\" ) // this is a custom extension function from Jackson (needs to be imported) . withData ( MyCloudEventData ( 10 , Uri . of ( \"foobar\" ))) . build () // ...lastly inject the event into the request and send it to the server app ( Request ( POST , \"/foo/bar\" ). with ( eventLens of cloudEvent )) } // define a custom event which will be sent/received in the \"data\" field of the CloudEvent data class MyCloudEventData ( val value : Int , val uri : Uri )","title":"Code"},{"location":"guide/reference/cloud_native/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-cloudnative\" ) } http4k applications are naturally at home operating in distributed, Cloud Native environments. Whilst simple to create, this module provides requisite tooling to get apps up and running with the minimum of effort to enable the following operational aspects: Quick start \u00b6 Because http4k does not use reflection or annotation process for application startup, all of the supported Server-backends start up and shutdown very quickly - this is crucial for cloud-based environments where an orchestration framework might move instances around to redistribute load or avoid problematic server/rack/DCs. Observability \u00b6 Orchestration software such as Kubernetes and CloudFoundry regularly query a set of diagnostic endpoints to monitor the state of an application. This module provides standardised HttpHandler implementations to model the following endpoints: Liveness - used to determine if the application is actually alive. Readiness - used to determine if the application is available to receive production traffic from the cloud Load Balancer. This endpoint performs a series of diagnostic checks against it's dependencies (such as database connectivity) and collates the results to report back to the orchestrator. http4k provides the ReadinessCheck interface which can be implementaed as required and plugged into the endpoint. In Kubernetes, this set of endpoints is generally hosted on a second port to avoid the API clashes, so http4k provides the machinery to easily start these services on a different port to the main application API via the Http4kK8sServer object. Code \u00b6 package guide.reference.cloud_native import org.http4k.client.JavaHttpClient import org.http4k.cloudnative.Http4kK8sServer import org.http4k.cloudnative.asK8sServer import org.http4k.cloudnative.health.Completed import org.http4k.cloudnative.health.Health import org.http4k.cloudnative.health.ReadinessCheck import org.http4k.cloudnative.health.ReadinessCheckResult import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.DebuggingFilters import org.http4k.lens.Lens import org.http4k.lens.secret import org.http4k.routing.bind import org.http4k.server.SunHttp import org.http4k.server.asServer import kotlin.random.Random // the entire k8s application consists of 2 servers - the main and the health object App { // settings private val otherServiceUri : Lens < Environment , Uri > = EnvironmentKey . k8s . serviceUriFor ( \"otherservice\" ) private val dbRole = EnvironmentKey . required ( \"database.user.role\" ) private val dbPassword = EnvironmentKey . secret (). required ( \"database.user.password\" ) operator fun invoke ( env : Environment ): Http4kK8sServer { // define the main app API - it proxies to the \"other\" service val mainApp = ClientFilters . SetHostFrom ( otherServiceUri ( env )) . then ( rewriteUriToLocalhostAsWeDoNotHaveDns ) // this line only here to make the example work! . then ( JavaHttpClient ()) // define the health app API val healthApp = Health ( \"/config\" bind GET to { Response ( OK ). body ( env . keys (). toString ()) }, checks = listOf ( DatabaseCheck ( RandomlyFailingDatabase ( dbRole ( env ), dbPassword ( env ) ) ) ) ) return mainApp . asK8sServer ( :: SunHttp , env , healthApp ) } private val rewriteUriToLocalhostAsWeDoNotHaveDns = Filter { next -> { println ( \"Rewriting ${ it . uri } so we can proxy properly\" ) next ( it . uri ( it . uri . authority ( \"localhost:9000\" ))) } } } // this is a database client that we are going to health check class RandomlyFailingDatabase ( private val user : String , password : Secret ) { init { // the secret is a single-shot value whose value will be discarded after use password . use { println ( \"setting up the database connection with creds: $ user / $ it \" ) } } fun insertARecord () { if ( Random ( 1 ). nextBoolean ()) throw Exception ( \"oh no! $ user has no access\" ) } } // implements the check which will determine if this service is ready to go class DatabaseCheck ( private val db : RandomlyFailingDatabase ) : ReadinessCheck { override val name = \"database\" override fun invoke (): ReadinessCheckResult { db . insertARecord () return Completed ( name ) } } /** file app.properties contains database.user.role=admin database.user.password=myPassword */ fun main () { val defaultConfig = Environment . defaults ( EnvironmentKey . k8s . SERVICE_PORT of 8000 , EnvironmentKey . k8s . HEALTH_PORT of 8001 , EnvironmentKey . k8s . serviceUriFor ( \"otherservice\" ) of Uri . of ( \"https://localhost:8000\" ) ) // standard chaining order for properties is local file -> JVM -> Environment -> defaults -> boom! val k8sPodEnv = Environment . fromResource ( \"app.properties\" ) overrides Environment . JVM_PROPERTIES overrides Environment . ENV overrides defaultConfig // the end-server that we will proxy to val upstream = { _ : Request -> Response ( OK ). body ( \"HELLO!\" ) }. asServer ( SunHttp ( 9000 )). start () val server = App ( k8sPodEnv ). start () performHealthChecks () server . stop () upstream . stop () } private fun performHealthChecks () { val client = DebuggingFilters . PrintResponse (). then ( JavaHttpClient ()) // health checks client ( Request ( GET , \"http://localhost:8001/liveness\" )) client ( Request ( GET , \"http://localhost:8001/readiness\" )) client ( Request ( GET , \"http://localhost:8001/config\" )) // proxied call client ( Request ( GET , \"http://localhost:8000\" )) }","title":"Cloud Native Extensions"},{"location":"guide/reference/cloud_native/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-cloudnative\" ) } http4k applications are naturally at home operating in distributed, Cloud Native environments. Whilst simple to create, this module provides requisite tooling to get apps up and running with the minimum of effort to enable the following operational aspects:","title":"Installation (Gradle)"},{"location":"guide/reference/cloud_native/#quick_start","text":"Because http4k does not use reflection or annotation process for application startup, all of the supported Server-backends start up and shutdown very quickly - this is crucial for cloud-based environments where an orchestration framework might move instances around to redistribute load or avoid problematic server/rack/DCs.","title":"Quick start"},{"location":"guide/reference/cloud_native/#observability","text":"Orchestration software such as Kubernetes and CloudFoundry regularly query a set of diagnostic endpoints to monitor the state of an application. This module provides standardised HttpHandler implementations to model the following endpoints: Liveness - used to determine if the application is actually alive. Readiness - used to determine if the application is available to receive production traffic from the cloud Load Balancer. This endpoint performs a series of diagnostic checks against it's dependencies (such as database connectivity) and collates the results to report back to the orchestrator. http4k provides the ReadinessCheck interface which can be implementaed as required and plugged into the endpoint. In Kubernetes, this set of endpoints is generally hosted on a second port to avoid the API clashes, so http4k provides the machinery to easily start these services on a different port to the main application API via the Http4kK8sServer object.","title":"Observability"},{"location":"guide/reference/cloud_native/#code","text":"package guide.reference.cloud_native import org.http4k.client.JavaHttpClient import org.http4k.cloudnative.Http4kK8sServer import org.http4k.cloudnative.asK8sServer import org.http4k.cloudnative.health.Completed import org.http4k.cloudnative.health.Health import org.http4k.cloudnative.health.ReadinessCheck import org.http4k.cloudnative.health.ReadinessCheckResult import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.DebuggingFilters import org.http4k.lens.Lens import org.http4k.lens.secret import org.http4k.routing.bind import org.http4k.server.SunHttp import org.http4k.server.asServer import kotlin.random.Random // the entire k8s application consists of 2 servers - the main and the health object App { // settings private val otherServiceUri : Lens < Environment , Uri > = EnvironmentKey . k8s . serviceUriFor ( \"otherservice\" ) private val dbRole = EnvironmentKey . required ( \"database.user.role\" ) private val dbPassword = EnvironmentKey . secret (). required ( \"database.user.password\" ) operator fun invoke ( env : Environment ): Http4kK8sServer { // define the main app API - it proxies to the \"other\" service val mainApp = ClientFilters . SetHostFrom ( otherServiceUri ( env )) . then ( rewriteUriToLocalhostAsWeDoNotHaveDns ) // this line only here to make the example work! . then ( JavaHttpClient ()) // define the health app API val healthApp = Health ( \"/config\" bind GET to { Response ( OK ). body ( env . keys (). toString ()) }, checks = listOf ( DatabaseCheck ( RandomlyFailingDatabase ( dbRole ( env ), dbPassword ( env ) ) ) ) ) return mainApp . asK8sServer ( :: SunHttp , env , healthApp ) } private val rewriteUriToLocalhostAsWeDoNotHaveDns = Filter { next -> { println ( \"Rewriting ${ it . uri } so we can proxy properly\" ) next ( it . uri ( it . uri . authority ( \"localhost:9000\" ))) } } } // this is a database client that we are going to health check class RandomlyFailingDatabase ( private val user : String , password : Secret ) { init { // the secret is a single-shot value whose value will be discarded after use password . use { println ( \"setting up the database connection with creds: $ user / $ it \" ) } } fun insertARecord () { if ( Random ( 1 ). nextBoolean ()) throw Exception ( \"oh no! $ user has no access\" ) } } // implements the check which will determine if this service is ready to go class DatabaseCheck ( private val db : RandomlyFailingDatabase ) : ReadinessCheck { override val name = \"database\" override fun invoke (): ReadinessCheckResult { db . insertARecord () return Completed ( name ) } } /** file app.properties contains database.user.role=admin database.user.password=myPassword */ fun main () { val defaultConfig = Environment . defaults ( EnvironmentKey . k8s . SERVICE_PORT of 8000 , EnvironmentKey . k8s . HEALTH_PORT of 8001 , EnvironmentKey . k8s . serviceUriFor ( \"otherservice\" ) of Uri . of ( \"https://localhost:8000\" ) ) // standard chaining order for properties is local file -> JVM -> Environment -> defaults -> boom! val k8sPodEnv = Environment . fromResource ( \"app.properties\" ) overrides Environment . JVM_PROPERTIES overrides Environment . ENV overrides defaultConfig // the end-server that we will proxy to val upstream = { _ : Request -> Response ( OK ). body ( \"HELLO!\" ) }. asServer ( SunHttp ( 9000 )). start () val server = App ( k8sPodEnv ). start () performHealthChecks () server . stop () upstream . stop () } private fun performHealthChecks () { val client = DebuggingFilters . PrintResponse (). then ( JavaHttpClient ()) // health checks client ( Request ( GET , \"http://localhost:8001/liveness\" )) client ( Request ( GET , \"http://localhost:8001/readiness\" )) client ( Request ( GET , \"http://localhost:8001/config\" )) // proxied call client ( Request ( GET , \"http://localhost:8000\" )) }","title":"Code"},{"location":"guide/reference/config/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-config\" ) } http4k applications are naturally at home operating in distributed, Cloud Native environments. Whilst simple to create, this module provides requisite tooling to get apps up and running with the minimum of effort to enable the following operational aspects: Quick start \u00b6 Because http4k does not use reflection or annotation process for application startup, all of the supported Server-backends start up and shutdown very quickly - this is crucial for cloud-based environments where an orchestration framework might move instances around to redistribute load or avoid problematic server/rack/DCs. Configuration \u00b6 All application configuration should be injected via environmental variables. http4k provides an Environment object, along with typesafe variable binding using the in-built Lenses mechanism. This typesafe API is consistent with the other usages of Lenses Code \u00b6 package guide.reference.config import org.http4k.client.JavaHttpClient import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.DebuggingFilters import org.http4k.lens.secret import org.http4k.lens.uri import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer private val url = EnvironmentKey . uri (). required ( \"url\" ) private val secret = EnvironmentKey . secret (). required ( \"secret\" ) object App { operator fun invoke ( env : Environment ): Http4kServer { val app = routes ( \"/config\" bind GET to { Response ( OK ). body ( \"\"\" url: ${ url ( env ) } secret: ${ secret ( env ) } \"\"\" . trimIndent () ) } ) return app . asServer ( SunHttp ( 8001 )) } } fun main () { val defaultConfig = Environment . defaults ( url of Uri . of ( \"http://localhost:9000\" ), secret of Secret ( \"mysecret\" ) ) // standard chaining order for properties is local file -> JVM -> Environment -> defaults -> boom! val env = Environment . fromResource ( \"app.properties\" ) overrides Environment . JVM_PROPERTIES overrides Environment . ENV overrides defaultConfig val server = App ( env ). start () performHealthChecks () server . stop () } private fun performHealthChecks () { val client = DebuggingFilters . PrintResponse (). then ( JavaHttpClient ()) client ( Request ( GET , \"http://localhost:8001/config\" )) }","title":"Typesafe Configuration"},{"location":"guide/reference/config/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-config\" ) } http4k applications are naturally at home operating in distributed, Cloud Native environments. Whilst simple to create, this module provides requisite tooling to get apps up and running with the minimum of effort to enable the following operational aspects:","title":"Installation (Gradle)"},{"location":"guide/reference/config/#quick_start","text":"Because http4k does not use reflection or annotation process for application startup, all of the supported Server-backends start up and shutdown very quickly - this is crucial for cloud-based environments where an orchestration framework might move instances around to redistribute load or avoid problematic server/rack/DCs.","title":"Quick start"},{"location":"guide/reference/config/#configuration","text":"All application configuration should be injected via environmental variables. http4k provides an Environment object, along with typesafe variable binding using the in-built Lenses mechanism. This typesafe API is consistent with the other usages of Lenses","title":"Configuration"},{"location":"guide/reference/config/#code","text":"package guide.reference.config import org.http4k.client.JavaHttpClient import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.DebuggingFilters import org.http4k.lens.secret import org.http4k.lens.uri import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.SunHttp import org.http4k.server.asServer private val url = EnvironmentKey . uri (). required ( \"url\" ) private val secret = EnvironmentKey . secret (). required ( \"secret\" ) object App { operator fun invoke ( env : Environment ): Http4kServer { val app = routes ( \"/config\" bind GET to { Response ( OK ). body ( \"\"\" url: ${ url ( env ) } secret: ${ secret ( env ) } \"\"\" . trimIndent () ) } ) return app . asServer ( SunHttp ( 8001 )) } } fun main () { val defaultConfig = Environment . defaults ( url of Uri . of ( \"http://localhost:9000\" ), secret of Secret ( \"mysecret\" ) ) // standard chaining order for properties is local file -> JVM -> Environment -> defaults -> boom! val env = Environment . fromResource ( \"app.properties\" ) overrides Environment . JVM_PROPERTIES overrides Environment . ENV overrides defaultConfig val server = App ( env ). start () performHealthChecks () server . stop () } private fun performHealthChecks () { val client = DebuggingFilters . PrintResponse (). then ( JavaHttpClient ()) client ( Request ( GET , \"http://localhost:8001/config\" )) }","title":"Code"},{"location":"guide/reference/contracts/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) implementation ( \"org.http4k:http4k-format-\" ) } About \u00b6 The http4k-contract module adds a much more sophisticated routing mechanism to that available in http4k-core . It adds the facility to declare server-side Routes in a completely typesafe way, leveraging the Lens functionality from the core. These Routes are combined into Contracts , which have the following features: Auto-validating - the Route contract is automatically validated on each call for required-fields and type conversions, removing the requirement for any validation code to be written by the API user. Invalid calls result in a HTTP 400 (BAD_REQUEST) response. Self-describing: - a generated endpoint is provided which describes all of the Routes in that module. Implementations include OpenApi v2 & v3 documentation, including generation of JSON schema . These documents can then be used to generate HTTP client and server code in various languages using the OpenAPI generator . models for messages. Security: to secure the Routes against unauthorised access. Current implementations include ApiKey , BasicAuth , BearerAuth , OpenIdConnect and OAuth . Callbacks and Webhooks can be declared, which give the same level of documentation and model generation Code \u00b6 package guide.reference.contracts // for this example we're using Jackson - note that the auto method imported is an extension // function that is defined on the Jackson instance import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.bind import org.http4k.contract.bindCallback import org.http4k.contract.contract import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.ApiKeySecurity import org.http4k.contract.security.BasicAuthSecurity import org.http4k.contract.ui.swaggerUiLite import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.I_M_A_TEAPOT import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.with import org.http4k.format.Jackson import org.http4k.format.Jackson.auto import org.http4k.format.Klaxon.json import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer // 1. this route has no dynamic path segments and simply echoes the body back. We are also adding various metadata // to the route, which will be used in the OpenAPI documentation. All of the metadata is optional. fun echo (): ContractRoute = \"/echo\" meta { summary = \"echoes the body back\" description = \"This is a simple route which echoes the body back to the caller\" tags += Tag ( \"env\" , \"production\" ) consumes += TEXT_PLAIN produces += TEXT_PLAIN returning ( OK , I_M_A_TEAPOT ) } bindContract POST to { req : Request -> Response ( OK ). body ( req . body ) } // 2. this route has a dynamic path which is automatically injected into the handler and security applied. There // are various security instances, from Basic to APIKey to OAuth2 fun securelyGreet (): ContractRoute = \"/greet\" / Path . of ( \"name\" ) meta { security = BasicAuthSecurity ( \"myrealm\" , Credentials ( \"user\" , \"password\" )) } bindContract POST to { name -> { _ : Request -> Response ( OK ). body ( \"hello $ name \" ) } } // 3. this route uses a query lens to extract a parameter from the query string. we add the lens to the contract metadata fun copy (): ContractRoute { val times = Query . int (). required ( \"times\" ) return \"/copy\" meta { // register the query lens with the contract queries += times } bindContract POST to { req : Request -> // extract the value from the request using the lens val copies : Int = times ( req ) Response ( OK ). body ( req . bodyString (). repeat ( copies )) } } // 4. echoing JSON fun echoJson (): ContractRoute { data class NameAndMessage ( val name : String , val message : String ) // the body lens here is imported as an extension function from the Jackson instance val body = Body . auto < NameAndMessage > (). toLens () return \"/echoJson\" meta { // register the receiving and returning lenses - these also set the content type receiving ( body ) returning ( OK , body to NameAndMessage ( \"jim\" , \"hello!\" )) } bindContract POST to { req : Request -> val input : NameAndMessage = body ( req ) // we can inject the type directly into the response using either... Response ( OK ). with ( body of input ) // ... or the more convenient... (note that json() is an extension function from the Jackson instance) Response ( OK ). json ( input ) } } // this route has a callback registered, so can be used when processes have asynchronous updates // they will be POSTed back to callbackUrl received in the request fun routeWithCallback (): ContractRoute { data class StartProcess ( val callbackUrl : Uri ) val body = Body . auto < StartProcess > (). toLens () val spec = \"/callback\" meta { summary = \"kick off a process with an async callback\" // register the callback for later updates. The syntax of the callback URL comes // from the OpenApi spec callback ( \"update\" ) { \"\"\"{ ${ \" $ \" } request.body#/callbackUrl}\"\"\" meta { receiving ( body to StartProcess ( Uri . of ( \"http://caller\" ))) } bindCallback POST } } bindContract POST val echo : HttpHandler = { request : Request -> println ( body ( request )) Response ( OK ) } return spec to echo } // Combine the Routes into a contract and bind to a context, defining a renderer (in this example // OpenApi/Swagger) and a global security model (in this case an API-Key): val contract = contract { renderer = OpenApi3 ( ApiInfo ( \"My great API\" , \"v1.0\" ), Jackson ) descriptionPath = \"/openapi.json\" security = ApiKeySecurity ( Query . required ( \"api_key\" ), { it . isNotEmpty () }) routes += echo () routes += echoJson () routes += copy () routes += securelyGreet () routes += routeWithCallback () } val handler : HttpHandler = routes ( \"/api/v1\" bind contract ) // by default, the OpenAPI docs live at the root of the contract context, but we can override it.. fun main () { println ( handler ( Request ( GET , \"https://localhost:10000/api/v1/openapi.json\" ))) } When launched, OpenApi format documentation (including JSON schema models) can be found at the route of the module. For a more extended example, see the following example apps: TDD'd example application Todo backend (typesafe contract version) Naming of JSON Schema models \u00b6 There are currently 2 options for JSON schema generation. OpenApi v2 & v3: The standard mechanism can be used with any of the supported http4k JSON modules. It generates anonymous JSON schema definition names that are then listed in the schema section of the OpenApi docs. OpenApi3 ( ApiInfo ( \"title\" , \"1.2\" , \"module description\" ), Argo ) ... generates definitions like the following in the schema definitions: { \"components\" : { \"schemas\" : { \"object1283926341\" : { \"type\" : \"object\" , \"properties\" : { \"aString\" : { \"type\" : \"string\" } } } } } } OpenApi v3 only: By including a supported Auto-JSON marshalling module on the classpath (currently only http4k-format-jackson ), the names of the definitions are generated based on the Kotlin class instances provided to the Contract Route DSL. Note that an overloaded OpenApi function automatically provides the default Jackson instance, so we can remove it from the renderer creation: OpenApi3 ( ApiInfo ( \"title\" , \"1.2\" , \"module description\" ), Jackson ) ... generates definitions like the following in the schema definitions: { \"components\" :{ \"schemas\" :{ \"ArbObject\" : { \"properties\" : { \"uri\" : { \"example\" : \"http://foowang\" , \"type\" : \"string\" } }, \"example\" : { \"uri\" : \"http://foowang\" }, \"type\" : \"object\" , \"required\" : [ \"uri\" ] } } } } Receiving Binary content with http4k Contracts (application/octet-stream or multipart etc) \u00b6 With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. This is possible by instructing http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) }","title":"Typesafe contracts (OpenAPI3)"},{"location":"guide/reference/contracts/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-contract\" ) implementation ( \"org.http4k:http4k-format-\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/contracts/#about","text":"The http4k-contract module adds a much more sophisticated routing mechanism to that available in http4k-core . It adds the facility to declare server-side Routes in a completely typesafe way, leveraging the Lens functionality from the core. These Routes are combined into Contracts , which have the following features: Auto-validating - the Route contract is automatically validated on each call for required-fields and type conversions, removing the requirement for any validation code to be written by the API user. Invalid calls result in a HTTP 400 (BAD_REQUEST) response. Self-describing: - a generated endpoint is provided which describes all of the Routes in that module. Implementations include OpenApi v2 & v3 documentation, including generation of JSON schema . These documents can then be used to generate HTTP client and server code in various languages using the OpenAPI generator . models for messages. Security: to secure the Routes against unauthorised access. Current implementations include ApiKey , BasicAuth , BearerAuth , OpenIdConnect and OAuth . Callbacks and Webhooks can be declared, which give the same level of documentation and model generation","title":"About"},{"location":"guide/reference/contracts/#code","text":"package guide.reference.contracts // for this example we're using Jackson - note that the auto method imported is an extension // function that is defined on the Jackson instance import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.bind import org.http4k.contract.bindCallback import org.http4k.contract.contract import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.ApiKeySecurity import org.http4k.contract.security.BasicAuthSecurity import org.http4k.contract.ui.swaggerUiLite import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.I_M_A_TEAPOT import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.with import org.http4k.format.Jackson import org.http4k.format.Jackson.auto import org.http4k.format.Klaxon.json import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer // 1. this route has no dynamic path segments and simply echoes the body back. We are also adding various metadata // to the route, which will be used in the OpenAPI documentation. All of the metadata is optional. fun echo (): ContractRoute = \"/echo\" meta { summary = \"echoes the body back\" description = \"This is a simple route which echoes the body back to the caller\" tags += Tag ( \"env\" , \"production\" ) consumes += TEXT_PLAIN produces += TEXT_PLAIN returning ( OK , I_M_A_TEAPOT ) } bindContract POST to { req : Request -> Response ( OK ). body ( req . body ) } // 2. this route has a dynamic path which is automatically injected into the handler and security applied. There // are various security instances, from Basic to APIKey to OAuth2 fun securelyGreet (): ContractRoute = \"/greet\" / Path . of ( \"name\" ) meta { security = BasicAuthSecurity ( \"myrealm\" , Credentials ( \"user\" , \"password\" )) } bindContract POST to { name -> { _ : Request -> Response ( OK ). body ( \"hello $ name \" ) } } // 3. this route uses a query lens to extract a parameter from the query string. we add the lens to the contract metadata fun copy (): ContractRoute { val times = Query . int (). required ( \"times\" ) return \"/copy\" meta { // register the query lens with the contract queries += times } bindContract POST to { req : Request -> // extract the value from the request using the lens val copies : Int = times ( req ) Response ( OK ). body ( req . bodyString (). repeat ( copies )) } } // 4. echoing JSON fun echoJson (): ContractRoute { data class NameAndMessage ( val name : String , val message : String ) // the body lens here is imported as an extension function from the Jackson instance val body = Body . auto < NameAndMessage > (). toLens () return \"/echoJson\" meta { // register the receiving and returning lenses - these also set the content type receiving ( body ) returning ( OK , body to NameAndMessage ( \"jim\" , \"hello!\" )) } bindContract POST to { req : Request -> val input : NameAndMessage = body ( req ) // we can inject the type directly into the response using either... Response ( OK ). with ( body of input ) // ... or the more convenient... (note that json() is an extension function from the Jackson instance) Response ( OK ). json ( input ) } } // this route has a callback registered, so can be used when processes have asynchronous updates // they will be POSTed back to callbackUrl received in the request fun routeWithCallback (): ContractRoute { data class StartProcess ( val callbackUrl : Uri ) val body = Body . auto < StartProcess > (). toLens () val spec = \"/callback\" meta { summary = \"kick off a process with an async callback\" // register the callback for later updates. The syntax of the callback URL comes // from the OpenApi spec callback ( \"update\" ) { \"\"\"{ ${ \" $ \" } request.body#/callbackUrl}\"\"\" meta { receiving ( body to StartProcess ( Uri . of ( \"http://caller\" ))) } bindCallback POST } } bindContract POST val echo : HttpHandler = { request : Request -> println ( body ( request )) Response ( OK ) } return spec to echo } // Combine the Routes into a contract and bind to a context, defining a renderer (in this example // OpenApi/Swagger) and a global security model (in this case an API-Key): val contract = contract { renderer = OpenApi3 ( ApiInfo ( \"My great API\" , \"v1.0\" ), Jackson ) descriptionPath = \"/openapi.json\" security = ApiKeySecurity ( Query . required ( \"api_key\" ), { it . isNotEmpty () }) routes += echo () routes += echoJson () routes += copy () routes += securelyGreet () routes += routeWithCallback () } val handler : HttpHandler = routes ( \"/api/v1\" bind contract ) // by default, the OpenAPI docs live at the root of the contract context, but we can override it.. fun main () { println ( handler ( Request ( GET , \"https://localhost:10000/api/v1/openapi.json\" ))) } When launched, OpenApi format documentation (including JSON schema models) can be found at the route of the module. For a more extended example, see the following example apps: TDD'd example application Todo backend (typesafe contract version)","title":"Code"},{"location":"guide/reference/contracts/#naming_of_json_schema_models","text":"There are currently 2 options for JSON schema generation. OpenApi v2 & v3: The standard mechanism can be used with any of the supported http4k JSON modules. It generates anonymous JSON schema definition names that are then listed in the schema section of the OpenApi docs. OpenApi3 ( ApiInfo ( \"title\" , \"1.2\" , \"module description\" ), Argo ) ... generates definitions like the following in the schema definitions: { \"components\" : { \"schemas\" : { \"object1283926341\" : { \"type\" : \"object\" , \"properties\" : { \"aString\" : { \"type\" : \"string\" } } } } } } OpenApi v3 only: By including a supported Auto-JSON marshalling module on the classpath (currently only http4k-format-jackson ), the names of the definitions are generated based on the Kotlin class instances provided to the Contract Route DSL. Note that an overloaded OpenApi function automatically provides the default Jackson instance, so we can remove it from the renderer creation: OpenApi3 ( ApiInfo ( \"title\" , \"1.2\" , \"module description\" ), Jackson ) ... generates definitions like the following in the schema definitions: { \"components\" :{ \"schemas\" :{ \"ArbObject\" : { \"properties\" : { \"uri\" : { \"example\" : \"http://foowang\" , \"type\" : \"string\" } }, \"example\" : { \"uri\" : \"http://foowang\" }, \"type\" : \"object\" , \"required\" : [ \"uri\" ] } } } }","title":"Naming of JSON Schema models"},{"location":"guide/reference/contracts/#receiving_binary_content_with_http4k_contracts_applicationoctet-stream_or_multipart_etc","text":"With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. This is possible by instructing http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) }","title":"Receiving Binary content with http4k Contracts (application/octet-stream or multipart etc)"},{"location":"guide/reference/core/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) } About \u00b6 Apart from Kotlin StdLib, the core module has ZERO dependencies and provides the following: Immutable versions of the HTTP spec objects (Request, Response, Cookies etc). HTTP handler and filter abstractions which models services as simple, composable functions. Simple routing implementation, plus HttpHandlerServlet to enable plugging into any Servlet engine. Lens mechanism for typesafe destructuring and construction of HTTP messages. Typesafe Request Context operations using Lenses. Abstractions for Servers, Clients, JSON Message formats, Templating, Websockets etc. SunHttp Ultra-fast single-LOC development server-backend Static file-serving capability with Caching and Hot-Reload Single Page App routing for React and co. See how-to guides for an example. Bundled WebJars routing - activate in single-LOC. See the how-to guides for an example. APIs to record and replay HTTP traffic to disk or memory HttpHandlers \u00b6 In http4k, an HTTP service is just a typealias of a simple function: typealias HttpHandler = ( Request ) -> Response First described in this Twitter paper \"Your Server as a Function\" , this abstraction allows us lots of flexibility in a language like Kotlin, since the conceptual barrier to service construction is reduced to effectively nil. Here is the simplest example - note that we don't need any special infrastructure to create an HttpHandler , neither do we need to launch a real HTTP container to exercise it: val handler = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val get = Request ( Method . GET , \"/\" ). query ( \"name\" , \"John Doe\" ) val response = handler ( get ) println ( response . status ) println ( response . bodyString ()) To mount the HttpHandler in a container, the can simply be converted to a Servlet by calling handler.asServlet() Filters \u00b6 Filters add extra processing to either the Request or Response. In http4k, they are modelled as: interface Filter : ( HttpHandler ) -> HttpHandler Filters are designed to simply compose together (using then() ) , creating reusable stacks of behaviour which can then be applied to any HttpHandler . For example, to add Basic Auth and latency reporting to a service: val handler = { _ : Request -> Response ( OK ) } val myFilter = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } val latencyAndBasicAuth : Filter = ServerFilters . BasicAuth ( \"my realm\" , \"user\" , \"password\" ). then ( myFilter ) val app : HttpHandler = latencyAndBasicAuth . then ( handler ) The http4k-core module comes with a set of handy Filters for application to both Server and Client HttpHandlers , covering common things like: Request tracing headers (x-b3-traceid etc) Basic Auth Cache Control CORS Cookie handling Compression and un-compression Cross-cutting concerns like logging, exception handling Debugging request and responses Check out the org.http4k.filter package for the exact list. Testing Filters \u00b6 package guide.reference.core import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.hamkrest.hasHeader import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.Test val AddLatency = Filter { next -> { next ( it ). header ( \"x-extra-header\" , \"some value\" ) } } class FilterTest { @Test fun `adds a special header` () { val handler : HttpHandler = AddLatency . then { Response ( OK ) } val response : Response = handler ( Request ( GET , \"/echo/my+great+message\" )) assertThat ( response , hasStatus ( OK ). and ( hasHeader ( \"x-extra-header\" , \"some value\" ))) } } Routers - Nestable, path-based Routing \u00b6 Create a Router using routes() to bind a static or dynamic path to either an HttpHandler, or to another sub-Router. These Routers can be nested infinitely deep and http4k will search for a matching route using a depth-first search algorithm, before falling back finally to a 404: routes ( \"/hello\" bind routes ( \"/{name:.*}\" bind GET to { request : Request -> Response ( OK ). body ( \"Hello, ${ request . path ( \" name \" ) } !\" ) } ), \"/fail\" bind POST to { request : Request -> Response ( INTERNAL_SERVER_ERROR ) } ). asServer ( Jetty ( 8000 )). start () Note that the http4k-contract module contains a more typesafe implementation of routing functionality, with runtime-generated live documentation in OpenApi format. Testing Routers \u00b6 package guide.testing import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.junit.jupiter.api.Test val EchoPath = \"/echo/{message}\" bind GET to { r -> Response ( OK ). body ( r . path ( \"message\" ) ?: \"nothing!\" ) } class DynamicPathTest { @Test fun `echoes body from path` () { val route : RoutingHttpHandler = routes ( EchoPath ) val response : Response = route ( Request ( GET , \"/echo/my%20great%20message\" )) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"my great message\" ))) } } Typesafe parameter destructuring/construction of HTTP messages with Lenses \u00b6 Getting values from HTTP messages is one thing, but we want to ensure that those values are both present and valid. For this purpose, we can use a Lens . A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional). To utilise a lens, first you have to declare it with the form .. . There is one \"location\" type for each part of the message, each with config/mapping operations which are specific to that location: Location Starting type Applicable to Multiplicity Requirement terminator Examples Query String Request Singular or multiple Optional or Required Query.optional(\"name\") Query.required(\"name\") Query.int().required(\"name\") Query.localDate().multi.required(\"name\") Query.map(::CustomType, { it.value }).required(\"name\") Header String Request or Response Singular or multiple Optional or Required Header.optional(\"name\") Header.required(\"name\") Header.int().required(\"name\") Header.localDate().multi.required(\"name\") Header.map(::CustomType, { it.value }).required(\"name\") Path String Request Singular Required Path.of(\"name\") Path.int().of(\"name\") Path.map(::CustomType, { it.value }).of(\"name\") FormField String WebForm Singular or multiple Optional or Required FormField.optional(\"name\") FormField.required(\"name\") FormField.int().required(\"name\") FormField.localDate().multi.required(\"name\") FormField.map(::CustomType, { it.value }).required(\"name\") Body ByteBuffer Request or Response Singular Required Body.string(ContentType.TEXT_PLAIN).toLens() Body.json().toLens() Body.webForm(Validator.Strict, FormField.required(\"name\")).toLens() Once the lens is declared, you can use it on a target object to either get or set the value: Retrieving a value: use .extract() , or the more concise invoke form: () Setting a value: use .inject(, ) , or the more concise invoke form: (, ) Code \u00b6 package guide.reference.core import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.localDate import org.http4k.lens.nonEmptyString import org.http4k.lens.string import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import java.time.LocalDate val pathLocalDate = Path . localDate (). of ( \"date\" ) val requiredQuery = Query . required ( \"myQueryName\" ) val nonEmptyQuery = Query . nonEmptyString (). required ( \"myNonEmptyQuery\" ) val optionalHeader = Header . int (). optional ( \"Content-Length\" ) val responseBody = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Most of the useful common JDK types are covered. However, if we want to use our own types, we can just use `map()` data class CustomType ( val value : String ) val requiredCustomQuery = Query . map ( :: CustomType , { it . value }). required ( \"myCustomType\" ) //To use the Lens, simply `invoke() or extract()` it using an HTTP message to extract the value, or alternatively `invoke() or inject()` it with the value if we are modifying (via copy) the message: val handler : RoutingHttpHandler = routes ( \"/hello/{date:.*}\" bind GET to { request : Request -> val pathDate : LocalDate = pathLocalDate ( request ) // SAME AS: // val pathDate: LocalDate = pathLocalDate.extract(request) val customType : CustomType = requiredCustomQuery ( request ) val anIntHeader : Int? = optionalHeader ( request ) val baseResponse = Response ( OK ) val responseWithHeader = optionalHeader ( anIntHeader , baseResponse ) // SAME AS: // val responseWithHeader = optionalHeader.inject(anIntHeader, baseResponse) responseBody ( \"you sent $ pathDate and $ customType \" , responseWithHeader ) } ) //With the addition of the `CatchLensFailure` filter, no other validation is required when using Lenses, as http4k will handle invalid requests by returning a BAD_REQUEST (400) response. val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" ) ) //More conveniently for construction of HTTP messages, multiple lenses can be used at once to modify a message, which is useful for properly building both requests and responses in a typesafe way without resorting to string values (especially in URLs which should never be constructed using String concatenation): val modifiedRequest : Request = Request ( GET , \"http://google.com/{pathLocalDate}\" ). with ( pathLocalDate of LocalDate . now (), requiredQuery of \"myAmazingString\" , optionalHeader of 123 ) Serving static assets \u00b6 For serving static assets, just bind a path to a Static block as below, using either a Classpath or Directory (Hot reloading) based ResourceLoader instance (find these on the ResourceLoader companion object). Typically, Directory is used during development and the Classpath strategy is used to serve assets in production from an UberJar. This is usually based on a \"devmode\" flag when constructing your app\". Note that you should avoid setting the Classpath value to the root because otherwise it will serve anything from your classpath (including Java class files!)!: routes ( \"/static\" bind static ( Classpath ( \"/org/http4k/some/package/name\" )), \"/hotreload\" bind static ( Directory ( \"path/to/static/dir/goes/here\" )) ) Single Page Apps \u00b6 These can be easily activated as below, and default to serving from /public package: routes ( \"/reference/api\" bind { Response ( OK ). body ( \"some api content\" ) }, singlePageApp () ) Typesafe Websockets. \u00b6 Websockets have been modeled using the same methodology as standard HTTP endpoints - ie. with both simplicity and testability as a first class concern, as well as benefiting from Lens-based typesafety. Websocket communication consists of 3 main concepts: WsHandler - represented as a typealias: WsHandler = (Request) -> WsResponse . This is responsible for matching an HTTP request to a websocket. WsConsumer - represented as a typealias: WsConsumer = (WebSocket) -> Unit . This function is called on connection of a websocket and allow the API user to react to events coming from the connected websocket. WsMessage - a message which is sent or received on a websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. Just like the http4k HTTP message model, WsMessages are immutable data classes. The routing aspect of Websockets is done using a very similar API to the standard HTTP routing for HTTP messages and dynamic parts of the upgrade request are available when constructing a websocket instance: import java.nio.file.Pathdata class Wrapper ( val value : String ) val body = WsMessage . string (). map ( :: Wrapper , Wrapper :: value ). toLens () val nameLens = Path . of ( \"name\" ) val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . send ( WsMessage ( \"hello $ name \" )) ws . onMessage { val received = body ( it ) ws . send ( body ( received )) } ws . onClose { println ( \"closed\" ) } } } ) ) A WsHandler can be combined with an HttpHandler into a PolyHandler and then mounted into a supported backend server using asServer() : val app = PolyHandler ( routes ( \"/\" bind { r : Request -> Response ( OK ) } ), websockets ( \"/ws\" bind { req : Request -> WsResponse { ws : Websocket -> ws . send ( WsMessage ( \"hello!\" )) } } ) ) app . asServer ( Jetty ( 9000 )). start () Alternatively, the WsHandler can be also converted to a synchronous WsClient - this allows testing to be done completely offline, which allows for super-fast tests: val client = app . testWsClient ( Request ( Method . GET , \"ws://localhost:9000/hello/bob\" )) !! client . send ( WsMessage ( \"1\" )) client . close ( Status ( 200 , \"bob\" )) client . received . take ( 2 ). forEach ( :: println ) Request and Response toString() \u00b6 The HttpMessages used by http4k toString in the HTTP wire format, which it simple to capture and replay HTTP message streams later in a similar way to tools like Mountebank . CURL format \u00b6 Creates curl command for a given request - this is useful to include in audit logs so exact requests can be replayed if required: val curl = Request ( POST , \"http://httpbin.org/post\" ). body ( listOf ( \"foo\" to \"bar\" ). toBody ()). toCurl () // curl -X POST --data \"foo=bar\" \"http://httpbin.org/post\"","title":"Core"},{"location":"guide/reference/core/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/core/#about","text":"Apart from Kotlin StdLib, the core module has ZERO dependencies and provides the following: Immutable versions of the HTTP spec objects (Request, Response, Cookies etc). HTTP handler and filter abstractions which models services as simple, composable functions. Simple routing implementation, plus HttpHandlerServlet to enable plugging into any Servlet engine. Lens mechanism for typesafe destructuring and construction of HTTP messages. Typesafe Request Context operations using Lenses. Abstractions for Servers, Clients, JSON Message formats, Templating, Websockets etc. SunHttp Ultra-fast single-LOC development server-backend Static file-serving capability with Caching and Hot-Reload Single Page App routing for React and co. See how-to guides for an example. Bundled WebJars routing - activate in single-LOC. See the how-to guides for an example. APIs to record and replay HTTP traffic to disk or memory","title":"About"},{"location":"guide/reference/core/#httphandlers","text":"In http4k, an HTTP service is just a typealias of a simple function: typealias HttpHandler = ( Request ) -> Response First described in this Twitter paper \"Your Server as a Function\" , this abstraction allows us lots of flexibility in a language like Kotlin, since the conceptual barrier to service construction is reduced to effectively nil. Here is the simplest example - note that we don't need any special infrastructure to create an HttpHandler , neither do we need to launch a real HTTP container to exercise it: val handler = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val get = Request ( Method . GET , \"/\" ). query ( \"name\" , \"John Doe\" ) val response = handler ( get ) println ( response . status ) println ( response . bodyString ()) To mount the HttpHandler in a container, the can simply be converted to a Servlet by calling handler.asServlet()","title":"HttpHandlers"},{"location":"guide/reference/core/#filters","text":"Filters add extra processing to either the Request or Response. In http4k, they are modelled as: interface Filter : ( HttpHandler ) -> HttpHandler Filters are designed to simply compose together (using then() ) , creating reusable stacks of behaviour which can then be applied to any HttpHandler . For example, to add Basic Auth and latency reporting to a service: val handler = { _ : Request -> Response ( OK ) } val myFilter = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } val latencyAndBasicAuth : Filter = ServerFilters . BasicAuth ( \"my realm\" , \"user\" , \"password\" ). then ( myFilter ) val app : HttpHandler = latencyAndBasicAuth . then ( handler ) The http4k-core module comes with a set of handy Filters for application to both Server and Client HttpHandlers , covering common things like: Request tracing headers (x-b3-traceid etc) Basic Auth Cache Control CORS Cookie handling Compression and un-compression Cross-cutting concerns like logging, exception handling Debugging request and responses Check out the org.http4k.filter package for the exact list.","title":"Filters"},{"location":"guide/reference/core/#testing_filters","text":"package guide.reference.core import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.hamkrest.hasHeader import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.Test val AddLatency = Filter { next -> { next ( it ). header ( \"x-extra-header\" , \"some value\" ) } } class FilterTest { @Test fun `adds a special header` () { val handler : HttpHandler = AddLatency . then { Response ( OK ) } val response : Response = handler ( Request ( GET , \"/echo/my+great+message\" )) assertThat ( response , hasStatus ( OK ). and ( hasHeader ( \"x-extra-header\" , \"some value\" ))) } }","title":"Testing Filters"},{"location":"guide/reference/core/#routers_-_nestable_path-based_routing","text":"Create a Router using routes() to bind a static or dynamic path to either an HttpHandler, or to another sub-Router. These Routers can be nested infinitely deep and http4k will search for a matching route using a depth-first search algorithm, before falling back finally to a 404: routes ( \"/hello\" bind routes ( \"/{name:.*}\" bind GET to { request : Request -> Response ( OK ). body ( \"Hello, ${ request . path ( \" name \" ) } !\" ) } ), \"/fail\" bind POST to { request : Request -> Response ( INTERNAL_SERVER_ERROR ) } ). asServer ( Jetty ( 8000 )). start () Note that the http4k-contract module contains a more typesafe implementation of routing functionality, with runtime-generated live documentation in OpenApi format.","title":"Routers - Nestable, path-based Routing"},{"location":"guide/reference/core/#testing_routers","text":"package guide.testing import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.junit.jupiter.api.Test val EchoPath = \"/echo/{message}\" bind GET to { r -> Response ( OK ). body ( r . path ( \"message\" ) ?: \"nothing!\" ) } class DynamicPathTest { @Test fun `echoes body from path` () { val route : RoutingHttpHandler = routes ( EchoPath ) val response : Response = route ( Request ( GET , \"/echo/my%20great%20message\" )) assertThat ( response , hasStatus ( OK ). and ( hasBody ( \"my great message\" ))) } }","title":"Testing Routers"},{"location":"guide/reference/core/#typesafe_parameter_destructuringconstruction_of_http_messages_with_lenses","text":"Getting values from HTTP messages is one thing, but we want to ensure that those values are both present and valid. For this purpose, we can use a Lens . A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional). To utilise a lens, first you have to declare it with the form .. . There is one \"location\" type for each part of the message, each with config/mapping operations which are specific to that location: Location Starting type Applicable to Multiplicity Requirement terminator Examples Query String Request Singular or multiple Optional or Required Query.optional(\"name\") Query.required(\"name\") Query.int().required(\"name\") Query.localDate().multi.required(\"name\") Query.map(::CustomType, { it.value }).required(\"name\") Header String Request or Response Singular or multiple Optional or Required Header.optional(\"name\") Header.required(\"name\") Header.int().required(\"name\") Header.localDate().multi.required(\"name\") Header.map(::CustomType, { it.value }).required(\"name\") Path String Request Singular Required Path.of(\"name\") Path.int().of(\"name\") Path.map(::CustomType, { it.value }).of(\"name\") FormField String WebForm Singular or multiple Optional or Required FormField.optional(\"name\") FormField.required(\"name\") FormField.int().required(\"name\") FormField.localDate().multi.required(\"name\") FormField.map(::CustomType, { it.value }).required(\"name\") Body ByteBuffer Request or Response Singular Required Body.string(ContentType.TEXT_PLAIN).toLens() Body.json().toLens() Body.webForm(Validator.Strict, FormField.required(\"name\")).toLens() Once the lens is declared, you can use it on a target object to either get or set the value: Retrieving a value: use .extract() , or the more concise invoke form: () Setting a value: use .inject(, ) , or the more concise invoke form: (, )","title":"Typesafe parameter destructuring/construction of HTTP messages with Lenses"},{"location":"guide/reference/core/#code","text":"package guide.reference.core import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ServerFilters import org.http4k.lens.Header import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.lens.localDate import org.http4k.lens.nonEmptyString import org.http4k.lens.string import org.http4k.routing.RoutingHttpHandler import org.http4k.routing.bind import org.http4k.routing.routes import java.time.LocalDate val pathLocalDate = Path . localDate (). of ( \"date\" ) val requiredQuery = Query . required ( \"myQueryName\" ) val nonEmptyQuery = Query . nonEmptyString (). required ( \"myNonEmptyQuery\" ) val optionalHeader = Header . int (). optional ( \"Content-Length\" ) val responseBody = Body . string ( ContentType . TEXT_PLAIN ). toLens () // Most of the useful common JDK types are covered. However, if we want to use our own types, we can just use `map()` data class CustomType ( val value : String ) val requiredCustomQuery = Query . map ( :: CustomType , { it . value }). required ( \"myCustomType\" ) //To use the Lens, simply `invoke() or extract()` it using an HTTP message to extract the value, or alternatively `invoke() or inject()` it with the value if we are modifying (via copy) the message: val handler : RoutingHttpHandler = routes ( \"/hello/{date:.*}\" bind GET to { request : Request -> val pathDate : LocalDate = pathLocalDate ( request ) // SAME AS: // val pathDate: LocalDate = pathLocalDate.extract(request) val customType : CustomType = requiredCustomQuery ( request ) val anIntHeader : Int? = optionalHeader ( request ) val baseResponse = Response ( OK ) val responseWithHeader = optionalHeader ( anIntHeader , baseResponse ) // SAME AS: // val responseWithHeader = optionalHeader.inject(anIntHeader, baseResponse) responseBody ( \"you sent $ pathDate and $ customType \" , responseWithHeader ) } ) //With the addition of the `CatchLensFailure` filter, no other validation is required when using Lenses, as http4k will handle invalid requests by returning a BAD_REQUEST (400) response. val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" ) ) //More conveniently for construction of HTTP messages, multiple lenses can be used at once to modify a message, which is useful for properly building both requests and responses in a typesafe way without resorting to string values (especially in URLs which should never be constructed using String concatenation): val modifiedRequest : Request = Request ( GET , \"http://google.com/{pathLocalDate}\" ). with ( pathLocalDate of LocalDate . now (), requiredQuery of \"myAmazingString\" , optionalHeader of 123 )","title":"Code"},{"location":"guide/reference/core/#serving_static_assets","text":"For serving static assets, just bind a path to a Static block as below, using either a Classpath or Directory (Hot reloading) based ResourceLoader instance (find these on the ResourceLoader companion object). Typically, Directory is used during development and the Classpath strategy is used to serve assets in production from an UberJar. This is usually based on a \"devmode\" flag when constructing your app\". Note that you should avoid setting the Classpath value to the root because otherwise it will serve anything from your classpath (including Java class files!)!: routes ( \"/static\" bind static ( Classpath ( \"/org/http4k/some/package/name\" )), \"/hotreload\" bind static ( Directory ( \"path/to/static/dir/goes/here\" )) )","title":"Serving static assets"},{"location":"guide/reference/core/#single_page_apps","text":"These can be easily activated as below, and default to serving from /public package: routes ( \"/reference/api\" bind { Response ( OK ). body ( \"some api content\" ) }, singlePageApp () )","title":"Single Page Apps"},{"location":"guide/reference/core/#typesafe_websockets","text":"Websockets have been modeled using the same methodology as standard HTTP endpoints - ie. with both simplicity and testability as a first class concern, as well as benefiting from Lens-based typesafety. Websocket communication consists of 3 main concepts: WsHandler - represented as a typealias: WsHandler = (Request) -> WsResponse . This is responsible for matching an HTTP request to a websocket. WsConsumer - represented as a typealias: WsConsumer = (WebSocket) -> Unit . This function is called on connection of a websocket and allow the API user to react to events coming from the connected websocket. WsMessage - a message which is sent or received on a websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. Just like the http4k HTTP message model, WsMessages are immutable data classes. The routing aspect of Websockets is done using a very similar API to the standard HTTP routing for HTTP messages and dynamic parts of the upgrade request are available when constructing a websocket instance: import java.nio.file.Pathdata class Wrapper ( val value : String ) val body = WsMessage . string (). map ( :: Wrapper , Wrapper :: value ). toLens () val nameLens = Path . of ( \"name\" ) val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . send ( WsMessage ( \"hello $ name \" )) ws . onMessage { val received = body ( it ) ws . send ( body ( received )) } ws . onClose { println ( \"closed\" ) } } } ) ) A WsHandler can be combined with an HttpHandler into a PolyHandler and then mounted into a supported backend server using asServer() : val app = PolyHandler ( routes ( \"/\" bind { r : Request -> Response ( OK ) } ), websockets ( \"/ws\" bind { req : Request -> WsResponse { ws : Websocket -> ws . send ( WsMessage ( \"hello!\" )) } } ) ) app . asServer ( Jetty ( 9000 )). start () Alternatively, the WsHandler can be also converted to a synchronous WsClient - this allows testing to be done completely offline, which allows for super-fast tests: val client = app . testWsClient ( Request ( Method . GET , \"ws://localhost:9000/hello/bob\" )) !! client . send ( WsMessage ( \"1\" )) client . close ( Status ( 200 , \"bob\" )) client . received . take ( 2 ). forEach ( :: println )","title":"Typesafe Websockets."},{"location":"guide/reference/core/#request_and_response_tostring","text":"The HttpMessages used by http4k toString in the HTTP wire format, which it simple to capture and replay HTTP message streams later in a similar way to tools like Mountebank .","title":"Request and Response toString()"},{"location":"guide/reference/core/#curl_format","text":"Creates curl command for a given request - this is useful to include in audit logs so exact requests can be replayed if required: val curl = Request ( POST , \"http://httpbin.org/post\" ). body ( listOf ( \"foo\" to \"bar\" ). toBody ()). toCurl () // curl -X POST --data \"foo=bar\" \"http://httpbin.org/post\"","title":"CURL format"},{"location":"guide/reference/dataframe/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-dataframe\" ) } About \u00b6 This module adds the ability to use Kotlin DataFrames as a first-class citizen when reading from HTTP messages. Extraction from the HTTP message body is done automatically when using a lens with a DataFrame type. Code \u00b6 package guide.reference.dataframe import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.dataframe.CSV import org.http4k.format.dataframe.dataFrame import org.http4k.format.dataframe.dataFrameCsv import org.http4k.server.SunHttp import org.http4k.server.asServer import org.jetbrains.kotlinx.dataframe.api.filter fun main () { // define a simple CSV endpoint val app = { _ : Request -> Response ( OK ). body ( \"\"\" full_name,html_url,stargazers_count http4k,https://http4k.org,100 http4k-connect,https://http4k-connect.org,10 forkhandles,https://forkhandles.dev,20 \"\"\" . trimIndent () ) } app . asServer ( SunHttp ( 8000 )). start () val response = JavaHttpClient ()( Request ( GET , \"http://localhost:8000\" )) // load all the data into an untyped DataFrame val frame = response . dataFrame ( CSV ()) // filter all projects starting with \"h\" val all_h_projects = frame . filter { it [ \"full_name\" ]?. toString () ?. startsWith ( \"h\" ) ?: false } println ( all_h_projects ) // you can also use the Kotlin KSP DataFrame plugin to generate typed data classes. // Then use the `dataFrameCsv` extension function to cast the DataFrame to a typed DataFrame // this is a fake class - we would the generated one in a real project and to manipulate // the dataframe as above data class Repository ( val fullName : String ) println ( response . dataFrameCsv < Repository > ()) }","title":"DataFrame"},{"location":"guide/reference/dataframe/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-dataframe\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/dataframe/#about","text":"This module adds the ability to use Kotlin DataFrames as a first-class citizen when reading from HTTP messages. Extraction from the HTTP message body is done automatically when using a lens with a DataFrame type.","title":"About"},{"location":"guide/reference/dataframe/#code","text":"package guide.reference.dataframe import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.dataframe.CSV import org.http4k.format.dataframe.dataFrame import org.http4k.format.dataframe.dataFrameCsv import org.http4k.server.SunHttp import org.http4k.server.asServer import org.jetbrains.kotlinx.dataframe.api.filter fun main () { // define a simple CSV endpoint val app = { _ : Request -> Response ( OK ). body ( \"\"\" full_name,html_url,stargazers_count http4k,https://http4k.org,100 http4k-connect,https://http4k-connect.org,10 forkhandles,https://forkhandles.dev,20 \"\"\" . trimIndent () ) } app . asServer ( SunHttp ( 8000 )). start () val response = JavaHttpClient ()( Request ( GET , \"http://localhost:8000\" )) // load all the data into an untyped DataFrame val frame = response . dataFrame ( CSV ()) // filter all projects starting with \"h\" val all_h_projects = frame . filter { it [ \"full_name\" ]?. toString () ?. startsWith ( \"h\" ) ?: false } println ( all_h_projects ) // you can also use the Kotlin KSP DataFrame plugin to generate typed data classes. // Then use the `dataFrameCsv` extension function to cast the DataFrame to a typed DataFrame // this is a fake class - we would the generated one in a real project and to manipulate // the dataframe as above data class Repository ( val fullName : String ) println ( response . dataFrameCsv < Repository > ()) }","title":"Code"},{"location":"guide/reference/digest/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-security-digest\" ) } About \u00b6 Support for integrating with servers secured by Digest authentication; useful for working with legacy servers or IOT devices that don't typically support TLS. For completeness, a Digest Provider has also been included for use with servers. Digest authentication is useful for protecting credentials in transit when traffic isn't encrypted. Instead of the client transmitting plain-text or encrypted credentials, it sends a hash of the credentials instead; this ensures a man-in-the-middle can never intercept the credentials, despite the connection being insecure. Despite being made redundant by TLS, digest authentication has a major disadvantage; it typically requires user credentials to be accessible by the server. In most other authentication mechanisms, the server can store a non-reversible hash, which reduces the severity of a database breach. At it's most basic, the digest authentication flow works like this: Client makes an HTTP call to a server protected by Digest authentication The server responds with an HTTP 401 , including a Digest challenge in the WWW-Authenticate header. This header includes all the information the client needs to correctly generate a credentials hash With the user-supplied credentials, the client converts them into hash and encodes them as a hexadecimal digest , then transmits them to the server, along with the plaintext username With the username given by the client, the server looks up the password for that user, generates the expected hash , and compares is to the one supplied by the client. If they match, it grants the client access to the protected resource Example Provider \u00b6 This example has an integrated username/password store; you will want to come up with your own version, with credentials encrypted at rest. The server accepts a path parameter, and parrots back the value provided by the client. package guide.reference.digest import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DigestAuth import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val users = mapOf ( \"admin\" to \"password\" , \"user\" to \"hunter2\" ) val routes = routes ( \"/hello/{name}\" bind GET to { request -> val name = request . path ( \"name\" ) Response ( OK ). body ( \"Hello $ name \" ) } ) val authFilter = ServerFilters . DigestAuth ( realm = \"http4k\" , passwordLookup = { username -> users [ username ] }) authFilter . then ( routes ) . asServer ( SunHttp ( 8000 )) . start () . block () } Example Client \u00b6 This example integrates with the provider above, sending a request with a value to be parroted back. package guide.reference.digest import org.http4k.client.JavaHttpClient import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.DigestAuth fun main () { val credentials = Credentials ( \"admin\" , \"password\" ) val client = ClientFilters . DigestAuth ( credentials ) . then ( JavaHttpClient ()) val request = Request ( GET , \"http://localhost:8000/hello/http4k\" ) val response = client ( request ) println ( response ) }","title":"Digest"},{"location":"guide/reference/digest/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-security-digest\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/digest/#about","text":"Support for integrating with servers secured by Digest authentication; useful for working with legacy servers or IOT devices that don't typically support TLS. For completeness, a Digest Provider has also been included for use with servers. Digest authentication is useful for protecting credentials in transit when traffic isn't encrypted. Instead of the client transmitting plain-text or encrypted credentials, it sends a hash of the credentials instead; this ensures a man-in-the-middle can never intercept the credentials, despite the connection being insecure. Despite being made redundant by TLS, digest authentication has a major disadvantage; it typically requires user credentials to be accessible by the server. In most other authentication mechanisms, the server can store a non-reversible hash, which reduces the severity of a database breach. At it's most basic, the digest authentication flow works like this: Client makes an HTTP call to a server protected by Digest authentication The server responds with an HTTP 401 , including a Digest challenge in the WWW-Authenticate header. This header includes all the information the client needs to correctly generate a credentials hash With the user-supplied credentials, the client converts them into hash and encodes them as a hexadecimal digest , then transmits them to the server, along with the plaintext username With the username given by the client, the server looks up the password for that user, generates the expected hash , and compares is to the one supplied by the client. If they match, it grants the client access to the protected resource","title":"About"},{"location":"guide/reference/digest/#example_provider","text":"This example has an integrated username/password store; you will want to come up with your own version, with credentials encrypted at rest. The server accepts a path parameter, and parrots back the value provided by the client. package guide.reference.digest import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.DigestAuth import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { val users = mapOf ( \"admin\" to \"password\" , \"user\" to \"hunter2\" ) val routes = routes ( \"/hello/{name}\" bind GET to { request -> val name = request . path ( \"name\" ) Response ( OK ). body ( \"Hello $ name \" ) } ) val authFilter = ServerFilters . DigestAuth ( realm = \"http4k\" , passwordLookup = { username -> users [ username ] }) authFilter . then ( routes ) . asServer ( SunHttp ( 8000 )) . start () . block () }","title":"Example Provider"},{"location":"guide/reference/digest/#example_client","text":"This example integrates with the provider above, sending a request with a value to be parroted back. package guide.reference.digest import org.http4k.client.JavaHttpClient import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.DigestAuth fun main () { val credentials = Credentials ( \"admin\" , \"password\" ) val client = ClientFilters . DigestAuth ( credentials ) . then ( JavaHttpClient ()) val request = Request ( GET , \"http://localhost:8000/hello/http4k\" ) val response = client ( request ) println ( response ) }","title":"Example Client"},{"location":"guide/reference/failsafe/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-failsafe\" ) } About \u00b6 This module provides a configurable Filter to provide fault tolerance (CircuitBreaking, RateLimiting, Retrying, Bulkheading, Timeouts etc.), by integrating with the Failsafe library. Basic example \u00b6 Here's an example that uses BulkHeading to demonstrate how easy it is to use the filter with configured Failsafe policies. package guide.reference.failsafe import dev.failsafe.Bulkhead import dev.failsafe.Failsafe import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.FailsafeFilter import kotlin.concurrent.thread fun main () { // Configure a Failsafe policy val failsafeExecutor = Failsafe . with ( Bulkhead . of < Response > ( 5 ) ) // Use the filter in a filter chain val app = FailsafeFilter ( failsafeExecutor ). then { Thread . sleep ( 100 ) Response ( OK ) } // Throw a bunch of requests at the filter - only 5 should pass for ( it in 1. . 10 ) { thread { println ( app ( Request ( GET , \"/\" )). status ) } } } Example of using multiple policies \u00b6 Using multiple Failsafe policies in the filter is just as easy, as the following example shows. package guide.reference.failsafe import dev.failsafe.CircuitBreaker import dev.failsafe.Failsafe import dev.failsafe.Fallback import dev.failsafe.RetryPolicy import dev.failsafe.Timeout import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.FailsafeFilter import java.time.Duration import kotlin.random.Random fun main () { // Configure multiple Failsafe policies val failsafeExecutor = Failsafe . with ( Fallback . of ( Response ( OK ). body ( \"Fallback\" )), RetryPolicy . builder < Response > () . withMaxAttempts ( 2 ) . onRetry { println ( \"Retrying\" ) } . handleResultIf { ! it . status . successful } . build (), CircuitBreaker . builder < Response > () . withFailureThreshold ( 3 ) . withDelay ( Duration . ofSeconds ( 3 )) . onOpen { println ( \"Circuit open\" ) } . onHalfOpen { println ( \"Circuit half open\" ) } . onClose { println ( \"Circuit closed\" ) } . handleResultIf { it . status . serverError } . build (), Timeout . of ( Duration . ofMillis ( 100 )) ) // We then create a very unstable client using the filter val client = FailsafeFilter ( failsafeExecutor ). then { when ( Random . nextInt ( 0 , 7 )) { 0 -> Response ( INTERNAL_SERVER_ERROR ). body ( \"Oh no!\" ) 1 , 2 -> { Thread . sleep ( 200 ) Response ( OK ). body ( \"Slow!\" ) } else -> Response ( OK ). body ( \"All good!\" ) }. also { println ( \"Call result: ${ it . bodyString () } \" ) } } // Throw a bunch of request at the filter - some will fail and be retried until // the circuit breaker opens and the fallback value will be used after that. repeat ( 1000 ) { client ( Request ( GET , \"/\" )) Thread . sleep ( 1000 ) } }","title":"Failsafe"},{"location":"guide/reference/failsafe/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-failsafe\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/failsafe/#about","text":"This module provides a configurable Filter to provide fault tolerance (CircuitBreaking, RateLimiting, Retrying, Bulkheading, Timeouts etc.), by integrating with the Failsafe library.","title":"About"},{"location":"guide/reference/failsafe/#basic_example","text":"Here's an example that uses BulkHeading to demonstrate how easy it is to use the filter with configured Failsafe policies. package guide.reference.failsafe import dev.failsafe.Bulkhead import dev.failsafe.Failsafe import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.FailsafeFilter import kotlin.concurrent.thread fun main () { // Configure a Failsafe policy val failsafeExecutor = Failsafe . with ( Bulkhead . of < Response > ( 5 ) ) // Use the filter in a filter chain val app = FailsafeFilter ( failsafeExecutor ). then { Thread . sleep ( 100 ) Response ( OK ) } // Throw a bunch of requests at the filter - only 5 should pass for ( it in 1. . 10 ) { thread { println ( app ( Request ( GET , \"/\" )). status ) } } }","title":"Basic example"},{"location":"guide/reference/failsafe/#example_of_using_multiple_policies","text":"Using multiple Failsafe policies in the filter is just as easy, as the following example shows. package guide.reference.failsafe import dev.failsafe.CircuitBreaker import dev.failsafe.Failsafe import dev.failsafe.Fallback import dev.failsafe.RetryPolicy import dev.failsafe.Timeout import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.FailsafeFilter import java.time.Duration import kotlin.random.Random fun main () { // Configure multiple Failsafe policies val failsafeExecutor = Failsafe . with ( Fallback . of ( Response ( OK ). body ( \"Fallback\" )), RetryPolicy . builder < Response > () . withMaxAttempts ( 2 ) . onRetry { println ( \"Retrying\" ) } . handleResultIf { ! it . status . successful } . build (), CircuitBreaker . builder < Response > () . withFailureThreshold ( 3 ) . withDelay ( Duration . ofSeconds ( 3 )) . onOpen { println ( \"Circuit open\" ) } . onHalfOpen { println ( \"Circuit half open\" ) } . onClose { println ( \"Circuit closed\" ) } . handleResultIf { it . status . serverError } . build (), Timeout . of ( Duration . ofMillis ( 100 )) ) // We then create a very unstable client using the filter val client = FailsafeFilter ( failsafeExecutor ). then { when ( Random . nextInt ( 0 , 7 )) { 0 -> Response ( INTERNAL_SERVER_ERROR ). body ( \"Oh no!\" ) 1 , 2 -> { Thread . sleep ( 200 ) Response ( OK ). body ( \"Slow!\" ) } else -> Response ( OK ). body ( \"All good!\" ) }. also { println ( \"Call result: ${ it . bodyString () } \" ) } } // Throw a bunch of request at the filter - some will fail and be retried until // the circuit breaker opens and the fallback value will be used after that. repeat ( 1000 ) { client ( Request ( GET , \"/\" )) Thread . sleep ( 1000 ) } }","title":"Example of using multiple policies"},{"location":"guide/reference/graphql/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-graphql\" ) // for the example below you will also need this dependency... implementation ( \"com.expediagroup:graphql-kotlin-schema-generator\" , version = \"5.3.2\" } About \u00b6 This module provides http4k integration for the excellent GraphQL-java library, allowing you to either serve or consume GraphQL services using a simple adapter API. As with the ethos of http4k, the uniform Server/Client GraphQLHandler model means that you can test applications entirely in-memory without binding to a port. Http4k also ships with a page serving the GraphQL playground which can be added as a simple route. Code \u00b6 package guide.reference.graphql import com.expediagroup.graphql.generator.SchemaGeneratorConfig import com.expediagroup.graphql.generator.TopLevelObject import com.expediagroup.graphql.generator.toSchema import graphql.GraphQL.newGraphQL import org.http4k.client.JavaHttpClient import org.http4k.client.asGraphQLHandler import org.http4k.core.HttpHandler import org.http4k.core.Uri import org.http4k.graphql.GraphQLHandler import org.http4k.graphql.GraphQLRequest import org.http4k.graphql.GraphQLResponse import org.http4k.routing.bind import org.http4k.routing.graphQL import org.http4k.routing.graphQLPlayground import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer object MyGraphQLHandler : GraphQLHandler { private val graphQL = newGraphQL ( toSchema ( SchemaGeneratorConfig ( supportedPackages = listOf ( \"guide.reference.graphql\" )), listOf ( TopLevelObject ( UserQueries ())), listOf () ) ). build () override fun invoke ( request : GraphQLRequest ) = GraphQLResponse . from ( graphQL . execute ( request . query )) } data class User ( val id : Int , val name : String ) data class Params ( val ids : List < Int > ) class UserQueries { private val userDb = listOf ( User ( id = 1 , name = \"Jim\" ), User ( id = 2 , name = \"Bob\" ), ) fun search ( params : Params ) = userDb . firstOrNull { it . id in params . ids } } fun main () { val app : HttpHandler = routes ( \"/graphql\" bind graphQL ( MyGraphQLHandler ), graphQLPlayground ( Uri . of ( \"/graphql\" )) ) // serve GQL queries/mutations at /graphql app . asServer ( SunHttp ( 8000 )). start () // for clients, just convert any app into a GQL handler val gql : GraphQLHandler = JavaHttpClient (). asGraphQLHandler ( Uri . of ( \"http://localhost:8000/graphql\" )) val response : GraphQLResponse = gql ( GraphQLRequest ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) ) println ( response ) println ( \"You can visit the GraphQL playground at: http://localhost:8000\" ) }","title":"GraphQL"},{"location":"guide/reference/graphql/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-graphql\" ) // for the example below you will also need this dependency... implementation ( \"com.expediagroup:graphql-kotlin-schema-generator\" , version = \"5.3.2\" }","title":"Installation (Gradle)"},{"location":"guide/reference/graphql/#about","text":"This module provides http4k integration for the excellent GraphQL-java library, allowing you to either serve or consume GraphQL services using a simple adapter API. As with the ethos of http4k, the uniform Server/Client GraphQLHandler model means that you can test applications entirely in-memory without binding to a port. Http4k also ships with a page serving the GraphQL playground which can be added as a simple route.","title":"About"},{"location":"guide/reference/graphql/#code","text":"package guide.reference.graphql import com.expediagroup.graphql.generator.SchemaGeneratorConfig import com.expediagroup.graphql.generator.TopLevelObject import com.expediagroup.graphql.generator.toSchema import graphql.GraphQL.newGraphQL import org.http4k.client.JavaHttpClient import org.http4k.client.asGraphQLHandler import org.http4k.core.HttpHandler import org.http4k.core.Uri import org.http4k.graphql.GraphQLHandler import org.http4k.graphql.GraphQLRequest import org.http4k.graphql.GraphQLResponse import org.http4k.routing.bind import org.http4k.routing.graphQL import org.http4k.routing.graphQLPlayground import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer object MyGraphQLHandler : GraphQLHandler { private val graphQL = newGraphQL ( toSchema ( SchemaGeneratorConfig ( supportedPackages = listOf ( \"guide.reference.graphql\" )), listOf ( TopLevelObject ( UserQueries ())), listOf () ) ). build () override fun invoke ( request : GraphQLRequest ) = GraphQLResponse . from ( graphQL . execute ( request . query )) } data class User ( val id : Int , val name : String ) data class Params ( val ids : List < Int > ) class UserQueries { private val userDb = listOf ( User ( id = 1 , name = \"Jim\" ), User ( id = 2 , name = \"Bob\" ), ) fun search ( params : Params ) = userDb . firstOrNull { it . id in params . ids } } fun main () { val app : HttpHandler = routes ( \"/graphql\" bind graphQL ( MyGraphQLHandler ), graphQLPlayground ( Uri . of ( \"/graphql\" )) ) // serve GQL queries/mutations at /graphql app . asServer ( SunHttp ( 8000 )). start () // for clients, just convert any app into a GQL handler val gql : GraphQLHandler = JavaHttpClient (). asGraphQLHandler ( Uri . of ( \"http://localhost:8000/graphql\" )) val response : GraphQLResponse = gql ( GraphQLRequest ( \"\"\"{ search(params: { ids: [1]}) { id name } }\"\"\" ) ) println ( response ) println ( \"You can visit the GraphQL playground at: http://localhost:8000\" ) }","title":"Code"},{"location":"guide/reference/hamkrest/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-hamkrest\" ) } About \u00b6 A set of Hamkrest matchers for use when testing http4k apps. Code \u00b6 package guide.reference.hamkrest import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasHeader import org.http4k.hamkrest.hasQuery import org.http4k.hamkrest.hasStatus import org.http4k.lens.string fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status assertThat ( Response ( OK ), hasStatus ( OK )) // query assertThat ( request , hasQuery ( \"a\" , \"b\" )) // header assertThat ( request , hasHeader ( \"my header\" , \"a value\" )) // body assertThat ( request , hasBody ( \"http4k is cool\" )) assertThat ( request , hasBody ( Body . string ( ContentType . TEXT_HTML ). toLens (), equalTo ( \"http4k is cool\" )) ) // composite assertThat ( request , hasBody ( \"http4k is cool\" ). and ( hasQuery ( \"a\" , \"b\" ))) }","title":"Hamkrest"},{"location":"guide/reference/hamkrest/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-hamkrest\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/hamkrest/#about","text":"A set of Hamkrest matchers for use when testing http4k apps.","title":"About"},{"location":"guide/reference/hamkrest/#code","text":"package guide.reference.hamkrest import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasHeader import org.http4k.hamkrest.hasQuery import org.http4k.hamkrest.hasStatus import org.http4k.lens.string fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status assertThat ( Response ( OK ), hasStatus ( OK )) // query assertThat ( request , hasQuery ( \"a\" , \"b\" )) // header assertThat ( request , hasHeader ( \"my header\" , \"a value\" )) // body assertThat ( request , hasBody ( \"http4k is cool\" )) assertThat ( request , hasBody ( Body . string ( ContentType . TEXT_HTML ). toLens (), equalTo ( \"http4k is cool\" )) ) // composite assertThat ( request , hasBody ( \"http4k is cool\" ). and ( hasQuery ( \"a\" , \"b\" ))) }","title":"Code"},{"location":"guide/reference/htmx/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-htmx\" ) implementation ( \"org.http4k:http4k-template-handlebars\" ) // Handlebars } About \u00b6 Utilities to support htmx development. Includes the htmx and hyperscript Webjar support and a set of classes/functions to ease development of htmx-powered applications. Code \u00b6 package guide.reference.htmx import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.isHtmx import org.http4k.core.with import org.http4k.routing.Router.Companion.orElse import org.http4k.routing.bind import org.http4k.routing.htmxWebjars import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel import java.util.Date fun main () { val view = Body . viewModel ( HandlebarsTemplates (). CachingClasspath (), TEXT_HTML ). toLens () val app = routes ( htmxWebjars (), \"/\" bind GET to routes ( // Respond to htmx powered requests Request . isHtmx bind { Response ( OK ). with ( view of Time ( Date ())) }, // Standard requests get routed to here orElse bind { Response ( OK ). with ( view of Index ) } ) ) app . asServer ( SunHttp ( 9000 )). start () System . err . println ( \"htmx server started at http://localhost:9000\" ) } // We are using Handlebars to power the templates used by this app - see the *.hbs files data object Index : ViewModel data class Time ( val date : Date ) : ViewModel","title":"htmx"},{"location":"guide/reference/htmx/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-htmx\" ) implementation ( \"org.http4k:http4k-template-handlebars\" ) // Handlebars }","title":"Installation (Gradle)"},{"location":"guide/reference/htmx/#about","text":"Utilities to support htmx development. Includes the htmx and hyperscript Webjar support and a set of classes/functions to ease development of htmx-powered applications.","title":"About"},{"location":"guide/reference/htmx/#code","text":"package guide.reference.htmx import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.isHtmx import org.http4k.core.with import org.http4k.routing.Router.Companion.orElse import org.http4k.routing.bind import org.http4k.routing.htmxWebjars import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel import java.util.Date fun main () { val view = Body . viewModel ( HandlebarsTemplates (). CachingClasspath (), TEXT_HTML ). toLens () val app = routes ( htmxWebjars (), \"/\" bind GET to routes ( // Respond to htmx powered requests Request . isHtmx bind { Response ( OK ). with ( view of Time ( Date ())) }, // Standard requests get routed to here orElse bind { Response ( OK ). with ( view of Index ) } ) ) app . asServer ( SunHttp ( 9000 )). start () System . err . println ( \"htmx server started at http://localhost:9000\" ) } // We are using Handlebars to power the templates used by this app - see the *.hbs files data object Index : ViewModel data class Time ( val date : Date ) : ViewModel","title":"Code"},{"location":"guide/reference/json/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Argo: implementation ( \"org.http4k:http4k-format-argo\" ) // Gson: implementation ( \"org.http4k:http4k-format-gson\" ) // Jackson: implementation ( \"org.http4k:http4k-format-jackson\" ) // Klaxon: implementation ( \"org.http4k:http4k-format-klaxon\" ) // KondorJson: implementation ( \"org.http4k:http4k-format-kondor-json\" ) // Moshi: implementation ( \"org.http4k:http4k-format-moshi\" ) // KotlinX Serialization: implementation ( \"org.http4k:http4k-format-kotlinx-serialization\" ) } About \u00b6 These modules add the ability to use JSON as a first-class citizen when reading from and to HTTP messages. Each implementation adds a set of standard methods and extension methods for converting common types into native JSON/XML objects, including custom Lens methods for each library so that JSON node objects can be written and read directly from HTTP messages: Notes on individual module choice \u00b6 As a default, we recommend using Moshi as an engine for JSON marshalling, as it is the most modern, lightweight and Kotlin-friendly library with automarshalling capabilities. It also has the option of Kotshi , which allows the compile-time generation of adapters for Kotlin data classes. If you are using OpenAPI contracts, the only current option is to use Jackson . Kondor is an excellent choice for manual mapping of classes to JSON, and does not use reflection. For the simplest use-cases, Argo is a good lightweight non-reflection choice, although it's API is Java-first. GSON support is provided in http4k, but is not recommended due to being mostly unsupported. We have found KotlinSerialisation is possibly the least friendly to use in the context of http4k. Code \u00b6 package guide.reference.json import com.fasterxml.jackson.databind.JsonNode import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson import org.http4k.format.Jackson.asJsonArray import org.http4k.format.Jackson.asJsonObject import org.http4k.format.Jackson.asJsonValue import org.http4k.format.Jackson.asPrettyJsonString import org.http4k.format.Jackson.json import org.http4k.format.Xml.xml import org.w3c.dom.Node val json = Jackson // Extension method API: val objectUsingExtensionFunctions : JsonNode = listOf ( \"thisIsAString\" to \"stringValue\" . asJsonValue (), \"thisIsANumber\" to 12345. asJsonValue (), \"thisIsAList\" to listOf ( true . asJsonValue ()). asJsonArray () ). asJsonObject () val jsonString : String = objectUsingExtensionFunctions . asPrettyJsonString () // Direct JSON library API: val objectUsingDirectApi : JsonNode = json . obj ( \"thisIsAString\" to json . string ( \"stringValue\" ), \"thisIsANumber\" to json . number ( 12345 ), \"thisIsAList\" to json . array ( listOf ( json . boolean ( true ))) ) // DSL JSON library API: val objectUsingDslApi : JsonNode = json { obj ( \"thisIsAString\" to string ( \"stringValue\" ), \"thisIsANumber\" to number ( 12345 ), \"thisIsAList\" to array ( listOf ( boolean ( true ))) ) } val response = Response ( OK ). with ( Body . json (). toLens () of json . array ( listOf ( objectUsingDirectApi , objectUsingExtensionFunctions , objectUsingDslApi ) ) ) val xmlLens = Body . xml (). toLens () val xmlNode : Node = xmlLens ( Request ( GET , \"\" ). body ( \" \" )) Auto-marshalling capabilities \u00b6 Some of the message libraries (eg. Moshi, Jackson, Kotlin serialization, GSON, XML, CSV ....) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection. We can use this facility in http4k to automatically marshall objects to/from HTTP message bodies using Lenses . Note that this approach also sets the appropriate Content-Type header for the message. Code \u00b6 package guide.reference.json import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.format.Jackson.json data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) fun main () { // We can use the auto method here from either Moshi, Jackson ... message format objects. // Note that the auto() method needs to be manually imported as IntelliJ won't pick it up automatically. val messageLens = Body . auto < Message > (). toLens () val myMessage = Message ( \"hello\" , Email ( \"bob@git.com\" ), Email ( \"sue@git.com\" )) /** * There are several options for injection/extraction API: */ // 1. Lens-first approach // to inject the body into the message apply the lens with the \"part\" - this also works with Response val requestWithEmail = messageLens ( myMessage , Request ( GET , \"/\" )) println ( requestWithEmail ) // Produces: // GET / HTTP/1.1 // content-type: application/json // // {\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}} // to extract the body from the message apply the lens - this also works with Response val extractedMessage = messageLens ( requestWithEmail ) println ( extractedMessage ) println ( extractedMessage == myMessage ) // Produces: // Message(subject=hello, from=Email(value=bob@git.com), to=Email(value=sue@git.com)) // true // 2. with()/of() approach - this reuses the lense val requestWithEmail2 = Request ( GET , \"/\" ). with ( messageLens of myMessage ) println ( requestWithEmail2 ) // 3. json() approach - user friendly but recreates the lense with every call val requestWithEmail3 = Request ( GET , \"/\" ). json ( myMessage ) println ( requestWithEmail3 ) val extractedMessage2 = requestWithEmail3 . json < Message > () println ( extractedMessage2 ) } serializing an object/class for a Response via Lens.inject() - this properly sets the Content-Type header to application/json : import kotlinx.serialization.Serializable import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.format.KotlinxSerialization.auto import org.http4k.lens.BiDiBodyLens @Serializable // required by Kotlinx.Serialization data class Car ( val brand : String , val model : String , val year : Int , val miles : Int ) // 'auto' is an extension function of each org.http4k.format.[serialization library] // example: https://github.com/http4k/http4k/blob/master/http4k-format/kotlinx-serialization/src/main/kotlin/org/http4k/format/ConfigurableKotlinxSerialization.kt val lensCarResponse : BiDiBodyLens < Car > = Body . auto < Car > (). toLens () // BiDi allows for outgoing + incoming fun main () { val sweetride = Car ( \"Porsche\" , \"911 Turbo\" , 1988 , 45000 ) // lens(object, response) serializes the object and sets content-type header to 'application/json' // can be used with any Serializable type (Map, List, etc) val app : HttpHandler = { _ : Request -> lensCarResponse ( sweetride , Response ( Status . OK )) } val request : Request = Request ( Method . GET , \"/\" ) val response = app ( request ) println ( response ) /* HTTP/1.1 200 OK content-type: application/json; charset=utf-8 {\"brand\":\"Porsche\",\"model\":\"911 Turbo\",\"year\":1988,\"miles\":45000} */ } There is a utility to generate Kotlin data class code for JSON documents here . These data classes are compatible with using the Body.auto() functionality. FAQ (aka gotchas) regarding Auto-marshalling capabilities \u00b6 Q. Where is the Body.auto method defined? A. Body.auto is an extension method which is declared on the parent singleton object for each of the message libraries that supports auto-marshalling - eg. Jackson , Gson , Moshi and Xml . All of these objects are declared in the same package, so you need to add an import similar to: import org.http4k.format.Jackson.auto Q. Jackson: The Data class auto-marshalling is not working correctly when my JSON fields start with capital letters A. Because of the way in which the Jackson library works, uppercase field names are NOT supported. Either switch out to use http4k-format-gson (which has the same API), or annotate your Data class with @JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class) or the fields with @JsonAlias or to get it work correctly. Q. Jackson: Boolean properties with names starting with \"is\" do not marshall properly A. This is due to the way in which the Jackson ObjectMapper is configured. Annotation of the fields in question should help, or using ObjectMapper.disable(MapperFeature.AUTO_DETECT_IS_GETTERS) Q. Moshi: Declared with Body.auto>().toLens() , my auto-marshalled List doesn't extract properly! A. This occurs in Moshi when serialising bare lists to/from JSON and is to do with the underlying library being lazy in deserialising objects (using LinkedHashTreeMap) ()). Use Body.auto>().toLens() instead. Yes, it's annoying but we haven't found a way to turn if off. Q. Kotlin Serialization: the standard mappings are not working on my data classes. A. This happens because http4k adds the standard mappings to Kotlin serialization as contextual serializers. This can be solved by marking the fields as @Contextual . Q. Gson: the data class auto-marshalling does not fail when a null is populated in a Kotlin non-nullable field A. This happens because http4k uses straight GSON demarshalling, of JVM objects with no-Kotlin library in the mix. The nullability generally gets checked at compile-type and the lack of a Kotlin sanity check library exposes this flaw. No current fix - apart from to use the Jackson demarshalling instead! This can be demonstrated by the following, where you can see that the output of the auto-unmarshalling a naked JSON is NOT the same as a native Kotlin list of objects. This can make tests break as the unmarshalled list is NOT equal to the native list. As shown, a workaround to this is to use Body.auto>().toLens() instead, and then compare using Arrays.equal() package guide.reference.json import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Moshi.auto data class MyIntWrapper ( val value : Int ) fun main () { val aListLens = Body . auto < List < MyIntWrapper >> (). toLens () val req = Request ( GET , \"/\" ). body ( \"\"\" [ {\"value\":1}, {\"value\":2} ] \"\"\" ) val extractedList = aListLens ( req ) val nativeList = listOf ( MyIntWrapper ( 1 ), MyIntWrapper ( 2 )) println ( nativeList ) println ( extractedList ) println ( extractedList == nativeList ) //solution: val anArrayLens = Body . auto < Array < MyIntWrapper >> (). toLens () println ( anArrayLens ( req ). contentEquals ( arrayOf ( MyIntWrapper ( 1 ), MyIntWrapper ( 2 )))) // produces: // [MyIntWrapper(value=1), MyIntWrapper(value=2)] // [{value=1}, {value=2}] // false // true }","title":"JSON handling"},{"location":"guide/reference/json/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Argo: implementation ( \"org.http4k:http4k-format-argo\" ) // Gson: implementation ( \"org.http4k:http4k-format-gson\" ) // Jackson: implementation ( \"org.http4k:http4k-format-jackson\" ) // Klaxon: implementation ( \"org.http4k:http4k-format-klaxon\" ) // KondorJson: implementation ( \"org.http4k:http4k-format-kondor-json\" ) // Moshi: implementation ( \"org.http4k:http4k-format-moshi\" ) // KotlinX Serialization: implementation ( \"org.http4k:http4k-format-kotlinx-serialization\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/json/#about","text":"These modules add the ability to use JSON as a first-class citizen when reading from and to HTTP messages. Each implementation adds a set of standard methods and extension methods for converting common types into native JSON/XML objects, including custom Lens methods for each library so that JSON node objects can be written and read directly from HTTP messages:","title":"About"},{"location":"guide/reference/json/#notes_on_individual_module_choice","text":"As a default, we recommend using Moshi as an engine for JSON marshalling, as it is the most modern, lightweight and Kotlin-friendly library with automarshalling capabilities. It also has the option of Kotshi , which allows the compile-time generation of adapters for Kotlin data classes. If you are using OpenAPI contracts, the only current option is to use Jackson . Kondor is an excellent choice for manual mapping of classes to JSON, and does not use reflection. For the simplest use-cases, Argo is a good lightweight non-reflection choice, although it's API is Java-first. GSON support is provided in http4k, but is not recommended due to being mostly unsupported. We have found KotlinSerialisation is possibly the least friendly to use in the context of http4k.","title":"Notes on individual module choice"},{"location":"guide/reference/json/#code","text":"package guide.reference.json import com.fasterxml.jackson.databind.JsonNode import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson import org.http4k.format.Jackson.asJsonArray import org.http4k.format.Jackson.asJsonObject import org.http4k.format.Jackson.asJsonValue import org.http4k.format.Jackson.asPrettyJsonString import org.http4k.format.Jackson.json import org.http4k.format.Xml.xml import org.w3c.dom.Node val json = Jackson // Extension method API: val objectUsingExtensionFunctions : JsonNode = listOf ( \"thisIsAString\" to \"stringValue\" . asJsonValue (), \"thisIsANumber\" to 12345. asJsonValue (), \"thisIsAList\" to listOf ( true . asJsonValue ()). asJsonArray () ). asJsonObject () val jsonString : String = objectUsingExtensionFunctions . asPrettyJsonString () // Direct JSON library API: val objectUsingDirectApi : JsonNode = json . obj ( \"thisIsAString\" to json . string ( \"stringValue\" ), \"thisIsANumber\" to json . number ( 12345 ), \"thisIsAList\" to json . array ( listOf ( json . boolean ( true ))) ) // DSL JSON library API: val objectUsingDslApi : JsonNode = json { obj ( \"thisIsAString\" to string ( \"stringValue\" ), \"thisIsANumber\" to number ( 12345 ), \"thisIsAList\" to array ( listOf ( boolean ( true ))) ) } val response = Response ( OK ). with ( Body . json (). toLens () of json . array ( listOf ( objectUsingDirectApi , objectUsingExtensionFunctions , objectUsingDslApi ) ) ) val xmlLens = Body . xml (). toLens () val xmlNode : Node = xmlLens ( Request ( GET , \"\" ). body ( \" \" ))","title":"Code"},{"location":"guide/reference/json/#auto-marshalling_capabilities","text":"Some of the message libraries (eg. Moshi, Jackson, Kotlin serialization, GSON, XML, CSV ....) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection. We can use this facility in http4k to automatically marshall objects to/from HTTP message bodies using Lenses . Note that this approach also sets the appropriate Content-Type header for the message.","title":"Auto-marshalling capabilities"},{"location":"guide/reference/json/#code_1","text":"package guide.reference.json import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.format.Jackson.json data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) fun main () { // We can use the auto method here from either Moshi, Jackson ... message format objects. // Note that the auto() method needs to be manually imported as IntelliJ won't pick it up automatically. val messageLens = Body . auto < Message > (). toLens () val myMessage = Message ( \"hello\" , Email ( \"bob@git.com\" ), Email ( \"sue@git.com\" )) /** * There are several options for injection/extraction API: */ // 1. Lens-first approach // to inject the body into the message apply the lens with the \"part\" - this also works with Response val requestWithEmail = messageLens ( myMessage , Request ( GET , \"/\" )) println ( requestWithEmail ) // Produces: // GET / HTTP/1.1 // content-type: application/json // // {\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}} // to extract the body from the message apply the lens - this also works with Response val extractedMessage = messageLens ( requestWithEmail ) println ( extractedMessage ) println ( extractedMessage == myMessage ) // Produces: // Message(subject=hello, from=Email(value=bob@git.com), to=Email(value=sue@git.com)) // true // 2. with()/of() approach - this reuses the lense val requestWithEmail2 = Request ( GET , \"/\" ). with ( messageLens of myMessage ) println ( requestWithEmail2 ) // 3. json() approach - user friendly but recreates the lense with every call val requestWithEmail3 = Request ( GET , \"/\" ). json ( myMessage ) println ( requestWithEmail3 ) val extractedMessage2 = requestWithEmail3 . json < Message > () println ( extractedMessage2 ) } serializing an object/class for a Response via Lens.inject() - this properly sets the Content-Type header to application/json : import kotlinx.serialization.Serializable import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.format.KotlinxSerialization.auto import org.http4k.lens.BiDiBodyLens @Serializable // required by Kotlinx.Serialization data class Car ( val brand : String , val model : String , val year : Int , val miles : Int ) // 'auto' is an extension function of each org.http4k.format.[serialization library] // example: https://github.com/http4k/http4k/blob/master/http4k-format/kotlinx-serialization/src/main/kotlin/org/http4k/format/ConfigurableKotlinxSerialization.kt val lensCarResponse : BiDiBodyLens < Car > = Body . auto < Car > (). toLens () // BiDi allows for outgoing + incoming fun main () { val sweetride = Car ( \"Porsche\" , \"911 Turbo\" , 1988 , 45000 ) // lens(object, response) serializes the object and sets content-type header to 'application/json' // can be used with any Serializable type (Map, List, etc) val app : HttpHandler = { _ : Request -> lensCarResponse ( sweetride , Response ( Status . OK )) } val request : Request = Request ( Method . GET , \"/\" ) val response = app ( request ) println ( response ) /* HTTP/1.1 200 OK content-type: application/json; charset=utf-8 {\"brand\":\"Porsche\",\"model\":\"911 Turbo\",\"year\":1988,\"miles\":45000} */ } There is a utility to generate Kotlin data class code for JSON documents here . These data classes are compatible with using the Body.auto() functionality.","title":"Code"},{"location":"guide/reference/json/#faq_aka_gotchas_regarding_auto-marshalling_capabilities","text":"Q. Where is the Body.auto method defined? A. Body.auto is an extension method which is declared on the parent singleton object for each of the message libraries that supports auto-marshalling - eg. Jackson , Gson , Moshi and Xml . All of these objects are declared in the same package, so you need to add an import similar to: import org.http4k.format.Jackson.auto Q. Jackson: The Data class auto-marshalling is not working correctly when my JSON fields start with capital letters A. Because of the way in which the Jackson library works, uppercase field names are NOT supported. Either switch out to use http4k-format-gson (which has the same API), or annotate your Data class with @JsonNaming(PropertyNamingStrategy.UpperCamelCaseStrategy.class) or the fields with @JsonAlias or to get it work correctly. Q. Jackson: Boolean properties with names starting with \"is\" do not marshall properly A. This is due to the way in which the Jackson ObjectMapper is configured. Annotation of the fields in question should help, or using ObjectMapper.disable(MapperFeature.AUTO_DETECT_IS_GETTERS) Q. Moshi: Declared with Body.auto>().toLens() , my auto-marshalled List doesn't extract properly! A. This occurs in Moshi when serialising bare lists to/from JSON and is to do with the underlying library being lazy in deserialising objects (using LinkedHashTreeMap) ()). Use Body.auto>().toLens() instead. Yes, it's annoying but we haven't found a way to turn if off. Q. Kotlin Serialization: the standard mappings are not working on my data classes. A. This happens because http4k adds the standard mappings to Kotlin serialization as contextual serializers. This can be solved by marking the fields as @Contextual . Q. Gson: the data class auto-marshalling does not fail when a null is populated in a Kotlin non-nullable field A. This happens because http4k uses straight GSON demarshalling, of JVM objects with no-Kotlin library in the mix. The nullability generally gets checked at compile-type and the lack of a Kotlin sanity check library exposes this flaw. No current fix - apart from to use the Jackson demarshalling instead! This can be demonstrated by the following, where you can see that the output of the auto-unmarshalling a naked JSON is NOT the same as a native Kotlin list of objects. This can make tests break as the unmarshalled list is NOT equal to the native list. As shown, a workaround to this is to use Body.auto>().toLens() instead, and then compare using Arrays.equal() package guide.reference.json import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Moshi.auto data class MyIntWrapper ( val value : Int ) fun main () { val aListLens = Body . auto < List < MyIntWrapper >> (). toLens () val req = Request ( GET , \"/\" ). body ( \"\"\" [ {\"value\":1}, {\"value\":2} ] \"\"\" ) val extractedList = aListLens ( req ) val nativeList = listOf ( MyIntWrapper ( 1 ), MyIntWrapper ( 2 )) println ( nativeList ) println ( extractedList ) println ( extractedList == nativeList ) //solution: val anArrayLens = Body . auto < Array < MyIntWrapper >> (). toLens () println ( anArrayLens ( req ). contentEquals ( arrayOf ( MyIntWrapper ( 1 ), MyIntWrapper ( 2 )))) // produces: // [MyIntWrapper(value=1), MyIntWrapper(value=2)] // [{value=1}, {value=2}] // false // true }","title":"FAQ (aka gotchas) regarding Auto-marshalling capabilities"},{"location":"guide/reference/jsonrpc/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-jsonrpc\" ) } About \u00b6 Support for JSON-RPC handlers, with support for both manual and automatic marshalling modes. Each service method \"name\" is bound to a particular endpoint function and then the entire API is exposed as a standard http4k HttpHandler , so it can be composed with other HttpHandlers and Filters. A specialised ErrorHandler can also be assigned to the RPC contract. Note that in order to activate JSON RPC, you need to import one of the supported JSON modules. Code \u00b6 package guide.reference.jsonrpc import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.format.Jackson import org.http4k.format.Json import org.http4k.jsonrpc.ErrorHandler import org.http4k.jsonrpc.ErrorMessage import org.http4k.jsonrpc.JsonRpc import java.util.concurrent.atomic.AtomicInteger class Counter { private val value = AtomicInteger () fun increment ( amount : Increment ): Int = when { amount . value == 10 -> throw RuntimeException ( \"Boom!\" ) amount . value < 0 -> throw NegativeIncrementException () else -> value . addAndGet ( amount . value ) } fun currentValue (): Int = value . get () data class Increment ( val value : Int ) class NegativeIncrementException : RuntimeException ( \"negative increment not allowed\" ) } object CounterErrorHandler : ErrorHandler { override fun invoke ( error : Throwable ): ErrorMessage? = when ( error ) { is Counter . NegativeIncrementException -> NegativeIncrementExceptionMessage () else -> null } private class NegativeIncrementExceptionMessage : ErrorMessage ( 1 , \"Increment by negative\" ) { override fun < NODE > data ( json : Json < NODE > ) = json . string ( \"cannot increment counter by negative\" ) } } fun main () { val counter = Counter () val rpcHandler : HttpHandler = JsonRpc . auto ( Jackson , CounterErrorHandler ) { method ( \"increment\" , handler ( counter :: increment )) method ( \"current\" , handler ( counter :: currentValue )) } fun runRequest ( s : String ) { println ( rpcHandler ( Request ( POST , \"/rpc\" ) . header ( \"Content-Type\" , \"application/json\" ) . body ( s ) ) ) } val increment = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"increment\", \"params\": {\"value\": 3}, \"id\": 1} \"\"\" runRequest ( increment ) val incrementInvalid = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"increment\", \"params\": {\"value\": -1}, \"id\": 2} \"\"\" runRequest ( incrementInvalid ) val current = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"current\", \"id\": 3} \"\"\" runRequest ( current ) }","title":"JSON RPC"},{"location":"guide/reference/jsonrpc/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-jsonrpc\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/jsonrpc/#about","text":"Support for JSON-RPC handlers, with support for both manual and automatic marshalling modes. Each service method \"name\" is bound to a particular endpoint function and then the entire API is exposed as a standard http4k HttpHandler , so it can be composed with other HttpHandlers and Filters. A specialised ErrorHandler can also be assigned to the RPC contract. Note that in order to activate JSON RPC, you need to import one of the supported JSON modules.","title":"About"},{"location":"guide/reference/jsonrpc/#code","text":"package guide.reference.jsonrpc import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.format.Jackson import org.http4k.format.Json import org.http4k.jsonrpc.ErrorHandler import org.http4k.jsonrpc.ErrorMessage import org.http4k.jsonrpc.JsonRpc import java.util.concurrent.atomic.AtomicInteger class Counter { private val value = AtomicInteger () fun increment ( amount : Increment ): Int = when { amount . value == 10 -> throw RuntimeException ( \"Boom!\" ) amount . value < 0 -> throw NegativeIncrementException () else -> value . addAndGet ( amount . value ) } fun currentValue (): Int = value . get () data class Increment ( val value : Int ) class NegativeIncrementException : RuntimeException ( \"negative increment not allowed\" ) } object CounterErrorHandler : ErrorHandler { override fun invoke ( error : Throwable ): ErrorMessage? = when ( error ) { is Counter . NegativeIncrementException -> NegativeIncrementExceptionMessage () else -> null } private class NegativeIncrementExceptionMessage : ErrorMessage ( 1 , \"Increment by negative\" ) { override fun < NODE > data ( json : Json < NODE > ) = json . string ( \"cannot increment counter by negative\" ) } } fun main () { val counter = Counter () val rpcHandler : HttpHandler = JsonRpc . auto ( Jackson , CounterErrorHandler ) { method ( \"increment\" , handler ( counter :: increment )) method ( \"current\" , handler ( counter :: currentValue )) } fun runRequest ( s : String ) { println ( rpcHandler ( Request ( POST , \"/rpc\" ) . header ( \"Content-Type\" , \"application/json\" ) . body ( s ) ) ) } val increment = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"increment\", \"params\": {\"value\": 3}, \"id\": 1} \"\"\" runRequest ( increment ) val incrementInvalid = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"increment\", \"params\": {\"value\": -1}, \"id\": 2} \"\"\" runRequest ( incrementInvalid ) val current = \"\"\" {\"jsonrpc\": \"2.0\", \"method\": \"current\", \"id\": 3} \"\"\" runRequest ( current ) }","title":"Code"},{"location":"guide/reference/kotest/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-kotest\" ) } About \u00b6 A set of Kotest matchers for use when testing http4k apps. Code \u00b6 package guide.reference.kotest import io.kotest.matchers.and import io.kotest.matchers.be import io.kotest.matchers.should import io.kotest.matchers.string.startWith import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.kotest.haveBody import org.http4k.kotest.haveHeader import org.http4k.kotest.haveQuery import org.http4k.kotest.haveStatus import org.http4k.kotest.shouldHaveBody import org.http4k.kotest.shouldHaveHeader import org.http4k.kotest.shouldHaveQuery import org.http4k.kotest.shouldHaveStatus import org.http4k.lens.string fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status Response ( OK ) should haveStatus ( OK ) Response ( OK ) shouldHaveStatus OK // query request should haveQuery ( \"a\" , \"b\" ) request . shouldHaveQuery ( \"a\" , \"b\" ) // header request should haveHeader ( \"my header\" , \"a value\" ) request . shouldHaveHeader ( \"my header\" , \"a value\" ) // body request should haveBody ( startWith ( \"http4k is cool\" )) request should haveBody ( \"http4k is cool\" ) request should haveBody ( Body . string ( ContentType . TEXT_HTML ). toLens (), be ( \"http4k is cool\" )) request shouldHaveBody \"http4k is cool\" // composite request should ( haveQuery ( \"a\" , \"b\" ) and haveBody ( \"http4k is cool\" )) }","title":"Kotest"},{"location":"guide/reference/kotest/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-kotest\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/kotest/#about","text":"A set of Kotest matchers for use when testing http4k apps.","title":"About"},{"location":"guide/reference/kotest/#code","text":"package guide.reference.kotest import io.kotest.matchers.and import io.kotest.matchers.be import io.kotest.matchers.should import io.kotest.matchers.string.startWith import org.http4k.core.Body import org.http4k.core.ContentType import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.kotest.haveBody import org.http4k.kotest.haveHeader import org.http4k.kotest.haveQuery import org.http4k.kotest.haveStatus import org.http4k.kotest.shouldHaveBody import org.http4k.kotest.shouldHaveHeader import org.http4k.kotest.shouldHaveQuery import org.http4k.kotest.shouldHaveStatus import org.http4k.lens.string fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status Response ( OK ) should haveStatus ( OK ) Response ( OK ) shouldHaveStatus OK // query request should haveQuery ( \"a\" , \"b\" ) request . shouldHaveQuery ( \"a\" , \"b\" ) // header request should haveHeader ( \"my header\" , \"a value\" ) request . shouldHaveHeader ( \"my header\" , \"a value\" ) // body request should haveBody ( startWith ( \"http4k is cool\" )) request should haveBody ( \"http4k is cool\" ) request should haveBody ( Body . string ( ContentType . TEXT_HTML ). toLens (), be ( \"http4k is cool\" )) request shouldHaveBody \"http4k is cool\" // composite request should ( haveQuery ( \"a\" , \"b\" ) and haveBody ( \"http4k is cool\" )) }","title":"Code"},{"location":"guide/reference/micrometer/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-metrics-micrometer\" ) } About \u00b6 This module provides configurable Filters to provide metrics for http4k apps, plugging into the awesome Micrometer library. Micrometer \u00b6 Both Server and Client filters are available for recording request counts and latency, optionally overriding values for the metric names, descriptions and request identification. package guide.reference.micrometer import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // this is a micrometer registry used mostly for testing - substitute the correct implementation. val registry = SimpleMeterRegistry () val server = routes ( \"/metrics\" bind GET to { Response ( OK ) }) // apply filters to a server... val app = ServerFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( server ) // ... or to a client val client = ClientFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ApacheClient ()) // make some calls repeat (( 0. . 10 ). count ()) { app ( Request ( GET , \"/metrics\" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results registry . forEachMeter { println ( \" ${ it . id } ${ it . measure (). joinToString ( \" , \" ) } \" ) } }","title":"Micrometer"},{"location":"guide/reference/micrometer/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-metrics-micrometer\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/micrometer/#about","text":"This module provides configurable Filters to provide metrics for http4k apps, plugging into the awesome Micrometer library.","title":"About"},{"location":"guide/reference/micrometer/#micrometer","text":"Both Server and Client filters are available for recording request counts and latency, optionally overriding values for the metric names, descriptions and request identification. package guide.reference.micrometer import io.micrometer.core.instrument.simple.SimpleMeterRegistry import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.MicrometerMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // this is a micrometer registry used mostly for testing - substitute the correct implementation. val registry = SimpleMeterRegistry () val server = routes ( \"/metrics\" bind GET to { Response ( OK ) }) // apply filters to a server... val app = ServerFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ServerFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( server ) // ... or to a client val client = ClientFilters . MicrometerMetrics . RequestCounter ( registry ) . then ( ClientFilters . MicrometerMetrics . RequestTimer ( registry )) . then ( ApacheClient ()) // make some calls repeat (( 0. . 10 ). count ()) { app ( Request ( GET , \"/metrics\" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results registry . forEachMeter { println ( \" ${ it . id } ${ it . measure (). joinToString ( \" , \" ) } \" ) } }","title":"Micrometer"},{"location":"guide/reference/multipart/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-multipart\" ) } About \u00b6 Multipart form support for fields and files, including a set of lens extensions for fields/files. See the how-to guides for example use. Receiving Binary content with http4k Contracts \u00b6 With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. This is possible by instructing http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) }","title":"Multipart forms"},{"location":"guide/reference/multipart/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-multipart\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/multipart/#about","text":"Multipart form support for fields and files, including a set of lens extensions for fields/files. See the how-to guides for example use.","title":"About"},{"location":"guide/reference/multipart/#receiving_binary_content_with_http4k_contracts","text":"With binary attachments, you need to turn ensure that the pre-flight validation does not eat the stream. This is possible by instructing http4k to ignore the incoming body for validation purposes: routes += \"/api/document-upload\" meta { preFlightExtraction = PreFlightExtraction . IgnoreBody } bindContract POST to { req -> Response ( OK ) }","title":"Receiving Binary content with http4k Contracts"},{"location":"guide/reference/oauth/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-security-oauth\" ) } About \u00b6 Support for using integrating with external OAuth2 providers for authentication purposes and to provide access to external APIs of entities such as Auth0, Google etc. Specifically, http4k supports the popular OAuth2 Authorization Code and Refresh Token Grants. Authorization Code Grant \u00b6 This flow requires user interaction; it uses a callback mechanism that plays out like this: App developer (you!) creates an application on the OAuth provider and receives a Client Id and a Client Secret . You also provide a \"callback\" URL to the provider which will be used later. When accessing a protected resource, your app checks for an AccessToken from the user (via cookie or similar) If the user has no token, the app redirects the user browser back to the OAuth provider site, along with the \"state\" of the user - containing a generated CrossSiteRequestForgeryToken (CSRF - which is also stored by the app) and the original URI the user was trying to access. The user logs in on the OAuth provider site, which generates a code that is returned as a query parameter in a redirect back to the registered callback URL in your app, along with the CSRF token. Your app checks the content of the CSRF token to determine that the redirect is genuine, then sends the received code back to the OAuth provider in exchange for a valid AccessToken . This completes the flow The AccessToken can then be used to access various services from the OAuth provider APIs. There is a single user-defined interface, OAuthPersistence , required to implement to enable this flow. This interface is required to provide the custom way in which your application will store and retrieve the CSRF and AccessToken for a request. A common way to do this is through Cookies, but the values should definitely be encrypted. http4k only provides an insecure version of this class that you can use for testing. In order to remain provider-agnostic, the AccessToken object also contains the entirety of the (typically JSON) token response from the provider, which may include other fields depending on the types of scope for which your application is authorised by the user. To enable OAuth integration, construct a configured instance of OAuthProvider . This provides 3 things: A filter to protect application resources A callback HttpHandler for the OAuth provider to redirect the authenticated user to A fully configured API client (which populated the Host on the URI) - this allows different implementations of the provider to be used across environments. Example provider \u00b6 Out of the box, http4k provides implementations for several OAuth providers. package guide.reference.oauth import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.InsecureCookieBasedOAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.google import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // set these before running this example val googleClientId = System . getenv ( \"CLIENT_ID\" ) val googleClientSecret = System . getenv ( \"CLIENT_SECRET\" ) val port = 9000 // the callback uri which is configured in our OAuth provider val callbackUri = Uri . of ( \"http://localhost: $ port /callback\" ) // this is a test implementation of the OAuthPersistence interface, which should be // implemented by application developers val oAuthPersistence = InsecureCookieBasedOAuthPersistence ( \"Google\" ) // pre-defined configuration exist for common OAuth providers val oauthProvider = OAuthProvider . google ( ApacheClient (), Credentials ( googleClientId , googleClientSecret ), callbackUri , oAuthPersistence ) // the 2 main points here are the callback handler and the authFilter, which protects the root resource val app : HttpHandler = routes ( callbackUri . path bind GET to oauthProvider . callback , \"/\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"hello!\" ) } ) ServerFilters . CatchAll () . then ( app ) . asServer ( SunHttp ( port )). start (). block () } // browse to: http://localhost:9000 - you'll be redirected to google for authentication See the how-to guides for a custom implementation. Refresh Token Grant \u00b6 This flow can be performed \"offline\", i.e. once a user has given their consent via the Authorization Code grant, your server can continue to access protected resources on behalf of the user after the original AccessToken expires. The flow plays out like this: After the user performs the Authorization Code grant, your server will store the RefreshToken The next time you need to access a protected resource on behalf of the user, your server retrieves the user's RefreshToken , then uses the preconfigured Client Id and Client Secret to exchange the RefreshToken for a new AccessToken . [Optional] Your server can cache the refreshed AccessToken if several calls need to be made in a short period of time Example Filter \u00b6 Http4k provides a filter that can be attached to your client to authorize requests to the OAuth resource server. package guide.reference.oauth import org.http4k.client.JavaHttpClient import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.security.OAuthProviderConfig import org.http4k.security.oauth.client.OAuthOffline import org.http4k.security.oauth.core.RefreshToken fun main () { // set these before running example val refreshToken = RefreshToken ( System . getenv ( \"REFRESH_TOKEN\" )) val clientCredentials = Credentials ( System . getenv ( \"CLIENT_ID\" ), System . getenv ( \"CLIENT_SECRET\" )) val authServerBase = Uri . of ( System . getenv ( \"OAUTH_AUTH_SERVER_HOST\" )) val resourceServerHost = Uri . of ( System . getenv ( \"OAUTH_RESOURCE_SERVER_HOST\" )) // all the configuration we need to talk to our OAuth Auth Server (Google, Auth0, Okta, etc.) val config = OAuthProviderConfig ( authBase = authServerBase , authPath = \"/oauth2/authorize\" , tokenPath = \"/oauth2/token\" , credentials = clientCredentials ) // construct a client with a filter to authorize our requests to the OAuth Resource server val client = ClientFilters . SetHostFrom ( resourceServerHost ) . then ( ClientFilters . OAuthOffline ( config , refreshToken , JavaHttpClient ())) . then ( JavaHttpClient ()) // Make a request to the OAuth Resource server val request = Request ( GET , \"/v1/secure/resource\" ) val response = client ( request ) println ( response ) }","title":"OAuth"},{"location":"guide/reference/oauth/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-security-oauth\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/oauth/#about","text":"Support for using integrating with external OAuth2 providers for authentication purposes and to provide access to external APIs of entities such as Auth0, Google etc. Specifically, http4k supports the popular OAuth2 Authorization Code and Refresh Token Grants.","title":"About"},{"location":"guide/reference/oauth/#authorization_code_grant","text":"This flow requires user interaction; it uses a callback mechanism that plays out like this: App developer (you!) creates an application on the OAuth provider and receives a Client Id and a Client Secret . You also provide a \"callback\" URL to the provider which will be used later. When accessing a protected resource, your app checks for an AccessToken from the user (via cookie or similar) If the user has no token, the app redirects the user browser back to the OAuth provider site, along with the \"state\" of the user - containing a generated CrossSiteRequestForgeryToken (CSRF - which is also stored by the app) and the original URI the user was trying to access. The user logs in on the OAuth provider site, which generates a code that is returned as a query parameter in a redirect back to the registered callback URL in your app, along with the CSRF token. Your app checks the content of the CSRF token to determine that the redirect is genuine, then sends the received code back to the OAuth provider in exchange for a valid AccessToken . This completes the flow The AccessToken can then be used to access various services from the OAuth provider APIs. There is a single user-defined interface, OAuthPersistence , required to implement to enable this flow. This interface is required to provide the custom way in which your application will store and retrieve the CSRF and AccessToken for a request. A common way to do this is through Cookies, but the values should definitely be encrypted. http4k only provides an insecure version of this class that you can use for testing. In order to remain provider-agnostic, the AccessToken object also contains the entirety of the (typically JSON) token response from the provider, which may include other fields depending on the types of scope for which your application is authorised by the user. To enable OAuth integration, construct a configured instance of OAuthProvider . This provides 3 things: A filter to protect application resources A callback HttpHandler for the OAuth provider to redirect the authenticated user to A fully configured API client (which populated the Host on the URI) - this allows different implementations of the provider to be used across environments.","title":"Authorization Code Grant"},{"location":"guide/reference/oauth/#example_provider","text":"Out of the box, http4k provides implementations for several OAuth providers. package guide.reference.oauth import org.http4k.client.ApacheClient import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.security.InsecureCookieBasedOAuthPersistence import org.http4k.security.OAuthProvider import org.http4k.security.google import org.http4k.server.SunHttp import org.http4k.server.asServer fun main () { // set these before running this example val googleClientId = System . getenv ( \"CLIENT_ID\" ) val googleClientSecret = System . getenv ( \"CLIENT_SECRET\" ) val port = 9000 // the callback uri which is configured in our OAuth provider val callbackUri = Uri . of ( \"http://localhost: $ port /callback\" ) // this is a test implementation of the OAuthPersistence interface, which should be // implemented by application developers val oAuthPersistence = InsecureCookieBasedOAuthPersistence ( \"Google\" ) // pre-defined configuration exist for common OAuth providers val oauthProvider = OAuthProvider . google ( ApacheClient (), Credentials ( googleClientId , googleClientSecret ), callbackUri , oAuthPersistence ) // the 2 main points here are the callback handler and the authFilter, which protects the root resource val app : HttpHandler = routes ( callbackUri . path bind GET to oauthProvider . callback , \"/\" bind GET to oauthProvider . authFilter . then { Response ( OK ). body ( \"hello!\" ) } ) ServerFilters . CatchAll () . then ( app ) . asServer ( SunHttp ( port )). start (). block () } // browse to: http://localhost:9000 - you'll be redirected to google for authentication See the how-to guides for a custom implementation.","title":"Example provider"},{"location":"guide/reference/oauth/#refresh_token_grant","text":"This flow can be performed \"offline\", i.e. once a user has given their consent via the Authorization Code grant, your server can continue to access protected resources on behalf of the user after the original AccessToken expires. The flow plays out like this: After the user performs the Authorization Code grant, your server will store the RefreshToken The next time you need to access a protected resource on behalf of the user, your server retrieves the user's RefreshToken , then uses the preconfigured Client Id and Client Secret to exchange the RefreshToken for a new AccessToken . [Optional] Your server can cache the refreshed AccessToken if several calls need to be made in a short period of time","title":"Refresh Token Grant"},{"location":"guide/reference/oauth/#example_filter","text":"Http4k provides a filter that can be attached to your client to authorize requests to the OAuth resource server. package guide.reference.oauth import org.http4k.client.JavaHttpClient import org.http4k.core.Credentials import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.security.OAuthProviderConfig import org.http4k.security.oauth.client.OAuthOffline import org.http4k.security.oauth.core.RefreshToken fun main () { // set these before running example val refreshToken = RefreshToken ( System . getenv ( \"REFRESH_TOKEN\" )) val clientCredentials = Credentials ( System . getenv ( \"CLIENT_ID\" ), System . getenv ( \"CLIENT_SECRET\" )) val authServerBase = Uri . of ( System . getenv ( \"OAUTH_AUTH_SERVER_HOST\" )) val resourceServerHost = Uri . of ( System . getenv ( \"OAUTH_RESOURCE_SERVER_HOST\" )) // all the configuration we need to talk to our OAuth Auth Server (Google, Auth0, Okta, etc.) val config = OAuthProviderConfig ( authBase = authServerBase , authPath = \"/oauth2/authorize\" , tokenPath = \"/oauth2/token\" , credentials = clientCredentials ) // construct a client with a filter to authorize our requests to the OAuth Resource server val client = ClientFilters . SetHostFrom ( resourceServerHost ) . then ( ClientFilters . OAuthOffline ( config , refreshToken , JavaHttpClient ())) . then ( JavaHttpClient ()) // Make a request to the OAuth Resource server val request = Request ( GET , \"/v1/secure/resource\" ) val response = client ( request ) println ( response ) }","title":"Example Filter"},{"location":"guide/reference/opentelemetry/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-opentelemetry\" ) } About \u00b6 This module provides configurable Filters to provide distributed tracing and metrics for http4k apps, plugging into the awesome OpenTelemetry APIs. OpenTelemetry is a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior. Tracing \u00b6 OpenTelemetry provides a pluggable interface for tracing propagation, so you can easily switch between different implementations such as AWS X-Ray, B3 and Jaeger etc. package guide.reference.opentelemetry import io.opentelemetry.context.propagation.ContextPropagators.create import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator import io.opentelemetry.sdk.OpenTelemetrySdk import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.OpenTelemetryTracing import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes fun main () { // configure OpenTelemetry using the Amazon XRAY tracing scheme val openTelemetry = OpenTelemetrySdk . builder () . setPropagators ( create ( AwsXrayPropagator . getInstance ())) . buildAndRegisterGlobal () // this HttpHandler represents a 3rd party service, and will repeat the request body val repeater : HttpHandler = { println ( \"REMOTE REQUEST WITH TRACING HEADERS: $ it \" ) Response ( OK ). body ( it . bodyString () + it . bodyString ()) } // we will propagate the tracing headers using the tracer instance val repeaterClient = ClientFilters . OpenTelemetryTracing ( openTelemetry ). then ( repeater ) // this is the server app which will add tracing spans to incoming requests val app = ServerFilters . OpenTelemetryTracing ( openTelemetry ) . then ( routes ( \"/echo/{name}\" bind GET to { val remoteResponse = repeaterClient ( Request ( POST , \"http://aRemoteServer/endpoint\" ) . body ( it . path ( \"name\" ) !! ) ) Response ( OK ). body ( remoteResponse . bodyString ()) })) println ( \"RETURNED TO CALLER: \" + app ( Request ( GET , \"http://localhost:8080/echo/david\" ))) } Metrics \u00b6 Both Server and Client filters are available for recording request counts and latency, optionally overriding values for the metric names, descriptions and request identification. package guide.reference.opentelemetry import io.opentelemetry.sdk.OpenTelemetrySdk import io.opentelemetry.sdk.metrics.SdkMeterProvider import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.OpenTelemetryMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // test only: this sets up the metrics provider to something we can read val inMemoryMetricReader = InMemoryMetricReader . create () OpenTelemetrySdk . builder () . setMeterProvider ( SdkMeterProvider . builder () . registerMetricReader ( inMemoryMetricReader ) . build () ) . buildAndRegisterGlobal () val server = routes ( \"/metrics\" bind GET to { Response ( OK ) }) // apply metrics filters to a server... val app = ServerFilters . OpenTelemetryMetrics . RequestCounter () . then ( ServerFilters . OpenTelemetryMetrics . RequestTimer ()) . then ( server ) // ... or to a client val client = ClientFilters . OpenTelemetryMetrics . RequestCounter () . then ( ClientFilters . OpenTelemetryMetrics . RequestTimer ()) . then ( ApacheClient ()) // make some calls repeat ( 5 ) { app ( Request ( GET , \"/metrics\" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results inMemoryMetricReader . collectAllMetrics (). forEach { println ( \"metric: \" + it . name + \", value: \" + ( it . longSumData . points . takeIf { it . isNotEmpty () } ?: it . doubleSumData . points ) ) } }","title":"OpenTelemetry"},{"location":"guide/reference/opentelemetry/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-opentelemetry\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/opentelemetry/#about","text":"This module provides configurable Filters to provide distributed tracing and metrics for http4k apps, plugging into the awesome OpenTelemetry APIs. OpenTelemetry is a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior.","title":"About"},{"location":"guide/reference/opentelemetry/#tracing","text":"OpenTelemetry provides a pluggable interface for tracing propagation, so you can easily switch between different implementations such as AWS X-Ray, B3 and Jaeger etc. package guide.reference.opentelemetry import io.opentelemetry.context.propagation.ContextPropagators.create import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator import io.opentelemetry.sdk.OpenTelemetrySdk import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.OpenTelemetryTracing import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes fun main () { // configure OpenTelemetry using the Amazon XRAY tracing scheme val openTelemetry = OpenTelemetrySdk . builder () . setPropagators ( create ( AwsXrayPropagator . getInstance ())) . buildAndRegisterGlobal () // this HttpHandler represents a 3rd party service, and will repeat the request body val repeater : HttpHandler = { println ( \"REMOTE REQUEST WITH TRACING HEADERS: $ it \" ) Response ( OK ). body ( it . bodyString () + it . bodyString ()) } // we will propagate the tracing headers using the tracer instance val repeaterClient = ClientFilters . OpenTelemetryTracing ( openTelemetry ). then ( repeater ) // this is the server app which will add tracing spans to incoming requests val app = ServerFilters . OpenTelemetryTracing ( openTelemetry ) . then ( routes ( \"/echo/{name}\" bind GET to { val remoteResponse = repeaterClient ( Request ( POST , \"http://aRemoteServer/endpoint\" ) . body ( it . path ( \"name\" ) !! ) ) Response ( OK ). body ( remoteResponse . bodyString ()) })) println ( \"RETURNED TO CALLER: \" + app ( Request ( GET , \"http://localhost:8080/echo/david\" ))) }","title":"Tracing"},{"location":"guide/reference/opentelemetry/#metrics","text":"Both Server and Client filters are available for recording request counts and latency, optionally overriding values for the metric names, descriptions and request identification. package guide.reference.opentelemetry import io.opentelemetry.sdk.OpenTelemetrySdk import io.opentelemetry.sdk.metrics.SdkMeterProvider import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader import org.http4k.client.ApacheClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ClientFilters import org.http4k.filter.OpenTelemetryMetrics import org.http4k.filter.ServerFilters import org.http4k.routing.bind import org.http4k.routing.routes fun main () { // test only: this sets up the metrics provider to something we can read val inMemoryMetricReader = InMemoryMetricReader . create () OpenTelemetrySdk . builder () . setMeterProvider ( SdkMeterProvider . builder () . registerMetricReader ( inMemoryMetricReader ) . build () ) . buildAndRegisterGlobal () val server = routes ( \"/metrics\" bind GET to { Response ( OK ) }) // apply metrics filters to a server... val app = ServerFilters . OpenTelemetryMetrics . RequestCounter () . then ( ServerFilters . OpenTelemetryMetrics . RequestTimer ()) . then ( server ) // ... or to a client val client = ClientFilters . OpenTelemetryMetrics . RequestCounter () . then ( ClientFilters . OpenTelemetryMetrics . RequestTimer ()) . then ( ApacheClient ()) // make some calls repeat ( 5 ) { app ( Request ( GET , \"/metrics\" )) client ( Request ( GET , \"https://http4k.org\" )) } // see some results inMemoryMetricReader . collectAllMetrics (). forEach { println ( \"metric: \" + it . name + \", value: \" + ( it . longSumData . points . takeIf { it . isNotEmpty () } ?: it . doubleSumData . points ) ) } }","title":"Metrics"},{"location":"guide/reference/playwright/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-playwright\" ) } About \u00b6 A JUnit extension for simply testing your http4k applications using the Playwright browser-automation library. Create your application as normal and pass to the JUnit extension when registering it. The application is then launched on a random port and a connected browser object injected into the test - requests to non-http URLs are automatically routed to your app. Code \u00b6 package guide.reference.playwright import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.containsSubstring import com.natpryce.hamkrest.equalTo import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.with import org.http4k.lens.Header import org.http4k.playwright.Http4kBrowser import org.http4k.playwright.LaunchPlaywrightBrowser import org.http4k.routing.bind import org.http4k.routing.routes import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension class LaunchPlaywrightBrowserTest { private val app = routes ( \"/foo\" bind GET to { _ : Request -> Response ( OK ). body ( \"foo\" ) }, \"/redirect\" bind GET to { _ : Request -> Response ( Status . FOUND ). with ( Header . LOCATION of Uri . of ( \"https://example.com\" )) }, \"/\" bind GET to { _ : Request -> Response ( OK ). body ( \"helloworld\" ) } ) @RegisterExtension // this defaults to Chromium and the app is launched on a random port val playwright = LaunchPlaywrightBrowser ( app ) @Test fun `can check browser interactions` ( browser : Http4kBrowser ) { with ( browser . newPage ()) { // you don't have to specify the entire URL of the application paths - this is handled automatically assertThat ( String ( navigateHome (). body ()), equalTo ( \"helloworld\" )) assertThat ( String ( navigate ( \"/foo\" ). body ()), equalTo ( \"foo\" )) assertThat ( String ( navigate ( \"/redirect\" ). body ()), containsSubstring ( \"Example Domain\" )) assertThat ( String ( navigate ( \"http://google.com\" ). body ()), containsSubstring ( \"google\" )) } } }","title":"http4k Webdriver Module"},{"location":"guide/reference/playwright/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-playwright\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/playwright/#about","text":"A JUnit extension for simply testing your http4k applications using the Playwright browser-automation library. Create your application as normal and pass to the JUnit extension when registering it. The application is then launched on a random port and a connected browser object injected into the test - requests to non-http URLs are automatically routed to your app.","title":"About"},{"location":"guide/reference/playwright/#code","text":"package guide.reference.playwright import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.containsSubstring import com.natpryce.hamkrest.equalTo import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.with import org.http4k.lens.Header import org.http4k.playwright.Http4kBrowser import org.http4k.playwright.LaunchPlaywrightBrowser import org.http4k.routing.bind import org.http4k.routing.routes import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension class LaunchPlaywrightBrowserTest { private val app = routes ( \"/foo\" bind GET to { _ : Request -> Response ( OK ). body ( \"foo\" ) }, \"/redirect\" bind GET to { _ : Request -> Response ( Status . FOUND ). with ( Header . LOCATION of Uri . of ( \"https://example.com\" )) }, \"/\" bind GET to { _ : Request -> Response ( OK ). body ( \"helloworld\" ) } ) @RegisterExtension // this defaults to Chromium and the app is launched on a random port val playwright = LaunchPlaywrightBrowser ( app ) @Test fun `can check browser interactions` ( browser : Http4kBrowser ) { with ( browser . newPage ()) { // you don't have to specify the entire URL of the application paths - this is handled automatically assertThat ( String ( navigateHome (). body ()), equalTo ( \"helloworld\" )) assertThat ( String ( navigate ( \"/foo\" ). body ()), equalTo ( \"foo\" )) assertThat ( String ( navigate ( \"/redirect\" ). body ()), containsSubstring ( \"Example Domain\" )) assertThat ( String ( navigate ( \"http://google.com\" ). body ()), containsSubstring ( \"google\" )) } } }","title":"Code"},{"location":"guide/reference/resilience4j/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-resilience4j\" ) } About \u00b6 This module provides configurable Filters to provide CircuitBreaking, RateLimiting, Retrying and Bulkheading, by integrating with the awesome Resilience4J library. Circuit Breaking \u00b6 A Circuit Filter detects failures and then Opens for a set period to allow the underlying system to recover. package guide.reference.resilience4j import io.github.resilience4j.circuitbreaker.CircuitBreaker import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType.COUNT_BASED import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration import java.util.ArrayDeque // Circuit state transition: CLOSED (ok) -> OPEN (dead) -> HALF_OPEN (test) -> CLOSED (ok) fun main () { // these example responses are queued up to trigger the circuit state changes val responses = ArrayDeque < Response > () responses . add ( Response ( INTERNAL_SERVER_ERROR )) responses . add ( Response ( OK )) responses . add ( Response ( OK )) // configure the circuit breaker filter here val circuitBreaker = CircuitBreaker . of ( \"circuit\" , CircuitBreakerConfig . custom () . slidingWindow ( 2 , 2 , COUNT_BASED ) . permittedNumberOfCallsInHalfOpenState ( 2 ) . waitDurationInOpenState ( Duration . ofSeconds ( 1 )) . build () ) val circuited = ResilienceFilters . CircuitBreak ( circuitBreaker , isError = { r : Response -> ! r . status . successful } // this defaults to >= 500 ). then { responses . removeFirst () } println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) Thread . sleep ( 1100 ) // wait for reset println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) } Rate Limiting \u00b6 A RateLimit Filter monitors the number of requests over a set window. package guide.reference.resilience4j import io.github.resilience4j.ratelimiter.RateLimiter import io.github.resilience4j.ratelimiter.RateLimiterConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration fun main () { // configure the rate limiter filter here val config = RateLimiterConfig . custom () . limitRefreshPeriod ( Duration . ofSeconds ( 1 )) . limitForPeriod ( 1 ) . timeoutDuration ( Duration . ofMillis ( 10 )). build () // set up the responses to sleep for a bit val rateLimits = ResilienceFilters . RateLimit ( RateLimiter . of ( \"ratelimiter\" , config )) . then { Response ( OK ) } println ( rateLimits ( Request ( GET , \"/\" )). status ) println ( rateLimits ( Request ( GET , \"/\" )). status ) } Retrying \u00b6 A Retrying Filter retries requests if a failure is generated. package guide.reference.resilience4j import io.github.resilience4j.retry.Retry import io.github.resilience4j.retry.RetryConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.util.ArrayDeque fun main () { // configure the retry filter here, with max attempts and backoff val retry = Retry . of ( \"retrying\" , RetryConfig . custom < RetryConfig > () . maxAttempts ( 3 ) . intervalFunction { attempt : Int -> ( attempt * 2 ). toLong () } . build ()) // queued up responses val responses = ArrayDeque < Response > () responses . add ( Response ( INTERNAL_SERVER_ERROR )) responses . add ( Response ( OK )) val retrying = ResilienceFilters . RetryFailures ( retry , isError = { r : Response -> ! r . status . successful } ). then { val response = responses . removeFirst () println ( \"trying request, will return \" + response . status ) response } println ( retrying ( Request ( GET , \"/\" ))) } Bulkheading \u00b6 A Bulkhead Filter limits the amount of parallel calls that can be executed. package guide.reference.resilience4j import io.github.resilience4j.bulkhead.Bulkhead import io.github.resilience4j.bulkhead.BulkheadConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration import kotlin.concurrent.thread fun main () { // configure the Bulkhead filter here val config = BulkheadConfig . custom () . maxConcurrentCalls ( 5 ) . maxWaitDuration ( Duration . ofMillis ( 1000 )) . build () val bulkheading = ResilienceFilters . Bulkheading ( Bulkhead . of ( \"bulkhead\" , config )). then { Thread . sleep ( 100 ) Response ( OK ) } // throw a bunch of requests at the filter - only 5 should pass for ( it in 1. . 10 ) { thread { println ( bulkheading ( Request ( GET , \"/\" )). status ) } } }","title":"Resilience4J"},{"location":"guide/reference/resilience4j/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-resilience4j\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/resilience4j/#about","text":"This module provides configurable Filters to provide CircuitBreaking, RateLimiting, Retrying and Bulkheading, by integrating with the awesome Resilience4J library.","title":"About"},{"location":"guide/reference/resilience4j/#circuit_breaking","text":"A Circuit Filter detects failures and then Opens for a set period to allow the underlying system to recover. package guide.reference.resilience4j import io.github.resilience4j.circuitbreaker.CircuitBreaker import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType.COUNT_BASED import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration import java.util.ArrayDeque // Circuit state transition: CLOSED (ok) -> OPEN (dead) -> HALF_OPEN (test) -> CLOSED (ok) fun main () { // these example responses are queued up to trigger the circuit state changes val responses = ArrayDeque < Response > () responses . add ( Response ( INTERNAL_SERVER_ERROR )) responses . add ( Response ( OK )) responses . add ( Response ( OK )) // configure the circuit breaker filter here val circuitBreaker = CircuitBreaker . of ( \"circuit\" , CircuitBreakerConfig . custom () . slidingWindow ( 2 , 2 , COUNT_BASED ) . permittedNumberOfCallsInHalfOpenState ( 2 ) . waitDurationInOpenState ( Duration . ofSeconds ( 1 )) . build () ) val circuited = ResilienceFilters . CircuitBreak ( circuitBreaker , isError = { r : Response -> ! r . status . successful } // this defaults to >= 500 ). then { responses . removeFirst () } println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) Thread . sleep ( 1100 ) // wait for reset println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) println ( \"Result: \" + circuited ( Request ( GET , \"/\" ) ). status + \" Circuit is: \" + circuitBreaker . state ) }","title":"Circuit Breaking"},{"location":"guide/reference/resilience4j/#rate_limiting","text":"A RateLimit Filter monitors the number of requests over a set window. package guide.reference.resilience4j import io.github.resilience4j.ratelimiter.RateLimiter import io.github.resilience4j.ratelimiter.RateLimiterConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration fun main () { // configure the rate limiter filter here val config = RateLimiterConfig . custom () . limitRefreshPeriod ( Duration . ofSeconds ( 1 )) . limitForPeriod ( 1 ) . timeoutDuration ( Duration . ofMillis ( 10 )). build () // set up the responses to sleep for a bit val rateLimits = ResilienceFilters . RateLimit ( RateLimiter . of ( \"ratelimiter\" , config )) . then { Response ( OK ) } println ( rateLimits ( Request ( GET , \"/\" )). status ) println ( rateLimits ( Request ( GET , \"/\" )). status ) }","title":"Rate Limiting"},{"location":"guide/reference/resilience4j/#retrying","text":"A Retrying Filter retries requests if a failure is generated. package guide.reference.resilience4j import io.github.resilience4j.retry.Retry import io.github.resilience4j.retry.RetryConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.util.ArrayDeque fun main () { // configure the retry filter here, with max attempts and backoff val retry = Retry . of ( \"retrying\" , RetryConfig . custom < RetryConfig > () . maxAttempts ( 3 ) . intervalFunction { attempt : Int -> ( attempt * 2 ). toLong () } . build ()) // queued up responses val responses = ArrayDeque < Response > () responses . add ( Response ( INTERNAL_SERVER_ERROR )) responses . add ( Response ( OK )) val retrying = ResilienceFilters . RetryFailures ( retry , isError = { r : Response -> ! r . status . successful } ). then { val response = responses . removeFirst () println ( \"trying request, will return \" + response . status ) response } println ( retrying ( Request ( GET , \"/\" ))) }","title":"Retrying"},{"location":"guide/reference/resilience4j/#bulkheading","text":"A Bulkhead Filter limits the amount of parallel calls that can be executed. package guide.reference.resilience4j import io.github.resilience4j.bulkhead.Bulkhead import io.github.resilience4j.bulkhead.BulkheadConfig import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ResilienceFilters import java.time.Duration import kotlin.concurrent.thread fun main () { // configure the Bulkhead filter here val config = BulkheadConfig . custom () . maxConcurrentCalls ( 5 ) . maxWaitDuration ( Duration . ofMillis ( 1000 )) . build () val bulkheading = ResilienceFilters . Bulkheading ( Bulkhead . of ( \"bulkhead\" , config )). then { Thread . sleep ( 100 ) Response ( OK ) } // throw a bunch of requests at the filter - only 5 should pass for ( it in 1. . 10 ) { thread { println ( bulkheading ( Request ( GET , \"/\" )). status ) } } }","title":"Bulkheading"},{"location":"guide/reference/serverless/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // AWS Lambda: implementation ( \"org.http4k:http4k-serverless-lambda\" ) // Google Cloud Functions: implementation ( \"org.http4k:http4k-serverless-gcf\" ) // Apache OpenWhisk (IBM Cloud Functions): implementation ( \"org.http4k:http4k-serverless-openwhisk\" ) // Azure Functions: implementation ( \"org.http4k:http4k-serverless-azure\" ) // Alibaba Function Compute: implementation ( \"org.http4k:http4k-serverless-alibaba\" ) // Tencent Serverless Cloud Functions: implementation ( \"org.http4k:http4k-serverless-tencent\" ) } About \u00b6 These modules provide integration with Serverless deployment environments, such as AWS Lambda or Google Cloud Functions by implementing a single interface. AWS Lambda integration (HTTP apps) \u00b6 Since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. This has the added bonus that you can test your applications in a local environment and then simply deploy them to AWS Lambda via S3 upload. In order to achieve this, only a single interface AppLoader needs to be implemented and a simple extension of AwsLambdaFunction supplied depending on which invocation type is required - Direct, ApiGateway V1/2 or ApplicationLoadBalancer. This is far from a complete guide, but configuring AWS Lambda and the API Gateway involves several stages: Users, Roles and Policies for the API Gateway and Lambda. API Gateway to proxy all requests to your Lambda. Building your http4k application into a standard UberJar. Optionally using Proguard to minify the JAR. Package up the (minified) JAR into a standard Zip distribution. Create and configure the Lambda function, and at the same time: Upload the standard Zip file to S3. Set the function execution to call the main http4k entry point: guide.modules.serverless.lambda.FunctionsExampleEntryClass We hope to soon provide some tools to automate at least some of the above process, or at least document it somewhat. However, AWS is a complicated beast and many people have a preferred way to set it up: CloudFormation templates, Serverless framework, Terraform, etc. In the meantime, here is an example of how the AppLoader is created and how to launch the app locally: Code \u00b6 package guide.reference.serverless.lambda import org.http4k.client.ApacheClient import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.ApiGatewayV1LambdaFunction import org.http4k.serverless.AppLoader // This AppLoader is responsible for building our HttpHandler which is supplied to AWS // It is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 17 )) } } // This class is the entry-point for the function call - configure it when deploying class FunctionsExampleEntryClass : ApiGatewayV1LambdaFunction ( TweetEchoLambda ) fun main () { // Launching your Lambda Function locally - by simply providing the operating ENVIRONMENT map as would // be configured on AWS. fun runLambdaLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( mapOf ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () val response = ApacheClient ()( Request ( POST , \"http://localhost:8000/\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) println ( response ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening at AWS. fun runLambdaAsAwsWould () { println ( \"RUNNING AS LAMBDA:\" ) // val response = FunctionsExampleEntryClass().handleRequest(ServerlessMoshi.asInputStream( // mapOf( // \"path\" to \"/\", // \"queryStringParameters\" to emptyMap(), // \"body\" to \"hello hello hello, i suppose this isn't 140 characters anymore..\", // \"headers\" to emptyMap(), // \"isBase64Encoded\" to false, // \"httpMethod\" to \"GET\" // ) // ), mock()) // println(response) } runLambdaLocally () runLambdaAsAwsWould () } AWS Lambda integration (Event-based apps) \u00b6 http4k also supports writing Event-based functions to receive AWS events from services like SQS and Dynamo. One advantage of using http4k version is that it uses the AWS SDK RequestStreamHandler instead of the standard RequestHandler - which avoids the heavyweight Jackson deserialisation process (we use Moshi under the covers) utilised by the standard AWS runtime. To use this events functionality, you should also import the AWS Events JAR: implementation ( \"com.amazonaws:aws-lambda-java-events:3.8.0\" ) Similarly to HttpHandler, for event processing in a functional style, the main body of the Lambda function is encapsulated in a single interface FnHandler . This typesafe class is created by an FnLoader function and simply passed into an extension of AwsLambdaEventFunction - which is the class configured as the entry point of your AWS lambda. The process of configuration is the same as for HTTP apps above. Code \u00b6 import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.events.SQSEvent import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage import dev.forkhandles.mock4k.mock import org.http4k.client.JavaHttpClient import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.AwsLambdaMoshi import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AwsLambdaEventFunction import org.http4k.serverless.FnHandler import org.http4k.serverless.FnLoader import org.http4k.serverless.ServerlessFilters.ReportFnTransaction import org.http4k.serverless.then import java.io.ByteArrayOutputStream // This is the handler for the incoming AWS SQS event. It's just a function so you can call it without any infrastructure fun EventFnHandler ( http : HttpHandler ) = FnHandler { e : SQSEvent , _ : Context -> e . records . forEach { http ( Request ( POST , \"http://localhost:8080/\" ). body ( it . body . reversed ())) } \"processed ${ e . records . size } messages\" } // We can add filters to the FnHandler if we want to - in this case print the transaction (with the letency). val loggingFunction = ReportFnTransaction < SQSEvent , Context , String > { tx -> println ( tx ) } // The FnLoader is responsible for constructing the handler and for handling the serialisation of the request and response fun EventFnLoader ( http : HttpHandler ) = FnLoader { env : Map < String , String > -> loggingFunction . then ( EventFnHandler ( http )) } // This class is the entry-point for the Lambda function call - configure it when deploying class EventFunction : AwsLambdaEventFunction ( EventFnLoader ( JavaHttpClient ())) fun main () { // this server receives the reversed event val receivingServer = { req : Request -> println ( req . bodyString ()) Response ( OK ) }. asServer ( SunHttp ( 8080 )). start () val sqsEvent = SQSEvent (). apply { records = listOf ( SQSMessage (). apply { body = \"hello world\" }, SQSMessage (). apply { body = \"goodbye world\" } ) } fun runLambdaInMemoryOrForTesting () { println ( \"RUNNING In memory:\" ) val app = EventFnHandler ( JavaHttpClient ()) app ( sqsEvent , mock ()) } fun runLambdaAsAwsWould () { println ( \"RUNNING as AWS would invoke the function:\" ) val out = ByteArrayOutputStream () EventFunction (). handleRequest ( AwsLambdaMoshi . asInputStream ( sqsEvent ), out , mock ()) // the response is empty b println ( out . toString ()) } runLambdaInMemoryOrForTesting () runLambdaAsAwsWould () receivingServer . stop () } Google Cloud Functions integration \u00b6 Google Cloud Functions are triggered in the cloud by calling an entry point class which implements their HttpFunction interface. In order to achieve this in http4k, only a single interface AppLoader needs to be implemented, and then a simple extension class needs to be written which accepts this interface. You can compose filters and handlers as usual and pass them to the constructor of the GoogleCloudFunction and make your entry point class extend from it. Here is an example: Code \u00b6 package guide.reference.serverless.gcf import org.http4k.client.ApacheClient import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AppLoader import org.http4k.serverless.FakeGCFRequest import org.http4k.serverless.FakeGCFResponse import org.http4k.serverless.GoogleCloudHttpFunction // This AppLoader is responsible for building our HttpHandler which is supplied to GCF // Along with the extension class below, is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { private val timer = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } override fun invoke ( env : Map < String , String > ): HttpHandler = timer . then ( routes ( \"/echo\" bind POST to { Response ( OK ). body ( it . bodyString (). take ( 18 )) } ) ) } // This class is the entry-point for the function call - configure it when deploying class FunctionsExampleEntryClass : GoogleCloudHttpFunction ( TweetEchoLambda ) fun main () { // Launching your Function locally - by simply providing the operating ENVIRONMENT map as would // be configured in GCP. fun runFunctionLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( System . getenv ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening at GCP. fun runFunctionAsGCFWould () { println ( \"RUNNING AS GCF:\" ) val response = FakeGCFResponse () FunctionsExampleEntryClass (). service ( FakeGCFRequest ( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ), response ) println ( response . status ) println ( response . headers ) println ( response . body ) } runFunctionLocally () runFunctionAsGCFWould () } If you are using gradle, gcloud can't deploy the function directly from the project, you must build the fat jar first. Applying this plugin shadow jar will provide you with appropriate gradle task to build the fat jar. After building, and having your jar as the only file in the libs/ folder you can deploy the function from the parent folder with : gcloud functions deploy example-function --runtime=java11 --entry-point=guide.modules.serverless.gcf.FunctionsExampleEntryClass --trigger-http --source=libs/ If you wan't to invoke functions locally you can do it with this gradle setup and passing a -PrunFunction.target parameter to the build task : configurations { invoker } dependencies { invoker ' com . google . cloud . functions . invoker : java - function - invoker : 1.0 . 0 - alpha - 2 - rc5 ' } tasks . register ( \"runFunction\" , JavaExec ) { main = ' com . google . cloud . functions . invoker . runner . Invoker ' classpath ( configurations . invoker ) inputs . files ( configurations . runtimeClasspath , sourceSets . main . output ) args ( ' -- target ' , project . findProperty ( ' runFunction . target ' ), ' -- port ' , project . findProperty ( ' runFunction . port ' ) ?: 8080 ) doFirst { args ( ' -- classpath ' , files ( configurations . runtimeClasspath , sourceSets . main . output ). asPath ) } } If you are using Maven, you do not have to build the fat JAR and can deploy the function from the project folder. Simple example on how to setup pom.xml to run functions locally and deploy Maven project to the cloud is shown here Apache OpenWhisk integration \u00b6 OpenWhisk has a Java runtime which is triggered by calling an entry point class which contains a static main() function receiving a GSON JsonObject . In order to achieve this in http4k, only a single interface AppLoader needs to be implemented, and then a simple class needs to be written which uses the OpenWhiskFunction wrapper. Because of the OpenWhisk runtime usage of the library, a compileOnly dependency also needs to be added on GSON to ensure that your function can build correctly. Code \u00b6 package guide.reference.serverless.openwhisk import com.google.gson.JsonObject import org.http4k.client.ApacheClient import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.format.Gson import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AppLoader import org.http4k.serverless.FakeOpenWhiskRawRequest import org.http4k.serverless.OpenWhiskFunction // This AppLoader is responsible for building our HttpHandler which is supplied to OpenWhisk // Along with the extension class below, is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { private val timer = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } override fun invoke ( env : Map < String , String > ): HttpHandler = timer . then ( routes ( \"/echo\" bind POST to { Response ( OK ). body ( it . bodyString (). take ( 18 )) } ) ) } // This class is the entry-point for the function call - configure it when deploying object FunctionsExampleEntryClass { @JvmStatic fun main ( request : JsonObject ) = OpenWhiskFunction ( TweetEchoLambda )( request ) } fun main () { // Launching your Function locally - by simply providing the operating ENVIRONMENT map as would // be configured in OpenWhisk. fun runFunctionLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( System . getenv ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening in OpenWhisk. fun runFunctionAsOpenWhiskWould () { println ( \"RUNNING AS OpenWhisk:\" ) val fakeOpenWhiskRequest = FakeOpenWhiskRawRequest ( \"POST\" , \"/echo\" , \"\" , emptyMap (), \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) val response = FunctionsExampleEntryClass . main ( Gson . asJsonObject ( fakeOpenWhiskRequest ) as JsonObject ) println ( response ) } runFunctionLocally () runFunctionAsOpenWhiskWould () } Packaging of the app should be done using ShadowJar and then an action created with the wsk CLI: wsk -i action create myFunctionName myApp.jar --main org.http4k.example.MyFunctionClass --web true Locally, you can then just call the function with curl : curl -k `wsk -i action get test --url | tail -1`","title":"Serverless backend"},{"location":"guide/reference/serverless/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // AWS Lambda: implementation ( \"org.http4k:http4k-serverless-lambda\" ) // Google Cloud Functions: implementation ( \"org.http4k:http4k-serverless-gcf\" ) // Apache OpenWhisk (IBM Cloud Functions): implementation ( \"org.http4k:http4k-serverless-openwhisk\" ) // Azure Functions: implementation ( \"org.http4k:http4k-serverless-azure\" ) // Alibaba Function Compute: implementation ( \"org.http4k:http4k-serverless-alibaba\" ) // Tencent Serverless Cloud Functions: implementation ( \"org.http4k:http4k-serverless-tencent\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/serverless/#about","text":"These modules provide integration with Serverless deployment environments, such as AWS Lambda or Google Cloud Functions by implementing a single interface.","title":"About"},{"location":"guide/reference/serverless/#aws_lambda_integration_http_apps","text":"Since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. This has the added bonus that you can test your applications in a local environment and then simply deploy them to AWS Lambda via S3 upload. In order to achieve this, only a single interface AppLoader needs to be implemented and a simple extension of AwsLambdaFunction supplied depending on which invocation type is required - Direct, ApiGateway V1/2 or ApplicationLoadBalancer. This is far from a complete guide, but configuring AWS Lambda and the API Gateway involves several stages: Users, Roles and Policies for the API Gateway and Lambda. API Gateway to proxy all requests to your Lambda. Building your http4k application into a standard UberJar. Optionally using Proguard to minify the JAR. Package up the (minified) JAR into a standard Zip distribution. Create and configure the Lambda function, and at the same time: Upload the standard Zip file to S3. Set the function execution to call the main http4k entry point: guide.modules.serverless.lambda.FunctionsExampleEntryClass We hope to soon provide some tools to automate at least some of the above process, or at least document it somewhat. However, AWS is a complicated beast and many people have a preferred way to set it up: CloudFormation templates, Serverless framework, Terraform, etc. In the meantime, here is an example of how the AppLoader is created and how to launch the app locally:","title":"AWS Lambda integration (HTTP apps)"},{"location":"guide/reference/serverless/#code","text":"package guide.reference.serverless.lambda import org.http4k.client.ApacheClient import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.ApiGatewayV1LambdaFunction import org.http4k.serverless.AppLoader // This AppLoader is responsible for building our HttpHandler which is supplied to AWS // It is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 17 )) } } // This class is the entry-point for the function call - configure it when deploying class FunctionsExampleEntryClass : ApiGatewayV1LambdaFunction ( TweetEchoLambda ) fun main () { // Launching your Lambda Function locally - by simply providing the operating ENVIRONMENT map as would // be configured on AWS. fun runLambdaLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( mapOf ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () val response = ApacheClient ()( Request ( POST , \"http://localhost:8000/\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) println ( response ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening at AWS. fun runLambdaAsAwsWould () { println ( \"RUNNING AS LAMBDA:\" ) // val response = FunctionsExampleEntryClass().handleRequest(ServerlessMoshi.asInputStream( // mapOf( // \"path\" to \"/\", // \"queryStringParameters\" to emptyMap(), // \"body\" to \"hello hello hello, i suppose this isn't 140 characters anymore..\", // \"headers\" to emptyMap(), // \"isBase64Encoded\" to false, // \"httpMethod\" to \"GET\" // ) // ), mock()) // println(response) } runLambdaLocally () runLambdaAsAwsWould () }","title":"Code"},{"location":"guide/reference/serverless/#aws_lambda_integration_event-based_apps","text":"http4k also supports writing Event-based functions to receive AWS events from services like SQS and Dynamo. One advantage of using http4k version is that it uses the AWS SDK RequestStreamHandler instead of the standard RequestHandler - which avoids the heavyweight Jackson deserialisation process (we use Moshi under the covers) utilised by the standard AWS runtime. To use this events functionality, you should also import the AWS Events JAR: implementation ( \"com.amazonaws:aws-lambda-java-events:3.8.0\" ) Similarly to HttpHandler, for event processing in a functional style, the main body of the Lambda function is encapsulated in a single interface FnHandler . This typesafe class is created by an FnLoader function and simply passed into an extension of AwsLambdaEventFunction - which is the class configured as the entry point of your AWS lambda. The process of configuration is the same as for HTTP apps above.","title":"AWS Lambda integration (Event-based apps)"},{"location":"guide/reference/serverless/#code_1","text":"import com.amazonaws.services.lambda.runtime.Context import com.amazonaws.services.lambda.runtime.events.SQSEvent import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage import dev.forkhandles.mock4k.mock import org.http4k.client.JavaHttpClient import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.AwsLambdaMoshi import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AwsLambdaEventFunction import org.http4k.serverless.FnHandler import org.http4k.serverless.FnLoader import org.http4k.serverless.ServerlessFilters.ReportFnTransaction import org.http4k.serverless.then import java.io.ByteArrayOutputStream // This is the handler for the incoming AWS SQS event. It's just a function so you can call it without any infrastructure fun EventFnHandler ( http : HttpHandler ) = FnHandler { e : SQSEvent , _ : Context -> e . records . forEach { http ( Request ( POST , \"http://localhost:8080/\" ). body ( it . body . reversed ())) } \"processed ${ e . records . size } messages\" } // We can add filters to the FnHandler if we want to - in this case print the transaction (with the letency). val loggingFunction = ReportFnTransaction < SQSEvent , Context , String > { tx -> println ( tx ) } // The FnLoader is responsible for constructing the handler and for handling the serialisation of the request and response fun EventFnLoader ( http : HttpHandler ) = FnLoader { env : Map < String , String > -> loggingFunction . then ( EventFnHandler ( http )) } // This class is the entry-point for the Lambda function call - configure it when deploying class EventFunction : AwsLambdaEventFunction ( EventFnLoader ( JavaHttpClient ())) fun main () { // this server receives the reversed event val receivingServer = { req : Request -> println ( req . bodyString ()) Response ( OK ) }. asServer ( SunHttp ( 8080 )). start () val sqsEvent = SQSEvent (). apply { records = listOf ( SQSMessage (). apply { body = \"hello world\" }, SQSMessage (). apply { body = \"goodbye world\" } ) } fun runLambdaInMemoryOrForTesting () { println ( \"RUNNING In memory:\" ) val app = EventFnHandler ( JavaHttpClient ()) app ( sqsEvent , mock ()) } fun runLambdaAsAwsWould () { println ( \"RUNNING as AWS would invoke the function:\" ) val out = ByteArrayOutputStream () EventFunction (). handleRequest ( AwsLambdaMoshi . asInputStream ( sqsEvent ), out , mock ()) // the response is empty b println ( out . toString ()) } runLambdaInMemoryOrForTesting () runLambdaAsAwsWould () receivingServer . stop () }","title":"Code"},{"location":"guide/reference/serverless/#google_cloud_functions_integration","text":"Google Cloud Functions are triggered in the cloud by calling an entry point class which implements their HttpFunction interface. In order to achieve this in http4k, only a single interface AppLoader needs to be implemented, and then a simple extension class needs to be written which accepts this interface. You can compose filters and handlers as usual and pass them to the constructor of the GoogleCloudFunction and make your entry point class extend from it. Here is an example:","title":"Google Cloud Functions integration"},{"location":"guide/reference/serverless/#code_2","text":"package guide.reference.serverless.gcf import org.http4k.client.ApacheClient import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AppLoader import org.http4k.serverless.FakeGCFRequest import org.http4k.serverless.FakeGCFResponse import org.http4k.serverless.GoogleCloudHttpFunction // This AppLoader is responsible for building our HttpHandler which is supplied to GCF // Along with the extension class below, is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { private val timer = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } override fun invoke ( env : Map < String , String > ): HttpHandler = timer . then ( routes ( \"/echo\" bind POST to { Response ( OK ). body ( it . bodyString (). take ( 18 )) } ) ) } // This class is the entry-point for the function call - configure it when deploying class FunctionsExampleEntryClass : GoogleCloudHttpFunction ( TweetEchoLambda ) fun main () { // Launching your Function locally - by simply providing the operating ENVIRONMENT map as would // be configured in GCP. fun runFunctionLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( System . getenv ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening at GCP. fun runFunctionAsGCFWould () { println ( \"RUNNING AS GCF:\" ) val response = FakeGCFResponse () FunctionsExampleEntryClass (). service ( FakeGCFRequest ( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ), response ) println ( response . status ) println ( response . headers ) println ( response . body ) } runFunctionLocally () runFunctionAsGCFWould () } If you are using gradle, gcloud can't deploy the function directly from the project, you must build the fat jar first. Applying this plugin shadow jar will provide you with appropriate gradle task to build the fat jar. After building, and having your jar as the only file in the libs/ folder you can deploy the function from the parent folder with : gcloud functions deploy example-function --runtime=java11 --entry-point=guide.modules.serverless.gcf.FunctionsExampleEntryClass --trigger-http --source=libs/ If you wan't to invoke functions locally you can do it with this gradle setup and passing a -PrunFunction.target parameter to the build task : configurations { invoker } dependencies { invoker ' com . google . cloud . functions . invoker : java - function - invoker : 1.0 . 0 - alpha - 2 - rc5 ' } tasks . register ( \"runFunction\" , JavaExec ) { main = ' com . google . cloud . functions . invoker . runner . Invoker ' classpath ( configurations . invoker ) inputs . files ( configurations . runtimeClasspath , sourceSets . main . output ) args ( ' -- target ' , project . findProperty ( ' runFunction . target ' ), ' -- port ' , project . findProperty ( ' runFunction . port ' ) ?: 8080 ) doFirst { args ( ' -- classpath ' , files ( configurations . runtimeClasspath , sourceSets . main . output ). asPath ) } } If you are using Maven, you do not have to build the fat JAR and can deploy the function from the project folder. Simple example on how to setup pom.xml to run functions locally and deploy Maven project to the cloud is shown here","title":"Code"},{"location":"guide/reference/serverless/#apache_openwhisk_integration","text":"OpenWhisk has a Java runtime which is triggered by calling an entry point class which contains a static main() function receiving a GSON JsonObject . In order to achieve this in http4k, only a single interface AppLoader needs to be implemented, and then a simple class needs to be written which uses the OpenWhiskFunction wrapper. Because of the OpenWhisk runtime usage of the library, a compileOnly dependency also needs to be added on GSON to ensure that your function can build correctly.","title":"Apache OpenWhisk integration"},{"location":"guide/reference/serverless/#code_3","text":"package guide.reference.serverless.openwhisk import com.google.gson.JsonObject import org.http4k.client.ApacheClient import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.format.Gson import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.serverless.AppLoader import org.http4k.serverless.FakeOpenWhiskRawRequest import org.http4k.serverless.OpenWhiskFunction // This AppLoader is responsible for building our HttpHandler which is supplied to OpenWhisk // Along with the extension class below, is the only actual piece of code that needs to be written. object TweetEchoLambda : AppLoader { private val timer = Filter { next : HttpHandler -> { request : Request -> val start = System . currentTimeMillis () val response = next ( request ) val latency = System . currentTimeMillis () - start println ( \"I took $ latency ms\" ) response } } override fun invoke ( env : Map < String , String > ): HttpHandler = timer . then ( routes ( \"/echo\" bind POST to { Response ( OK ). body ( it . bodyString (). take ( 18 )) } ) ) } // This class is the entry-point for the function call - configure it when deploying object FunctionsExampleEntryClass { @JvmStatic fun main ( request : JsonObject ) = OpenWhiskFunction ( TweetEchoLambda )( request ) } fun main () { // Launching your Function locally - by simply providing the operating ENVIRONMENT map as would // be configured in OpenWhisk. fun runFunctionLocally () { println ( \"RUNNING LOCALLY:\" ) val app : HttpHandler = TweetEchoLambda ( System . getenv ()) val localLambda = app . asServer ( SunHttp ( 8000 )). start () println ( ApacheClient ()( Request ( POST , \"http://localhost:8000/echo\" ). body ( \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) ) ) localLambda . stop () } // the following code is purely here for demonstration purposes, to explain exactly what is happening in OpenWhisk. fun runFunctionAsOpenWhiskWould () { println ( \"RUNNING AS OpenWhisk:\" ) val fakeOpenWhiskRequest = FakeOpenWhiskRawRequest ( \"POST\" , \"/echo\" , \"\" , emptyMap (), \"hello hello hello, i suppose this isn't 140 characters anymore..\" ) val response = FunctionsExampleEntryClass . main ( Gson . asJsonObject ( fakeOpenWhiskRequest ) as JsonObject ) println ( response ) } runFunctionLocally () runFunctionAsOpenWhiskWould () } Packaging of the app should be done using ShadowJar and then an action created with the wsk CLI: wsk -i action create myFunctionName myApp.jar --main org.http4k.example.MyFunctionClass --web true Locally, you can then just call the function with curl : curl -k `wsk -i action get test --url | tail -1`","title":"Code"},{"location":"guide/reference/servers/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Apache v5: implementation ( \"org.http4k:http4k-server-apache\" ) // Apache v4: implementation ( \"org.http4k:http4k-server-apache4\" ) // Jetty & JettyLoom: implementation ( \"org.http4k:http4k-server-jetty\" ) // Helidon (Loom): implementation ( \"org.http4k:http4k-server-helidon\" ) // Ktor CIO: implementation ( \"org.http4k:http4k-server-ktorcio\" ) // Ktor Netty: implementation ( \"org.http4k:http4k-server-ktornetty\" ) // Netty: implementation ( \"org.http4k:http4k-server-netty\" ) // Ratpack: implementation ( \"org.http4k:http4k-server-ratpack\" ) // Undertow: implementation ( \"org.http4k:http4k-server-undertow\" ) // Java WebSocket: implementation ( \"org.http4k:http4k-server-websocket\" ) // SunHttp & SunHttpLoom (for development only): implementation ( \"org.http4k:http4k-core\" ) } About \u00b6 Server-backend modules provide a consistent API to mount HttpHandlers into the specified container in 1 LOC, by simply passing it to the relevant ServerConfig implementation (in this case Jetty ): Code \u00b6 package guide.reference.servers import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { { _ : Request -> Response ( OK ). body ( \"Hello World\" ) }. asServer ( Jetty ( 8000 )). start () } Customisation \u00b6 Each of the server backends implement an interface ServerConfig , which is written with sensible defaults for the server in questions, but is also designed to be used as a starting point for tweaking to API user needs. To customize, simply use the relevant ServerConfig class as a starting point and reimplement as required. See the how-to guides for an example of this in use.","title":"Server backend"},{"location":"guide/reference/servers/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Apache v5: implementation ( \"org.http4k:http4k-server-apache\" ) // Apache v4: implementation ( \"org.http4k:http4k-server-apache4\" ) // Jetty & JettyLoom: implementation ( \"org.http4k:http4k-server-jetty\" ) // Helidon (Loom): implementation ( \"org.http4k:http4k-server-helidon\" ) // Ktor CIO: implementation ( \"org.http4k:http4k-server-ktorcio\" ) // Ktor Netty: implementation ( \"org.http4k:http4k-server-ktornetty\" ) // Netty: implementation ( \"org.http4k:http4k-server-netty\" ) // Ratpack: implementation ( \"org.http4k:http4k-server-ratpack\" ) // Undertow: implementation ( \"org.http4k:http4k-server-undertow\" ) // Java WebSocket: implementation ( \"org.http4k:http4k-server-websocket\" ) // SunHttp & SunHttpLoom (for development only): implementation ( \"org.http4k:http4k-core\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/servers/#about","text":"Server-backend modules provide a consistent API to mount HttpHandlers into the specified container in 1 LOC, by simply passing it to the relevant ServerConfig implementation (in this case Jetty ):","title":"About"},{"location":"guide/reference/servers/#code","text":"package guide.reference.servers import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Jetty import org.http4k.server.asServer fun main () { { _ : Request -> Response ( OK ). body ( \"Hello World\" ) }. asServer ( Jetty ( 8000 )). start () }","title":"Code"},{"location":"guide/reference/servers/#customisation","text":"Each of the server backends implement an interface ServerConfig , which is written with sensible defaults for the server in questions, but is also designed to be used as a starting point for tweaking to API user needs. To customize, simply use the relevant ServerConfig class as a starting point and reimplement as required. See the how-to guides for an example of this in use.","title":"Customisation"},{"location":"guide/reference/servicevirtualisation/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-servirtium\" ) } About \u00b6 Service Virtualisation testing technology provides a way of declaring contracts which can record HTTP conversations to a custom Markdown format and then replaying them later offline. http4k provides a fully featured implementation of the Servirtium solution to implement this concept. The basic idea is that you define an abstract contract Class/Interface which describes the expected behaviour for a system using a Client class (aka the Client-Under-Test ). This contract is then implemented twice: In a Recording contract - using a MiTM proxy which sits between the Client-Under-Test and the real service. This proxy records the HTTP traffic to a custom Markdown format which can be stored in a VCS, and can be configured to remove the dynamic sections of the traffic such as Date headers etc. In a Replaying contract - using an MiTM server which matches incoming traffic and replays a recorded conversation for the matched requests in order from the Markdown file. The result of these 2 implementations is that we can exercise the Client-Under-Test code against different versions of the contract without performing any actual remote calls to prove compatibility. This is important because there may be complicated orchestration required for testing against a real system, or the end service may be unavailable or flaky. http4k provides a few different pieces of support for [Serviritum]: [JUnit5] extensions which provide the record/replay behaviour, providing a way to record and replay contracts without the use of a real server. This is only compatible when the Client-Under-Test utilises a http4k Client, since it leverages the Server-as-a-Function paradigm. MiTM proxy servers for record/replay behaviour, by inserting themselves as a proxy in the HTTP call chain and intercepting the HTTP traffic. This is compatible with HTTP clients using any JVM technology, not just http4k services - so can be used as a general JVM-based solution for implementing Servirtium -style tests. A Storage Provider abstraction for storing and loading recorded contracts from various locations including disk and directly from GitHub . Examples of use \u00b6 Climate API Demo: github.com/http4k/servirtium-demo-kotlin-climate-tck . The climate API tested uses a simple programatic wrapper for World Bank's climate-data service. It can respond to requests with XML or JSON payloads, and the http4k-testing-servirtium module can record and payback either. This is a standard showcase for Servirtium. Kotlin Examples: \u00b6 Playback of a Servirtium recording: kotlin/DiskPlaybackClimateApiTests.kt Making a Servirtium recording: kotlin/DiskRecordingClimateApiTest.kt In the same package, see also a direct example for contrast, and one that can playback from recordings themselves on a website rather than from local disk. Java Examples: \u00b6 Playback of a Servirtium recording: java/DiskReplayClimateApiTests.java Making a Servirtium recording: java/DiskRecordingClimateApiTests.java In the same package, see also a direct example for contrast, and one that can playback from recordings themselves on a website rather than from local disk.","title":"Service Virtualisation"},{"location":"guide/reference/servicevirtualisation/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-servirtium\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/servicevirtualisation/#about","text":"Service Virtualisation testing technology provides a way of declaring contracts which can record HTTP conversations to a custom Markdown format and then replaying them later offline. http4k provides a fully featured implementation of the Servirtium solution to implement this concept. The basic idea is that you define an abstract contract Class/Interface which describes the expected behaviour for a system using a Client class (aka the Client-Under-Test ). This contract is then implemented twice: In a Recording contract - using a MiTM proxy which sits between the Client-Under-Test and the real service. This proxy records the HTTP traffic to a custom Markdown format which can be stored in a VCS, and can be configured to remove the dynamic sections of the traffic such as Date headers etc. In a Replaying contract - using an MiTM server which matches incoming traffic and replays a recorded conversation for the matched requests in order from the Markdown file. The result of these 2 implementations is that we can exercise the Client-Under-Test code against different versions of the contract without performing any actual remote calls to prove compatibility. This is important because there may be complicated orchestration required for testing against a real system, or the end service may be unavailable or flaky. http4k provides a few different pieces of support for [Serviritum]: [JUnit5] extensions which provide the record/replay behaviour, providing a way to record and replay contracts without the use of a real server. This is only compatible when the Client-Under-Test utilises a http4k Client, since it leverages the Server-as-a-Function paradigm. MiTM proxy servers for record/replay behaviour, by inserting themselves as a proxy in the HTTP call chain and intercepting the HTTP traffic. This is compatible with HTTP clients using any JVM technology, not just http4k services - so can be used as a general JVM-based solution for implementing Servirtium -style tests. A Storage Provider abstraction for storing and loading recorded contracts from various locations including disk and directly from GitHub .","title":"About"},{"location":"guide/reference/servicevirtualisation/#examples_of_use","text":"Climate API Demo: github.com/http4k/servirtium-demo-kotlin-climate-tck . The climate API tested uses a simple programatic wrapper for World Bank's climate-data service. It can respond to requests with XML or JSON payloads, and the http4k-testing-servirtium module can record and payback either. This is a standard showcase for Servirtium.","title":"Examples of use"},{"location":"guide/reference/servicevirtualisation/#kotlin_examples","text":"Playback of a Servirtium recording: kotlin/DiskPlaybackClimateApiTests.kt Making a Servirtium recording: kotlin/DiskRecordingClimateApiTest.kt In the same package, see also a direct example for contrast, and one that can playback from recordings themselves on a website rather than from local disk.","title":"Kotlin Examples:"},{"location":"guide/reference/servicevirtualisation/#java_examples","text":"Playback of a Servirtium recording: java/DiskReplayClimateApiTests.java Making a Servirtium recording: java/DiskRecordingClimateApiTests.java In the same package, see also a direct example for contrast, and one that can playback from recordings themselves on a website rather than from local disk.","title":"Java Examples:"},{"location":"guide/reference/strikt/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-strikt\" ) } About \u00b6 A set of Strikt matchers for use when testing http4k apps. Code \u00b6 package guide.reference.strikt import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.strikt.body import org.http4k.strikt.bodyString import org.http4k.strikt.header import org.http4k.strikt.query import org.http4k.strikt.status import strikt.api.expectThat import strikt.assertions.isEqualTo fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status expectThat ( Response ( OK )). status . isEqualTo ( OK ) // query expectThat ( request ). query ( \"a\" ). isEqualTo ( \"b\" ) // header expectThat ( request ). header ( \"my header\" ). isEqualTo ( \"a value\" ) // body expectThat ( request ). bodyString . isEqualTo ( \"http4k is cool\" ) expectThat ( request ). body . isEqualTo ( Body ( \"http4k is cool\" )) // composite expectThat ( request ) { bodyString . isEqualTo ( \"http4k is cool\" ) query ( \"a\" ). isEqualTo ( \"b\" ) } }","title":"Strikt"},{"location":"guide/reference/strikt/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-strikt\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/strikt/#about","text":"A set of Strikt matchers for use when testing http4k apps.","title":"About"},{"location":"guide/reference/strikt/#code","text":"package guide.reference.strikt import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.strikt.body import org.http4k.strikt.bodyString import org.http4k.strikt.header import org.http4k.strikt.query import org.http4k.strikt.status import strikt.api.expectThat import strikt.assertions.isEqualTo fun main () { val request = Request ( POST , \"/?a=b\" ). body ( \"http4k is cool\" ). header ( \"my header\" , \"a value\" ) // status expectThat ( Response ( OK )). status . isEqualTo ( OK ) // query expectThat ( request ). query ( \"a\" ). isEqualTo ( \"b\" ) // header expectThat ( request ). header ( \"my header\" ). isEqualTo ( \"a value\" ) // body expectThat ( request ). bodyString . isEqualTo ( \"http4k is cool\" ) expectThat ( request ). body . isEqualTo ( Body ( \"http4k is cool\" )) // composite expectThat ( request ) { bodyString . isEqualTo ( \"http4k is cool\" ) query ( \"a\" ). isEqualTo ( \"b\" ) } }","title":"Code"},{"location":"guide/reference/templating/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Dust: implementation ( \"org.http4k:http4k-template-dust\" ) // Freemarker: implementation ( \"org.http4k:http4k-template-freemarker\" ) // Handlebars: implementation ( \"org.http4k:http4k-template-handlebars\" ) // Pug4j: implementation ( \"org.http4k:http4k-template-pug4j\" ) // JTE: implementation ( \"org.http4k:http4k-template-jte\" ) // Pebble: implementation ( \"org.http4k:http4k-template-pebble\" ) // Rocker: implementation ( \"org.http4k:http4k-template-rocker\" ) // Thymeleaf: implementation ( \"org.http4k:http4k-template-thymeleaf\" ) } About \u00b6 The http4k templating API provides a standard mechanism for rendering using common templating libraries. Simply implement the ViewModel interface on a model class and pass it to the renderer to get a string. All of the implementations support view rendering using the following strategies: Cached on the classpath Cached from the filesystem Hot-Reloading from the filesystem The examples below are for Handlebars, but the others have the same APIs: Code \u00b6 package guide.reference.templating import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel import java.io.File data class Person ( val name : String , val age : Int ) : ViewModel fun main () { // first, create a Renderer - this can be a Caching instance or a HotReload for development val renderer = HandlebarsTemplates (). HotReload ( \"src/test/resources\" ) // first example uses a renderer to create a string val app : HttpHandler = { val viewModel = Person ( \"Bob\" , 45 ) val renderedView = renderer ( viewModel ) Response ( OK ). body ( renderedView ) } println ( app ( Request ( GET , \"/someUrl\" ))) // the lens example uses the Body.viewModel to also set the content type, and avoid using Strings val viewLens = Body . viewModel ( renderer , TEXT_HTML ). toLens () val appUsingLens : HttpHandler = { Response ( OK ). with ( viewLens of Person ( \"Bob\" , 45 )) } println ( appUsingLens ( Request ( GET , \"/someUrl\" ))) // overwrite the content - this will prove the hot reload works! File ( \"src/test/resources/guide.reference/templating/Person.hbs\" ). writer () . use { it . write ( \"{{name}} is not {{age}} years old\" ) } println ( appUsingLens ( Request ( GET , \"/someUrl\" ))) } Notes for Rocker \u00b6 Rocker differs slightly from the dynamic templating engines in that it generates Java classes at compile time. In order to fit this into the http4k model, we have created a special superclass RockerViewModel (which combines the Rocker and the http4k ViewModel interfaces into a common supertype). This should be used as the extendsModelClass property in the generation process by configuration. Note that as the generated classes are Java and NOT Kotlin, Java syntax should be used inside the view files (which need to be named Xyz.rocker.html ). Notes for Pebble \u00b6 The way the underlying model is exposed for Pebble templates differs from the rest of the templating engines. The properties of the ViewModel object are bound to the view context under a model key, rather than directly into the template context itself. This means that the model. prefix is required to access the properties of the underlying object in a Pebble template. In other words, rendering a firstName property, for example, is done using: {{ model.firstName }} instead of {{ firstName }}","title":"Templating"},{"location":"guide/reference/templating/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // Dust: implementation ( \"org.http4k:http4k-template-dust\" ) // Freemarker: implementation ( \"org.http4k:http4k-template-freemarker\" ) // Handlebars: implementation ( \"org.http4k:http4k-template-handlebars\" ) // Pug4j: implementation ( \"org.http4k:http4k-template-pug4j\" ) // JTE: implementation ( \"org.http4k:http4k-template-jte\" ) // Pebble: implementation ( \"org.http4k:http4k-template-pebble\" ) // Rocker: implementation ( \"org.http4k:http4k-template-rocker\" ) // Thymeleaf: implementation ( \"org.http4k:http4k-template-thymeleaf\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/templating/#about","text":"The http4k templating API provides a standard mechanism for rendering using common templating libraries. Simply implement the ViewModel interface on a model class and pass it to the renderer to get a string. All of the implementations support view rendering using the following strategies: Cached on the classpath Cached from the filesystem Hot-Reloading from the filesystem The examples below are for Handlebars, but the others have the same APIs:","title":"About"},{"location":"guide/reference/templating/#code","text":"package guide.reference.templating import org.http4k.core.Body import org.http4k.core.ContentType.Companion.TEXT_HTML import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.template.HandlebarsTemplates import org.http4k.template.ViewModel import org.http4k.template.viewModel import java.io.File data class Person ( val name : String , val age : Int ) : ViewModel fun main () { // first, create a Renderer - this can be a Caching instance or a HotReload for development val renderer = HandlebarsTemplates (). HotReload ( \"src/test/resources\" ) // first example uses a renderer to create a string val app : HttpHandler = { val viewModel = Person ( \"Bob\" , 45 ) val renderedView = renderer ( viewModel ) Response ( OK ). body ( renderedView ) } println ( app ( Request ( GET , \"/someUrl\" ))) // the lens example uses the Body.viewModel to also set the content type, and avoid using Strings val viewLens = Body . viewModel ( renderer , TEXT_HTML ). toLens () val appUsingLens : HttpHandler = { Response ( OK ). with ( viewLens of Person ( \"Bob\" , 45 )) } println ( appUsingLens ( Request ( GET , \"/someUrl\" ))) // overwrite the content - this will prove the hot reload works! File ( \"src/test/resources/guide.reference/templating/Person.hbs\" ). writer () . use { it . write ( \"{{name}} is not {{age}} years old\" ) } println ( appUsingLens ( Request ( GET , \"/someUrl\" ))) }","title":"Code"},{"location":"guide/reference/templating/#notes_for_rocker","text":"Rocker differs slightly from the dynamic templating engines in that it generates Java classes at compile time. In order to fit this into the http4k model, we have created a special superclass RockerViewModel (which combines the Rocker and the http4k ViewModel interfaces into a common supertype). This should be used as the extendsModelClass property in the generation process by configuration. Note that as the generated classes are Java and NOT Kotlin, Java syntax should be used inside the view files (which need to be named Xyz.rocker.html ).","title":"Notes for Rocker"},{"location":"guide/reference/templating/#notes_for_pebble","text":"The way the underlying model is exposed for Pebble templates differs from the rest of the templating engines. The properties of the ViewModel object are bound to the view context under a model key, rather than directly into the template context itself. This means that the model. prefix is required to access the properties of the underlying object in a Pebble template. In other words, rendering a firstName property, for example, is done using: {{ model.firstName }} instead of {{ firstName }}","title":"Notes for Pebble"},{"location":"guide/reference/testing/","text":"The creators of http4k takes testing very seriously - so seriously that there really isn't that much to say here! The API has been designed to make it as simple as possible to test both individual endpoints and entire applications in a consistent fashion, which is aided by remembering that: The input and output Request/Response objects are immutable. HttpHandlers are just functions. An entire http4k application is just an HttpHandler . Because of the above, there really isn't much required in the way of \"testing infrastructure\" - no magic containers or test fixtures that you might find in other frameworks. Testing is just matter of calling the correct function! Additionally, because the server and client HttpHandler interfaces are symmetrical - moving between in and out of container contexts (or indeed even to another HTTP framework entirely) is just a matter of switching out the HttpHandler implementation from the constructed app (out of container) to an HTTP client (in-container). That said, possibly the most useful thing is to demonstrate the process that we have developed to test micro-services. A simple example of the development process can be found here . Testing modules \u00b6 We have developed the following modules to help with testing: http4k-testing-approval : JUnit 5 extensions for Approval testing of http4k Request and Response messages. http4k-testing-chaos : API for declaring and injecting failure modes into http4k applications, allowing modelling and hence answering of \"what if\" style questions to help understand how code fares under failure conditions such as latency and dying processes. http4k-testing-hamkrest : a set of composable Hamkrest matchers for matching http4k objects against. http4k-testing-kotest : a set of composable Kotest matchers for matching http4k objects against. http4k-testing-servitium : a fully featured implementation of the Servirtium solution for Service Virtualization testing approach. http4k-testing-strikt : a set of custom Strikt assertions for matching http4k objects against. http4k-testing-webdriver : an ultra-lightweight Selenium WebDriver implementation which can be used to test-drive http4k apps (ie. HttpHandlers).","title":"Overview"},{"location":"guide/reference/testing/#testing_modules","text":"We have developed the following modules to help with testing: http4k-testing-approval : JUnit 5 extensions for Approval testing of http4k Request and Response messages. http4k-testing-chaos : API for declaring and injecting failure modes into http4k applications, allowing modelling and hence answering of \"what if\" style questions to help understand how code fares under failure conditions such as latency and dying processes. http4k-testing-hamkrest : a set of composable Hamkrest matchers for matching http4k objects against. http4k-testing-kotest : a set of composable Kotest matchers for matching http4k objects against. http4k-testing-servitium : a fully featured implementation of the Servirtium solution for Service Virtualization testing approach. http4k-testing-strikt : a set of custom Strikt assertions for matching http4k objects against. http4k-testing-webdriver : an ultra-lightweight Selenium WebDriver implementation which can be used to test-drive http4k apps (ie. HttpHandlers).","title":"Testing modules"},{"location":"guide/reference/tracerbullet/","text":"Installation (Gradle) \u00b6 Gradle setup \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:4.41.4.0\" )) implementation ( \"org.http4k:http4k-testing-tracerbullet\" ) } The http4k TracerBullet module brings together a lot of http4k innovations to help you to self-document your system and let's you answer questions about your system with your tests apart from the obvious one - \"Does it work?\". The essential idea is that the application uses the Events interface to send interesting information to a collector during the running of tests through a JUnit extension. At the end of the test run, the TracerBullet collates these Events using a distributed tracing mechanism (X-B3 headers) and a set of Tracers to create a tree of what exactly happened during the test - these events can be anything, but good examples are: HTTP traffic in and out of the application Database calls Business events Once we have the \"Trace tree\", we can use it to extract information about our applications and then use Renderers to use it to document HOW our system behaves as well as if it works. Examples of these renders are: PUML/Mermaid Sequence or Interaction diagrams Analysis of the number of outbound calls that a particular application is making. Eliminating duplicate database calls or inefficient logic. When testing multiple apps together using the Server-as-a-Function/Hexagonal techniques, we can also see the entire timeline of every call, so we can also determine maximum hop distance. And here is an example of a multi-service test and the type of visual documentation that is created: Additionally, in the case of the test failing, the extension still auto-generates the trace diagram for the traffic which did occur - this provides excellent feedback as to where the system went wrong: One of the best things about the TracerBullet plugin is that it fits in seamlessly with the rest of the http4k stack. Once your applications have been designed to send data to the Events stream, just install the plugin and the diagramming comes for free! Code \u00b6 Here's an example of making this work - note the use of the filters to use RequestTracing on the Events and the servers and clients. This is required to make the TracerBullet work. package guide.howto.self_document_systems_with_tests import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.events.EventFilters.AddServiceName import org.http4k.events.EventFilters.AddZipkinTraces import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.events.and import org.http4k.events.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.ResetRequestTracing import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters.RequestTracing import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.reverseProxy import org.http4k.routing.routes import org.http4k.tracing.Actor import org.http4k.tracing.ActorResolver import org.http4k.tracing.ActorType import org.http4k.tracing.TraceRenderPersistence import org.http4k.tracing.junit.TracerBulletEvents import org.http4k.tracing.persistence.FileSystem import org.http4k.tracing.renderer.PumlSequenceDiagram import org.http4k.tracing.tracer.HttpTracer import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import java.io.File // standardised events stack which records the service name and adds tracing fun TraceEvents ( actorName : String ) = AddZipkinTraces (). then ( AddServiceName ( actorName )) // standardised client filter stack which adds tracing and records traffic events fun ClientStack ( events : Events ) = ClientFilters . RequestTracing () . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) // standardised server filter stack which adds tracing and records traffic events fun ServerStack ( events : Events ) = RequestTracing (). then ( ReportHttpTransaction { events ( Incoming ( it )) }) // Our \"User\" object who will send a request to our system class User ( rawEvents : Events , rawHttp : HttpHandler ) { private val events = TraceEvents ( \"user\" ). then ( rawEvents ) // as the user is the initiator of requests, we need to reset the tracing for each call. private val http = ResetRequestTracing (). then ( ClientStack ( events )). then ( rawHttp ) fun initiateCall () = http ( Request ( GET , \"http://internal1/int1\" )) } // the first internal app fun Internal1 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal1\" ). then ( rawEvents ). and ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int1\" bind { _ : Request -> val first = http ( Request ( GET , \"http://external1/ext1\" )) when { first . status . successful -> http ( Request ( GET , \"http://internal2/int2\" )) else -> first } }) ) } // the second internal app fun Internal2 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal2\" ). then ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int2\" bind { _ : Request -> http ( Request ( GET , \"http://external2/ext2\" )) }) ) } // an external fake system fun FakeExternal1 (): HttpHandler = { Response ( OK ) } // another external fake system fun FakeExternal2 (): HttpHandler = { Response ( OK ) } private val actor = ActorResolver { Actor ( it . metadata [ \"service\" ] . toString (), ActorType . System ) } /** * Our test will capture the traffic and render it to the console */ class RenderingTest { @RegisterExtension // this events implementation will automatically capture the HTTP traffic val events = TracerBulletEvents ( listOf ( HttpTracer ( actor )), // A tracer to capture HTTP calls listOf ( PumlSequenceDiagram ), // Render the HTTP traffic as a PUML diagram TraceRenderPersistence . FileSystem ( File ( \".\" )) // Store the result ) @Test fun `render successful trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to FakeExternal1 (), \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } @Test @Disabled ( \"Remove this to run the failing test!\" ) fun `render failure trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to { _ : Request -> Response ( INTERNAL_SERVER_ERROR ) }, \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } } An extended example of this technique can be found at the this repository .","title":"Tracer Bullet"},{"location":"guide/reference/tracerbullet/#installation_gradle","text":"","title":"Installation (Gradle)"},{"location":"guide/reference/tracerbullet/#gradle_setup","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:4.41.4.0\" )) implementation ( \"org.http4k:http4k-testing-tracerbullet\" ) } The http4k TracerBullet module brings together a lot of http4k innovations to help you to self-document your system and let's you answer questions about your system with your tests apart from the obvious one - \"Does it work?\". The essential idea is that the application uses the Events interface to send interesting information to a collector during the running of tests through a JUnit extension. At the end of the test run, the TracerBullet collates these Events using a distributed tracing mechanism (X-B3 headers) and a set of Tracers to create a tree of what exactly happened during the test - these events can be anything, but good examples are: HTTP traffic in and out of the application Database calls Business events Once we have the \"Trace tree\", we can use it to extract information about our applications and then use Renderers to use it to document HOW our system behaves as well as if it works. Examples of these renders are: PUML/Mermaid Sequence or Interaction diagrams Analysis of the number of outbound calls that a particular application is making. Eliminating duplicate database calls or inefficient logic. When testing multiple apps together using the Server-as-a-Function/Hexagonal techniques, we can also see the entire timeline of every call, so we can also determine maximum hop distance. And here is an example of a multi-service test and the type of visual documentation that is created: Additionally, in the case of the test failing, the extension still auto-generates the trace diagram for the traffic which did occur - this provides excellent feedback as to where the system went wrong: One of the best things about the TracerBullet plugin is that it fits in seamlessly with the rest of the http4k stack. Once your applications have been designed to send data to the Events stream, just install the plugin and the diagramming comes for free!","title":"Gradle setup"},{"location":"guide/reference/tracerbullet/#code","text":"Here's an example of making this work - note the use of the filters to use RequestTracing on the Events and the servers and clients. This is required to make the TracerBullet work. package guide.howto.self_document_systems_with_tests import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.events.EventFilters.AddServiceName import org.http4k.events.EventFilters.AddZipkinTraces import org.http4k.events.Events import org.http4k.events.HttpEvent.Incoming import org.http4k.events.HttpEvent.Outgoing import org.http4k.events.and import org.http4k.events.then import org.http4k.filter.ClientFilters import org.http4k.filter.ClientFilters.ResetRequestTracing import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.ServerFilters.RequestTracing import org.http4k.hamkrest.hasStatus import org.http4k.routing.bind import org.http4k.routing.reverseProxy import org.http4k.routing.routes import org.http4k.tracing.Actor import org.http4k.tracing.ActorResolver import org.http4k.tracing.ActorType import org.http4k.tracing.TraceRenderPersistence import org.http4k.tracing.junit.TracerBulletEvents import org.http4k.tracing.persistence.FileSystem import org.http4k.tracing.renderer.PumlSequenceDiagram import org.http4k.tracing.tracer.HttpTracer import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.RegisterExtension import java.io.File // standardised events stack which records the service name and adds tracing fun TraceEvents ( actorName : String ) = AddZipkinTraces (). then ( AddServiceName ( actorName )) // standardised client filter stack which adds tracing and records traffic events fun ClientStack ( events : Events ) = ClientFilters . RequestTracing () . then ( ReportHttpTransaction { events ( Outgoing ( it )) }) // standardised server filter stack which adds tracing and records traffic events fun ServerStack ( events : Events ) = RequestTracing (). then ( ReportHttpTransaction { events ( Incoming ( it )) }) // Our \"User\" object who will send a request to our system class User ( rawEvents : Events , rawHttp : HttpHandler ) { private val events = TraceEvents ( \"user\" ). then ( rawEvents ) // as the user is the initiator of requests, we need to reset the tracing for each call. private val http = ResetRequestTracing (). then ( ClientStack ( events )). then ( rawHttp ) fun initiateCall () = http ( Request ( GET , \"http://internal1/int1\" )) } // the first internal app fun Internal1 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal1\" ). then ( rawEvents ). and ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int1\" bind { _ : Request -> val first = http ( Request ( GET , \"http://external1/ext1\" )) when { first . status . successful -> http ( Request ( GET , \"http://internal2/int2\" )) else -> first } }) ) } // the second internal app fun Internal2 ( rawEvents : Events , rawHttp : HttpHandler ): HttpHandler { val events = TraceEvents ( \"internal2\" ). then ( rawEvents ) val http = ClientStack ( events ). then ( rawHttp ) return ServerStack ( events ) . then ( routes ( \"/int2\" bind { _ : Request -> http ( Request ( GET , \"http://external2/ext2\" )) }) ) } // an external fake system fun FakeExternal1 (): HttpHandler = { Response ( OK ) } // another external fake system fun FakeExternal2 (): HttpHandler = { Response ( OK ) } private val actor = ActorResolver { Actor ( it . metadata [ \"service\" ] . toString (), ActorType . System ) } /** * Our test will capture the traffic and render it to the console */ class RenderingTest { @RegisterExtension // this events implementation will automatically capture the HTTP traffic val events = TracerBulletEvents ( listOf ( HttpTracer ( actor )), // A tracer to capture HTTP calls listOf ( PumlSequenceDiagram ), // Render the HTTP traffic as a PUML diagram TraceRenderPersistence . FileSystem ( File ( \".\" )) // Store the result ) @Test fun `render successful trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to FakeExternal1 (), \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } @Test @Disabled ( \"Remove this to run the failing test!\" ) fun `render failure trace` () { // compose our application(s) together val internalApp = Internal1 ( events , reverseProxy ( \"external1\" to { _ : Request -> Response ( INTERNAL_SERVER_ERROR ) }, \"internal2\" to Internal2 ( events , FakeExternal2 ()) ) ) // make a request to the composed stack assertThat ( User ( events , internalApp ). initiateCall (), hasStatus ( OK )) } } An extended example of this technique can be found at the this repository .","title":"Code"},{"location":"guide/reference/webdriver/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-webdriver\" ) } About \u00b6 A basic Selenium WebDriver API implementation for http4k HttpHandlers, which runs completely out of container (no network) for ultra fast tests, backed by JSoup. Feature Supported Notes Navigation yes simple back/forward/refresh history CSS selectors yes Link navigation yes Form field entry and submission yes Cookie storage yes manual expiry management JavaScript no Alerts no Screenshots no Frames no Multiple windows no Use the API like any other WebDriver implementation, by simply passing your app HttpHandler to construct it. Code \u00b6 package guide.reference.webdriver import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.webdriver.Http4kWebDriver import org.openqa.selenium.By fun main () { val app = routes ( \"/hello\" bind GET to { Response ( OK ). body ( \"hello \" ) } ) val driver = Http4kWebDriver ( app ) driver . navigate (). to ( \"http://localhost:10000/hello\" ) println ( driver . title ) println ( driver . findElement ( By . tagName ( \"title\" ))) // prints: // // hello // hello }","title":"WebDriver"},{"location":"guide/reference/webdriver/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-testing-webdriver\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/webdriver/#about","text":"A basic Selenium WebDriver API implementation for http4k HttpHandlers, which runs completely out of container (no network) for ultra fast tests, backed by JSoup. Feature Supported Notes Navigation yes simple back/forward/refresh history CSS selectors yes Link navigation yes Form field entry and submission yes Cookie storage yes manual expiry management JavaScript no Alerts no Screenshots no Frames no Multiple windows no Use the API like any other WebDriver implementation, by simply passing your app HttpHandler to construct it.","title":"About"},{"location":"guide/reference/webdriver/#code","text":"package guide.reference.webdriver import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.webdriver.Http4kWebDriver import org.openqa.selenium.By fun main () { val app = routes ( \"/hello\" bind GET to { Response ( OK ). body ( \"hello \" ) } ) val driver = Http4kWebDriver ( app ) driver . navigate (). to ( \"http://localhost:10000/hello\" ) println ( driver . title ) println ( driver . findElement ( By . tagName ( \"title\" ))) // prints: // // hello // hello }","title":"Code"},{"location":"guide/reference/webhooks/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-webhooks\" ) } About \u00b6 This module provides infrastructure for the Webhook standard , providing infrastructure for signing and verifying of Webhook requests (HMAC256 only currently) as per the standard, and support for the defined Webhook event wrapper format. The example below shows how to use sign and verify filters to automatically provide security and marshalling for the Standard Webhook format. Code \u00b6 package guide.reference.webhooks import org.http4k.client.JavaHttpClient import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ClientFilters import org.http4k.filter.ServerFilters import org.http4k.filter.SignWebhookPayload import org.http4k.filter.VerifyWebhookSignature import org.http4k.format.Jackson import org.http4k.format.Jackson.auto import org.http4k.lens.Header import org.http4k.lens.WEBHOOK_ID import org.http4k.lens.WEBHOOK_SIGNATURE import org.http4k.lens.WEBHOOK_TIMESTAMP import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.webhook.EventType import org.http4k.webhook.WebhookPayload import org.http4k.webhook.WebhookTimestamp import org.http4k.webhook.signing.HmacSha256 import org.http4k.webhook.signing.HmacSha256SigningSecret import java.time.Instant data class MyEventType ( val index : Int ) fun main () { val lens = Body . auto < WebhookPayload < MyEventType >> (). toLens () val signingSecret = HmacSha256SigningSecret . encode ( \"this_is_my_super_secret_secret\" ) val secureWebhookSendingClient = ClientFilters . SignWebhookPayload ( HmacSha256 . Signer ( signingSecret ), Jackson ) . then ( JavaHttpClient ()) val secureWebhookReceiver = ServerFilters . VerifyWebhookSignature ( HmacSha256 . Verifier ( signingSecret )) . then { req : Request -> listOf ( Header . WEBHOOK_ID , Header . WEBHOOK_SIGNATURE , Header . WEBHOOK_TIMESTAMP ) . forEach { println ( it . meta . name + \": \" + it ( req )) } println ( \"event received was: \" + lens ( req )) Response ( OK ) } val server = secureWebhookReceiver . asServer ( SunHttp ( 0 )). start () // create the webhook event wrapper with event type, timestamp val webhookPayload = WebhookPayload ( EventType . of ( \"foo.bar\" ), WebhookTimestamp . of ( Instant . now ()), MyEventType ( 123 ) ) val eventRequest = Request ( POST , Uri . of ( \"http://localhost: ${ server . port () } \" )). with ( lens of webhookPayload ) // send the webhook - the infra signs the request secureWebhookSendingClient ( eventRequest ) server . stop () }","title":"Webhooks"},{"location":"guide/reference/webhooks/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-webhooks\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/webhooks/#about","text":"This module provides infrastructure for the Webhook standard , providing infrastructure for signing and verifying of Webhook requests (HMAC256 only currently) as per the standard, and support for the defined Webhook event wrapper format. The example below shows how to use sign and verify filters to automatically provide security and marshalling for the Standard Webhook format.","title":"About"},{"location":"guide/reference/webhooks/#code","text":"package guide.reference.webhooks import org.http4k.client.JavaHttpClient import org.http4k.core.Body import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.core.with import org.http4k.filter.ClientFilters import org.http4k.filter.ServerFilters import org.http4k.filter.SignWebhookPayload import org.http4k.filter.VerifyWebhookSignature import org.http4k.format.Jackson import org.http4k.format.Jackson.auto import org.http4k.lens.Header import org.http4k.lens.WEBHOOK_ID import org.http4k.lens.WEBHOOK_SIGNATURE import org.http4k.lens.WEBHOOK_TIMESTAMP import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.webhook.EventType import org.http4k.webhook.WebhookPayload import org.http4k.webhook.WebhookTimestamp import org.http4k.webhook.signing.HmacSha256 import org.http4k.webhook.signing.HmacSha256SigningSecret import java.time.Instant data class MyEventType ( val index : Int ) fun main () { val lens = Body . auto < WebhookPayload < MyEventType >> (). toLens () val signingSecret = HmacSha256SigningSecret . encode ( \"this_is_my_super_secret_secret\" ) val secureWebhookSendingClient = ClientFilters . SignWebhookPayload ( HmacSha256 . Signer ( signingSecret ), Jackson ) . then ( JavaHttpClient ()) val secureWebhookReceiver = ServerFilters . VerifyWebhookSignature ( HmacSha256 . Verifier ( signingSecret )) . then { req : Request -> listOf ( Header . WEBHOOK_ID , Header . WEBHOOK_SIGNATURE , Header . WEBHOOK_TIMESTAMP ) . forEach { println ( it . meta . name + \": \" + it ( req )) } println ( \"event received was: \" + lens ( req )) Response ( OK ) } val server = secureWebhookReceiver . asServer ( SunHttp ( 0 )). start () // create the webhook event wrapper with event type, timestamp val webhookPayload = WebhookPayload ( EventType . of ( \"foo.bar\" ), WebhookTimestamp . of ( Instant . now ()), MyEventType ( 123 ) ) val eventRequest = Request ( POST , Uri . of ( \"http://localhost: ${ server . port () } \" )). with ( lens of webhookPayload ) // send the webhook - the infra signs the request secureWebhookSendingClient ( eventRequest ) server . stop () }","title":"Code"},{"location":"guide/reference/xml/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // json.org XML: implementation ( \"org.http4k:http4k-format-xml\" ) // Jackson XML: implementation ( \"org.http4k:http4k-format-jackson-xml\" ) } About \u00b6 These modules provide auto-marshalling functionality to convert XML into arbitrary data classes. The 2 differ slightly in their behaviour, due to the underlying libraries used for implementation. We recommend using http4k-format-jackson-xml as it has more predictable and consistent behaviour. JacksonXml \u00b6 Provides bi-directional conversion Does NOT expose an XML DOM node model Generally requires Jackson field annotations to manipulate output format Provides extension point to map custom types using BiDiMapping registration (so supports all Java and Http4k primitives such as Uri ) JacksonXML Code \u00b6 Provides extraction conversion only Exposes an XML DOM node model as a first-class citizen - so can read directly from a string into a DOM model Does not generate a wrapper element to represent the parent node Has trouble with repeating child-elements, depending on zero, one or many elements in the XML. This is due to the underlying library implementation Only handles objects with primitive JDK types package guide.reference.xml import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.JacksonXml.auto data class JacksonWrapper ( val message : JacksonMsg?) data class JacksonMsg ( val subject : String? , val from : String? , val to : String? , val content : String? ) fun main () { // We can use the auto method here from the JacksonXML message format object. Note that the // auto() method is an extension function which needs to be manually imported (IntelliJ won't pick it up automatically). val messageLens = Body . auto < JacksonWrapper > (). toLens () // extract the body from the message - this also works with Response val wrapper = JacksonWrapper ( JacksonMsg ( \"subject\" , \"from\" , \"to\" , \"content\" )) val message = \"\"\"david@http4k.org ivan@http4k.org hello world \"\"\" println ( messageLens ( Request ( GET , \"/\" ). body ( message ))) // inject a converted object-as-XML-string into a request println ( Request ( GET , \"\" ). with ( messageLens . of ( wrapper )). bodyString ()) } Xml \u00b6 As above, we recommend using http4k-format-jackson-xml as it has more predictable and consistent behaviour. XML Code \u00b6 package guide.reference.xml import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Xml.auto data class XmlWrapper ( val mesApacheClientStreamingContractessage : MessageXml?) data class MessageXml ( val subject : String? , val from : String? , val to : String? , val content : String? ) fun main () { // We can use the auto method here from the Xml message format object. Note that the // auto() method is an extension function which needs to be manually imported (IntelliJ won't pick it up automatically). // Also, this lense is ONLY one way - to extract values from a message val messageLens = Body . auto < XmlWrapper > (). toLens () // extract the body from the message - this also works with Response val message = \"\"\"david@http4k.org ivan@http4k.org hello world \"\"\" val requestWithEmail = Request ( GET , \"/\" ). body ( message ) println ( messageLens ( requestWithEmail )) }","title":"XML handling"},{"location":"guide/reference/xml/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) // json.org XML: implementation ( \"org.http4k:http4k-format-xml\" ) // Jackson XML: implementation ( \"org.http4k:http4k-format-jackson-xml\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/xml/#about","text":"These modules provide auto-marshalling functionality to convert XML into arbitrary data classes. The 2 differ slightly in their behaviour, due to the underlying libraries used for implementation. We recommend using http4k-format-jackson-xml as it has more predictable and consistent behaviour.","title":"About"},{"location":"guide/reference/xml/#jacksonxml","text":"Provides bi-directional conversion Does NOT expose an XML DOM node model Generally requires Jackson field annotations to manipulate output format Provides extension point to map custom types using BiDiMapping registration (so supports all Java and Http4k primitives such as Uri )","title":"JacksonXml"},{"location":"guide/reference/xml/#jacksonxml_code","text":"Provides extraction conversion only Exposes an XML DOM node model as a first-class citizen - so can read directly from a string into a DOM model Does not generate a wrapper element to represent the parent node Has trouble with repeating child-elements, depending on zero, one or many elements in the XML. This is due to the underlying library implementation Only handles objects with primitive JDK types package guide.reference.xml import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.with import org.http4k.format.JacksonXml.auto data class JacksonWrapper ( val message : JacksonMsg?) data class JacksonMsg ( val subject : String? , val from : String? , val to : String? , val content : String? ) fun main () { // We can use the auto method here from the JacksonXML message format object. Note that the // auto() method is an extension function which needs to be manually imported (IntelliJ won't pick it up automatically). val messageLens = Body . auto < JacksonWrapper > (). toLens () // extract the body from the message - this also works with Response val wrapper = JacksonWrapper ( JacksonMsg ( \"subject\" , \"from\" , \"to\" , \"content\" )) val message = \"\"\"david@http4k.org ivan@http4k.org hello world \"\"\" println ( messageLens ( Request ( GET , \"/\" ). body ( message ))) // inject a converted object-as-XML-string into a request println ( Request ( GET , \"\" ). with ( messageLens . of ( wrapper )). bodyString ()) }","title":"JacksonXML Code"},{"location":"guide/reference/xml/#xml","text":"As above, we recommend using http4k-format-jackson-xml as it has more predictable and consistent behaviour.","title":"Xml"},{"location":"guide/reference/xml/#xml_code","text":"package guide.reference.xml import org.http4k.core.Body import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.format.Xml.auto data class XmlWrapper ( val mesApacheClientStreamingContractessage : MessageXml?) data class MessageXml ( val subject : String? , val from : String? , val to : String? , val content : String? ) fun main () { // We can use the auto method here from the Xml message format object. Note that the // auto() method is an extension function which needs to be manually imported (IntelliJ won't pick it up automatically). // Also, this lense is ONLY one way - to extract values from a message val messageLens = Body . auto < XmlWrapper > (). toLens () // extract the body from the message - this also works with Response val message = \"\"\"david@http4k.org ivan@http4k.org hello world \"\"\" val requestWithEmail = Request ( GET , \"/\" ). body ( message ) println ( messageLens ( requestWithEmail )) }","title":"XML Code"},{"location":"guide/reference/yaml/","text":"Installation (Gradle) \u00b6 dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-jackson-yaml\" ) implementation ( \"org.http4k:http4k-format-moshi-yaml\" ) } About \u00b6 These modules add the ability to use YAML as a first-class citizen when reading from and to HTTP messages.","title":"YAML handling"},{"location":"guide/reference/yaml/#installation_gradle","text":"dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-format-jackson-yaml\" ) implementation ( \"org.http4k:http4k-format-moshi-yaml\" ) }","title":"Installation (Gradle)"},{"location":"guide/reference/yaml/#about","text":"These modules add the ability to use YAML as a first-class citizen when reading from and to HTTP messages.","title":"About"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/","text":"In this guide, we'll run you through the steps required to get an http4k application deployed and running on AWS Lambda with GraalVM and available to call over the internet using AWS ApiGateway. If you're not familiar with the http4k concepts for HTTP and Serverless apps, then we advise you read them here and here . To make an app you can follow the Your first http4k app tutorial. Then follow the steps in the Serverless http4k with AWS Lambda tutorial before tackling this guide. We'll take an existing http4k application built with Gradle and deployed with Pulumi, add the bits that are important to GraalVM Serverless HTTP apps, then compile it natively and deploy it to AWS Lambda and API Gateway using Pulumi. The resulting Lambda has super-quick startup time and low memory footprint. Pre-requisites: \u00b6 All the pre-requisites from the Your first http4k app and Serverless http4k with AWS Lambda tutorials. This will give you a working http4k application deployed to AWS Lambda. Docker installed and running on your system. See here for details. Step 1 \u00b6 We need to add the http4k AWS Lambda Serverless Runtime module to our project. Install it into your build.gradle file with: implementation ( \"org.http4k:http4k-serverless-lambda-runtime: ${ http4kVersion } \" ) This custom runtime is a lightweight, zero-reflection module which allows you to deploy both Java and GraalVM based binaries to AWS. Step 2 \u00b6 Lambdas working from a native binary have to supply their own main function to launch the runtime, instead of implementing the standard Request/StreamHandler interfaces. To use it on our app, we simply create a launcher and wrap our http4k HttpHandler with the appropriate FnHandler class before starting the Runtime. Put this into a new HelloServerlessHttp4k.kt (different package to before: package guide.tutorials.going_native_with_graal_on_aws_lambda import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.serverless.ApiGatewayV2FnLoader import org.http4k.serverless.AwsLambdaRuntime import org.http4k.serverless.asServer val http4kApp = routes ( \"/echo/{message:.*}\" bind GET to { Response ( OK ). body ( it . path ( \"message\" ) ?: \"(nothing to echo, use /echo/)\" ) }, \"/\" bind GET to { Response ( OK ). body ( \"ok\" ) } ) fun main () { ApiGatewayV2FnLoader ( http4kApp ). asServer ( AwsLambdaRuntime ()). start () } Update the Pulumi config to point to the new file: Step 3 \u00b6 Compile the Lambda code into a GraalVM file is a 2 stage process. First, install and configure the ShadowJar plugin into build.gradle to merge the entire application into a single JAR file with a known main class. Update/add the following sections: buildscript { repositories { mavenCentral () gradlePluginPortal () } dependencies { classpath ( \"com.github.johnrengelman:shadow:8.1.1\" ) classpath ( \"org.jetbrains.kotlin:kotlin-gradle-plugin: ${ kotlinVersion } \" ) } } apply ( plugin = \"java\" ) apply ( plugin = \"com.github.johnrengelman.shadow\" ) mainClassName = \"guide.tutorials.going_native_with_graal_on_aws_lambda.HelloServerlessHttp4kKt\" shadowJar { manifest . attributes [ \"Main-Class\" ] = mainClassName archiveBaseName . set ( project . name ) archiveClassifier . set ( null ) archiveVersion . set ( null ) mergeServiceFiles () } Run the new task with: ./gradlew shadowJar ... and then take a note of the JAR file that appears in build/libs . Step 4 \u00b6 Now that we have our JAR file, we need to create a GraalVM image and package it into a ZIP file which can be uploaded to AWS. http4k supplies a convenience Docker image that uses the native-image program to create the binary and then packages the ZIP file: docker run -v $( pwd ) :/source --platform = linux/amd64 \\ http4k/amazonlinux-java-graal-community-lambda-runtime \\ build/libs/HelloWorld.jar \\ HelloHttp4k.zip GraalVM will churn away for a few minutes and all being well, the HelloHttp4k.zip file will be generated in the main directory. Step 5 \u00b6 We need to update our Pulumi configuration to upload the new binary. This is pretty simple and just involves changing the runtime, ZIP target and handler in our index.ts . We can also remove the timeout as the native binary will startup in milliseconds: const lambdaFunction = new aws . lambda . Function ( \"hello-http4k\" , { code : new pulumi . asset . FileArchive ( \"HelloHttp4k.zip\" ), handler : \"unused\" , role : defaultRole.arn , runtime : \"provided.al2\" }); Step 6 \u00b6 Deploy your ZIP file to AWS with: pulumi up --stack dev --yes Pulumi will churn for a bit and all being well will display the URL at the end of the process. Step 7 \u00b6 You can now call your deployed lambda by visiting: https://{publishedUrl}/echo/helloHttp4k . You should see helloHttp4k in the response body. Notice that the response time is super-super quick, especially after the lambda is warm. If we invoke it from the console, you should see something similar: Step 8 \u00b6 To avoid any unwanted AWS charges, don't forget to delete all of the resources in your stack when you've finished by running: pulumi destroy --stack dev --yes Congratulations! \u00b6 You have successfully compiled an http4k application with GraalVM, then deployed and invoked it as a Lambda in AWS!","title":"Going native with Graal on AWS Lambda"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#pre-requisites","text":"All the pre-requisites from the Your first http4k app and Serverless http4k with AWS Lambda tutorials. This will give you a working http4k application deployed to AWS Lambda. Docker installed and running on your system. See here for details.","title":"Pre-requisites:"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_1","text":"We need to add the http4k AWS Lambda Serverless Runtime module to our project. Install it into your build.gradle file with: implementation ( \"org.http4k:http4k-serverless-lambda-runtime: ${ http4kVersion } \" ) This custom runtime is a lightweight, zero-reflection module which allows you to deploy both Java and GraalVM based binaries to AWS.","title":"Step 1"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_2","text":"Lambdas working from a native binary have to supply their own main function to launch the runtime, instead of implementing the standard Request/StreamHandler interfaces. To use it on our app, we simply create a launcher and wrap our http4k HttpHandler with the appropriate FnHandler class before starting the Runtime. Put this into a new HelloServerlessHttp4k.kt (different package to before: package guide.tutorials.going_native_with_graal_on_aws_lambda import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.serverless.ApiGatewayV2FnLoader import org.http4k.serverless.AwsLambdaRuntime import org.http4k.serverless.asServer val http4kApp = routes ( \"/echo/{message:.*}\" bind GET to { Response ( OK ). body ( it . path ( \"message\" ) ?: \"(nothing to echo, use /echo/)\" ) }, \"/\" bind GET to { Response ( OK ). body ( \"ok\" ) } ) fun main () { ApiGatewayV2FnLoader ( http4kApp ). asServer ( AwsLambdaRuntime ()). start () } Update the Pulumi config to point to the new file:","title":"Step 2"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_3","text":"Compile the Lambda code into a GraalVM file is a 2 stage process. First, install and configure the ShadowJar plugin into build.gradle to merge the entire application into a single JAR file with a known main class. Update/add the following sections: buildscript { repositories { mavenCentral () gradlePluginPortal () } dependencies { classpath ( \"com.github.johnrengelman:shadow:8.1.1\" ) classpath ( \"org.jetbrains.kotlin:kotlin-gradle-plugin: ${ kotlinVersion } \" ) } } apply ( plugin = \"java\" ) apply ( plugin = \"com.github.johnrengelman.shadow\" ) mainClassName = \"guide.tutorials.going_native_with_graal_on_aws_lambda.HelloServerlessHttp4kKt\" shadowJar { manifest . attributes [ \"Main-Class\" ] = mainClassName archiveBaseName . set ( project . name ) archiveClassifier . set ( null ) archiveVersion . set ( null ) mergeServiceFiles () } Run the new task with: ./gradlew shadowJar ... and then take a note of the JAR file that appears in build/libs .","title":"Step 3"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_4","text":"Now that we have our JAR file, we need to create a GraalVM image and package it into a ZIP file which can be uploaded to AWS. http4k supplies a convenience Docker image that uses the native-image program to create the binary and then packages the ZIP file: docker run -v $( pwd ) :/source --platform = linux/amd64 \\ http4k/amazonlinux-java-graal-community-lambda-runtime \\ build/libs/HelloWorld.jar \\ HelloHttp4k.zip GraalVM will churn away for a few minutes and all being well, the HelloHttp4k.zip file will be generated in the main directory.","title":"Step 4"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_5","text":"We need to update our Pulumi configuration to upload the new binary. This is pretty simple and just involves changing the runtime, ZIP target and handler in our index.ts . We can also remove the timeout as the native binary will startup in milliseconds: const lambdaFunction = new aws . lambda . Function ( \"hello-http4k\" , { code : new pulumi . asset . FileArchive ( \"HelloHttp4k.zip\" ), handler : \"unused\" , role : defaultRole.arn , runtime : \"provided.al2\" });","title":"Step 5"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_6","text":"Deploy your ZIP file to AWS with: pulumi up --stack dev --yes Pulumi will churn for a bit and all being well will display the URL at the end of the process.","title":"Step 6"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_7","text":"You can now call your deployed lambda by visiting: https://{publishedUrl}/echo/helloHttp4k . You should see helloHttp4k in the response body. Notice that the response time is super-super quick, especially after the lambda is warm. If we invoke it from the console, you should see something similar:","title":"Step 7"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#step_8","text":"To avoid any unwanted AWS charges, don't forget to delete all of the resources in your stack when you've finished by running: pulumi destroy --stack dev --yes","title":"Step 8"},{"location":"guide/tutorials/going_native_with_graal_on_aws_lambda/#congratulations","text":"You have successfully compiled an http4k application with GraalVM, then deployed and invoked it as a Lambda in AWS!","title":"Congratulations!"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/","text":"In this guide, we'll run you through the steps required to get an http4k application deployed and running on AWS Lambda and available to call over the internet using AWS ApiGateway. If you're not familiar with the http4k concepts for HTTP and Serverless apps, then we advise you read them here and here . To make an app you can follow the Your first http4k app tutorial before tackling this guide. We'll take an existing http4k application built with Gradle, add the bits that are important to Serverless HTTP apps then deploy it to AWS Lambda and API Gateway using Pulumi. Pre-requisites: \u00b6 All the pre-requisites from the Your first http4k app tutorial. The AWS CLI installed and an AWS profile set up to use. See here . Pulumi CLI installed and configured for your system. See here . A working http4k application, built with Gradle. You can generate one from the http4k Toolbox if required. For this example, we're going to assume a simple \"echo\" HttpHandler which responds to GET /echo/{message:.*}\" . Step 1 \u00b6 We need to add the AWS Lambda Serverless module to our project. Install it into your build.gradle file with: implementation ( \"org.http4k:http4k-serverless-lambda: ${ http4kVersion } \" ) Step 2 \u00b6 The AWS Lambda runtime works by implementing an AWS supplied interface Request/StreamHandler and configuring that class to be loaded on an invocation on the lambda. The invocation requests themselves are transmitted as JSON which normally is unmarshalled by Jackson in the AWS runtime into the relevant AWS Event class. http4k supplies pre-built StreamHandler adapters (they are faster) using the lightweight Moshi library to convert the invocations to standard http4k Request/Responses. We need to decide which version of the ApiGateway binding to use and then use the correct http4k class. For this example we're going to use ApiGateway HTTP Version 2, so we simply create a class HelloServerlessHttp4k extending the relevant http4k class and pass our app HttpHandler to it's constructor: package guide.tutorials.serverless_http4k_with_aws_lambda import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.serverless.ApiGatewayV2LambdaFunction val http4kApp = routes ( \"/echo/{message:.*}\" bind GET to { Response ( OK ). body ( it . path ( \"message\" ) ?: \"(nothing to echo, use /echo/)\" ) }, \"/\" bind GET to { Response ( OK ). body ( \"ok\" ) } ) @Suppress ( \"unused\" ) class HelloServerlessHttp4k : ApiGatewayV2LambdaFunction ( http4kApp ) Step 3 \u00b6 To build the Lambda code into a ZIP file, we need to add a task to our build.gradle : tasks . register ( \"buildLambdaZip\" , Zip ) { from compileKotlin from processResources into ( \"lib\" ) { from configurations . compileClasspath } } Run the new task with: ./gradlew buildLambdaZip ... and then take a note of the ZIP file that appears in build/distributions . Step 4 \u00b6 The next step is to configure the AWS resources to send requests to our Lambda function. This is quite involved as far as setup is concerned, but for this we're using Pulumi as it provides a simple way to get started. The concept here is that you configure a \"stack\" in your chosen language (we're choosing TypeScript). On the command line, generate a new Pulumi configuration by running: pulumi new --name hello-http4k --force ... followed by selecting aws-typescript and then all the default options until Pulumi has completed. Step 5 \u00b6 Pulumi creates a few files in the directory, but the most interesting one is index.ts , which is where we will configure our AWS resources for exposing the Lambda. Overwrite the content of index.ts with: import * as pulumi from \"@pulumi/pulumi\" ; import * as aws from \"@pulumi/aws\" ; import {RolePolicyAttachment} from \"@pulumi/aws/iam\" ; const defaultRole = new aws . iam . Role ( \"hello-http4k-default-role\" , { assumeRolePolicy : ` { \"Version\" : \"2012-10-17\" , \"Statement\" : [ { \"Action\" : \"sts:AssumeRole\" , \"Principal\" : { \"Service\" : \"lambda.amazonaws.com\" }, \"Effect\" : \"Allow\" , \"Sid\" : \"\" } ] } ` }); new RolePolicyAttachment ( \"hello-http4k-default-role-policy\" , { role : defaultRole , policyArn : aws . iam . ManagedPolicies . AWSLambdaBasicExecutionRole }); const lambdaFunction = new aws . lambda . Function ( \"hello-http4k\" , { code : new pulumi . asset . FileArchive ( \"build/distributions/HelloWorld.zip\" ), handler : \"guide.tutorials.serverless_http4k_with_aws_lambda.HelloServerlessHttp4k\" , role : defaultRole . arn , runtime : \"java11\" , timeout : 15 }); const logGroupApi = new aws . cloudwatch . LogGroup ( \"hello-http4k-api-route\" , { name : \"hello-http4k\" , }); const apiGatewayPermission = new aws . lambda . Permission ( \"hello-http4k-gateway-permission\" , { action : \"lambda:InvokeFunction\" , \"function\" : lambdaFunction . name , principal : \"apigateway.amazonaws.com\" }); const api = new aws . apigatewayv2 . Api ( \"hello-http4k-api\" , { protocolType : \"HTTP\" }); const apiDefaultStage = new aws . apigatewayv2 . Stage ( \"default\" , { apiId : api . id , autoDeploy : true , name : \" $ default \" , accessLogSettings : { destinationArn : logGroupApi . arn , format : ` { \"requestId\" : \" $ context .requestId\" , \"requestTime\" : \" $ context .requestTime\" , \"httpMethod\" : \" $ context .httpMethod\" , \"httpPath\" : \" $ context .path\" , \"status\" : \" $ context .status\" , \"integrationError\" : \" $ context .integrationErrorMessage\" } ` } }) const lambdaIntegration = new aws . apigatewayv2 . Integration ( \"hello-http4k-api-lambda-integration\" , { apiId : api . id , integrationType : \"AWS_PROXY\" , integrationUri : lambdaFunction . arn , payloadFormatVersion : \"2.0\" }); let serverlessHttp4kApiRoute = \"hello-http4k\" ; const apiDefaultRole = new aws . apigatewayv2 . Route ( serverlessHttp4kApiRoute + \"-api-route\" , { apiId : api . id , routeKey : `$ default ` , target : pulumi . interpolate ` integrations / $ { lambdaIntegration . id } ` }); export const publishedUrl = apiDefaultStage . invokeUrl ; The most important things to note in the above file are: (line 30) - the name of the input ZIP file - ensure this is correct from the last step. (line 70) - the publishedUrl - this latter value in used by Pulumi to bind the URL of our Lambda to once it has been deployed and will be displayed upon deployment. Step 6 \u00b6 Deploy your ZIP file to AWS with: pulumi up --stack dev --yes Pulumi will churn for a bit and all being well will display the URL at the end of the process. Step 7 \u00b6 You can now call your deployed lambda by visiting: https://{publishedUrl}/echo/helloHttp4k . You should see helloHttp4k in the response body. Step 8 \u00b6 To avoid any unwanted AWS charges, don't forget to delete all of the resources in your stack when you've finished by running: pulumi destroy --stack dev --yes Congratulations! \u00b6 You have successfully deployed and invoked an http4k Lambda to AWS! To see a complete example of a similar setup, you can check out the complete AWS Lambda app from the http4k Examples repo (Ready for more? Let's move on to deploying a native http4k GraalVM Lambda to AWS )","title":"Deploying an http4k app to AWS Lambda"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#pre-requisites","text":"All the pre-requisites from the Your first http4k app tutorial. The AWS CLI installed and an AWS profile set up to use. See here . Pulumi CLI installed and configured for your system. See here . A working http4k application, built with Gradle. You can generate one from the http4k Toolbox if required. For this example, we're going to assume a simple \"echo\" HttpHandler which responds to GET /echo/{message:.*}\" .","title":"Pre-requisites:"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_1","text":"We need to add the AWS Lambda Serverless module to our project. Install it into your build.gradle file with: implementation ( \"org.http4k:http4k-serverless-lambda: ${ http4kVersion } \" )","title":"Step 1"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_2","text":"The AWS Lambda runtime works by implementing an AWS supplied interface Request/StreamHandler and configuring that class to be loaded on an invocation on the lambda. The invocation requests themselves are transmitted as JSON which normally is unmarshalled by Jackson in the AWS runtime into the relevant AWS Event class. http4k supplies pre-built StreamHandler adapters (they are faster) using the lightweight Moshi library to convert the invocations to standard http4k Request/Responses. We need to decide which version of the ApiGateway binding to use and then use the correct http4k class. For this example we're going to use ApiGateway HTTP Version 2, so we simply create a class HelloServerlessHttp4k extending the relevant http4k class and pass our app HttpHandler to it's constructor: package guide.tutorials.serverless_http4k_with_aws_lambda import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.routing.bind import org.http4k.routing.path import org.http4k.routing.routes import org.http4k.serverless.ApiGatewayV2LambdaFunction val http4kApp = routes ( \"/echo/{message:.*}\" bind GET to { Response ( OK ). body ( it . path ( \"message\" ) ?: \"(nothing to echo, use /echo/)\" ) }, \"/\" bind GET to { Response ( OK ). body ( \"ok\" ) } ) @Suppress ( \"unused\" ) class HelloServerlessHttp4k : ApiGatewayV2LambdaFunction ( http4kApp )","title":"Step 2"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_3","text":"To build the Lambda code into a ZIP file, we need to add a task to our build.gradle : tasks . register ( \"buildLambdaZip\" , Zip ) { from compileKotlin from processResources into ( \"lib\" ) { from configurations . compileClasspath } } Run the new task with: ./gradlew buildLambdaZip ... and then take a note of the ZIP file that appears in build/distributions .","title":"Step 3"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_4","text":"The next step is to configure the AWS resources to send requests to our Lambda function. This is quite involved as far as setup is concerned, but for this we're using Pulumi as it provides a simple way to get started. The concept here is that you configure a \"stack\" in your chosen language (we're choosing TypeScript). On the command line, generate a new Pulumi configuration by running: pulumi new --name hello-http4k --force ... followed by selecting aws-typescript and then all the default options until Pulumi has completed.","title":"Step 4"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_5","text":"Pulumi creates a few files in the directory, but the most interesting one is index.ts , which is where we will configure our AWS resources for exposing the Lambda. Overwrite the content of index.ts with: import * as pulumi from \"@pulumi/pulumi\" ; import * as aws from \"@pulumi/aws\" ; import {RolePolicyAttachment} from \"@pulumi/aws/iam\" ; const defaultRole = new aws . iam . Role ( \"hello-http4k-default-role\" , { assumeRolePolicy : ` { \"Version\" : \"2012-10-17\" , \"Statement\" : [ { \"Action\" : \"sts:AssumeRole\" , \"Principal\" : { \"Service\" : \"lambda.amazonaws.com\" }, \"Effect\" : \"Allow\" , \"Sid\" : \"\" } ] } ` }); new RolePolicyAttachment ( \"hello-http4k-default-role-policy\" , { role : defaultRole , policyArn : aws . iam . ManagedPolicies . AWSLambdaBasicExecutionRole }); const lambdaFunction = new aws . lambda . Function ( \"hello-http4k\" , { code : new pulumi . asset . FileArchive ( \"build/distributions/HelloWorld.zip\" ), handler : \"guide.tutorials.serverless_http4k_with_aws_lambda.HelloServerlessHttp4k\" , role : defaultRole . arn , runtime : \"java11\" , timeout : 15 }); const logGroupApi = new aws . cloudwatch . LogGroup ( \"hello-http4k-api-route\" , { name : \"hello-http4k\" , }); const apiGatewayPermission = new aws . lambda . Permission ( \"hello-http4k-gateway-permission\" , { action : \"lambda:InvokeFunction\" , \"function\" : lambdaFunction . name , principal : \"apigateway.amazonaws.com\" }); const api = new aws . apigatewayv2 . Api ( \"hello-http4k-api\" , { protocolType : \"HTTP\" }); const apiDefaultStage = new aws . apigatewayv2 . Stage ( \"default\" , { apiId : api . id , autoDeploy : true , name : \" $ default \" , accessLogSettings : { destinationArn : logGroupApi . arn , format : ` { \"requestId\" : \" $ context .requestId\" , \"requestTime\" : \" $ context .requestTime\" , \"httpMethod\" : \" $ context .httpMethod\" , \"httpPath\" : \" $ context .path\" , \"status\" : \" $ context .status\" , \"integrationError\" : \" $ context .integrationErrorMessage\" } ` } }) const lambdaIntegration = new aws . apigatewayv2 . Integration ( \"hello-http4k-api-lambda-integration\" , { apiId : api . id , integrationType : \"AWS_PROXY\" , integrationUri : lambdaFunction . arn , payloadFormatVersion : \"2.0\" }); let serverlessHttp4kApiRoute = \"hello-http4k\" ; const apiDefaultRole = new aws . apigatewayv2 . Route ( serverlessHttp4kApiRoute + \"-api-route\" , { apiId : api . id , routeKey : `$ default ` , target : pulumi . interpolate ` integrations / $ { lambdaIntegration . id } ` }); export const publishedUrl = apiDefaultStage . invokeUrl ; The most important things to note in the above file are: (line 30) - the name of the input ZIP file - ensure this is correct from the last step. (line 70) - the publishedUrl - this latter value in used by Pulumi to bind the URL of our Lambda to once it has been deployed and will be displayed upon deployment.","title":"Step 5"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_6","text":"Deploy your ZIP file to AWS with: pulumi up --stack dev --yes Pulumi will churn for a bit and all being well will display the URL at the end of the process.","title":"Step 6"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_7","text":"You can now call your deployed lambda by visiting: https://{publishedUrl}/echo/helloHttp4k . You should see helloHttp4k in the response body.","title":"Step 7"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#step_8","text":"To avoid any unwanted AWS charges, don't forget to delete all of the resources in your stack when you've finished by running: pulumi destroy --stack dev --yes","title":"Step 8"},{"location":"guide/tutorials/serverless_http4k_with_aws_lambda/#congratulations","text":"You have successfully deployed and invoked an http4k Lambda to AWS! To see a complete example of a similar setup, you can check out the complete AWS Lambda app from the http4k Examples repo (Ready for more? Let's move on to deploying a native http4k GraalVM Lambda to AWS )","title":"Congratulations!"},{"location":"guide/tutorials/tdding_http4k/","text":"TDDing http4k \u00b6 This post is a guide to how we build http4k applications test first to provide excellent test coverage driven by decoupled tests. Application Design \u00b6 For this example, we will use an example of a Maths app with the following requirements: The app must add 2 numbers together via an HTTP call The app must multiply 2 numbers together via an HTTP call Answers generated by the service will be logged (via HTTP POST) to another server - the Recorder. Apps can generally be split into 3 tiers: Endpoint: HttpHandlers are constructed individually, by providing a builder function which takes the business-level dependencies. Application: Builder function which takes the transport-level dependencies, and converts them into business-level dependencies. All routes are constructed and collected in this tier. Server: Builder function which takes the configuration for environmental concerns such as ports and downstream urls. The tutorial is split into 4 sections: Part 1: Building a walking skeleton Part 2: Adding an endpoint Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"TDDing http4k"},{"location":"guide/tutorials/tdding_http4k/#tdding_http4k","text":"This post is a guide to how we build http4k applications test first to provide excellent test coverage driven by decoupled tests.","title":"TDDing http4k"},{"location":"guide/tutorials/tdding_http4k/#application_design","text":"For this example, we will use an example of a Maths app with the following requirements: The app must add 2 numbers together via an HTTP call The app must multiply 2 numbers together via an HTTP call Answers generated by the service will be logged (via HTTP POST) to another server - the Recorder. Apps can generally be split into 3 tiers: Endpoint: HttpHandlers are constructed individually, by providing a builder function which takes the business-level dependencies. Application: Builder function which takes the transport-level dependencies, and converts them into business-level dependencies. All routes are constructed and collected in this tier. Server: Builder function which takes the configuration for environmental concerns such as ports and downstream urls. The tutorial is split into 4 sections: Part 1: Building a walking skeleton Part 2: Adding an endpoint Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"Application Design"},{"location":"guide/tutorials/tdding_http4k/_1/","text":"Until we have an application that can be deployed, we cannot create any business value. The Walking Skeleton model dictates that putting the most trivial endpoint into a production environment will prove our deployment pipeline is sound, and helps to set the direction for the testing strategy that we will use going forward. We start with in ICT (In-Container-Test), which have the job of testing server-level concerns such as monitoring, documentation, and checking in a high-level way that the business endpoints are wired correctly. Requirements: \u00b6 The service can be pinged over HTTP to prove that is still alive. Tests: \u00b6 package guide.tutorials.tdding_http4k._1 import com.natpryce.hamkrest.assertion.assertThat import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `responds to ping` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) } } Production: \u00b6 package guide.tutorials.tdding_http4k._1 import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = { _ : Request -> Response ( OK ) }. asServer ( Jetty ( port )) Part 2: Adding an endpoint Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"TDDing http4k Part 1: Building a walking skeleton"},{"location":"guide/tutorials/tdding_http4k/_1/#requirements","text":"The service can be pinged over HTTP to prove that is still alive.","title":"Requirements:"},{"location":"guide/tutorials/tdding_http4k/_1/#tests","text":"package guide.tutorials.tdding_http4k._1 import com.natpryce.hamkrest.assertion.assertThat import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `responds to ping` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) } }","title":"Tests:"},{"location":"guide/tutorials/tdding_http4k/_1/#production","text":"package guide.tutorials.tdding_http4k._1 import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = { _ : Request -> Response ( OK ) }. asServer ( Jetty ( port )) Part 2: Adding an endpoint Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"Production:"},{"location":"guide/tutorials/tdding_http4k/_2/","text":"Part 1: Building a walking skeleton Starting with another EndToEnd test, we can then drill-down into the functional behaviour of the system by introducing OCT (Out of Container) tests and converting the e2e test to just test endpoint wiring (so far). The common assertions have also been converted to reusable extension methods on Response. Requirements: \u00b6 Implement an \"add\" service, which will sum a number of integer values. Tests: \u00b6 package guide.tutorials.tdding_http4k._2 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import guide.tutorials.tdding_http4k._2.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) } } class AddFunctionalTest { private val client = MyMathsApp () @Test fun `adds values together` () { client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } } Production: \u00b6 package guide.tutorials.tdding_http4k._2 import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = MyMathsApp (). asServer ( Jetty ( port )) fun MyMathsApp (): HttpHandler = CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to { request : Request -> val valuesToAdd = Query . int (). multi . defaulted ( \"value\" , listOf ())( request ) Response ( OK ). body ( valuesToAdd . sum (). toString ()) } ) ) Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"TDDing http4k Part 2: Adding an endpoint"},{"location":"guide/tutorials/tdding_http4k/_2/#requirements","text":"Implement an \"add\" service, which will sum a number of integer values.","title":"Requirements:"},{"location":"guide/tutorials/tdding_http4k/_2/#tests","text":"package guide.tutorials.tdding_http4k._2 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import guide.tutorials.tdding_http4k._2.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) } } class AddFunctionalTest { private val client = MyMathsApp () @Test fun `adds values together` () { client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } }","title":"Tests:"},{"location":"guide/tutorials/tdding_http4k/_2/#production","text":"package guide.tutorials.tdding_http4k._2 import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = MyMathsApp (). asServer ( Jetty ( port )) fun MyMathsApp (): HttpHandler = CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to { request : Request -> val valuesToAdd = Query . int (). multi . defaulted ( \"value\" , listOf ())( request ) Response ( OK ). body ( valuesToAdd . sum (). toString ()) } ) ) Part 3: Adding another endpoint Part 4: Adding an external dependency","title":"Production:"},{"location":"guide/tutorials/tdding_http4k/_3/","text":"Part 1: Building a walking skeleton Part 2: Adding an endpoint Requirements: \u00b6 Implement a \"multiply\" service, which will find the product of a number of integer values. Tests: \u00b6 package guide.tutorials.tdding_http4k._3 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import guide.tutorials.tdding_http4k._3.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) client ( Request ( GET , \"http://localhost: ${ server . port () } /multiply?value=2&value=4\" ) ). answerShouldBe ( 8 ) } } class AddFunctionalTest { private val client = MyMathsApp () @Test fun `adds values together` () { client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } } class MultiplyFunctionalTest { private val client = MyMathsApp () @Test fun `products values together` () { client ( Request ( GET , \"/multiply?value=2&value=4\" )). answerShouldBe ( 8 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/multiply\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/multiply?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } } Production: \u00b6 package guide.tutorials.tdding_http4k._3 import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = MyMathsApp (). asServer ( Jetty ( port )) fun MyMathsApp (): HttpHandler = CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to calculate { it . sum () }, \"/multiply\" bind GET to calculate { it . fold ( 1 ) { memo , next -> memo * next } } ) ) private fun calculate ( fn : ( List < Int > ) -> Int ): ( Request ) -> Response { val values = Query . int (). multi . defaulted ( \"value\" , listOf ()) return { request : Request -> val valuesToCalc = values ( request ) val answer = if ( valuesToCalc . isEmpty ()) 0 else fn ( valuesToCalc ) Response ( OK ). body ( answer . toString ()) } } Part 4: Adding an external dependency","title":"TDDing http4k Part 3: Adding another endpoint"},{"location":"guide/tutorials/tdding_http4k/_3/#requirements","text":"Implement a \"multiply\" service, which will find the product of a number of integer values.","title":"Requirements:"},{"location":"guide/tutorials/tdding_http4k/_3/#tests","text":"package guide.tutorials.tdding_http4k._3 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import guide.tutorials.tdding_http4k._3.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } class EndToEndTest { private val client = OkHttp () private val server = MyMathServer ( 0 ) @BeforeEach fun setup () { server . start () } @AfterEach fun teardown () { server . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) client ( Request ( GET , \"http://localhost: ${ server . port () } /multiply?value=2&value=4\" ) ). answerShouldBe ( 8 ) } } class AddFunctionalTest { private val client = MyMathsApp () @Test fun `adds values together` () { client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } } class MultiplyFunctionalTest { private val client = MyMathsApp () @Test fun `products values together` () { client ( Request ( GET , \"/multiply?value=2&value=4\" )). answerShouldBe ( 8 ) } @Test fun `answer is zero when no values` () { client ( Request ( GET , \"/multiply\" )). answerShouldBe ( 0 ) } @Test fun `bad request when some values are not numbers` () { assertThat ( client ( Request ( GET , \"/multiply?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) } }","title":"Tests:"},{"location":"guide/tutorials/tdding_http4k/_3/#production","text":"package guide.tutorials.tdding_http4k._3 import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.core.then import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer fun MyMathServer ( port : Int ): Http4kServer = MyMathsApp (). asServer ( Jetty ( port )) fun MyMathsApp (): HttpHandler = CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to calculate { it . sum () }, \"/multiply\" bind GET to calculate { it . fold ( 1 ) { memo , next -> memo * next } } ) ) private fun calculate ( fn : ( List < Int > ) -> Int ): ( Request ) -> Response { val values = Query . int (). multi . defaulted ( \"value\" , listOf ()) return { request : Request -> val valuesToCalc = values ( request ) val answer = if ( valuesToCalc . isEmpty ()) 0 else fn ( valuesToCalc ) Response ( OK ). body ( answer . toString ()) } } Part 4: Adding an external dependency","title":"Production:"},{"location":"guide/tutorials/tdding_http4k/_4/","text":"Part 1: Building a walking skeleton Part 2: Adding an endpoint Part 3: Adding another endpoint At this point, the separation of the layers starts to become clear: - The server layer is responsible for taking external configuration and instantiating the app layer. - The application layer API is only in terms of HTTP transports - it constructs business level abstractions which are passed down into to the individual endpoints The process here is to create fake versions of the dependency which can be tested against through the business interface. This requires another style of testing, CDCs (Consumer Driven Contracts), to be created. These contract tests ensure that our interactions with the external service are valid. Requirements: \u00b6 Results from calculations should be POSTed via HTTP to another \"answer recording\" service. Implementation Notes: \u00b6 The following process is followed to us to the final state, whilst always allowing us to keep the build green: Determine the HTTP contract required by the Recorder (in this case an HTTP POST to /{answer} Create RecorderCdc and RealRecorderTest and make it pass for the real dependency by implementing the Recorder Create FakeRecorderTest and FakeRecorderHttp and make it pass for the fake. We can now use the Fake to implement our requirement Include the FakeRecorderHttp in the setup of EndToEndTest, starting and stopping the server (even though it's not doing anything) Pass the configuration of the Recorder (baseUri) into the MyMathServer, which uses it to create the recorder HttpHandler Factor AppEnvironment out of the functional tests. This is where all the setup of the functional testing environment will be done Introduce the recorder HttpHandler to MyMathApp, creating a FakeRecorderHttp in the AppEnvironment Alter the AddFunctionalTest and MultiplyFunctionalTest to set the expectations on the interactions recorder in FakeRecorderHttp In MyMathApp, create the Recorder business implementation (Recorder) and pass it to calculate(), then implement the call to record() Tests: \u00b6 package guide.tutorials.tdding_http4k._4 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import guide.tutorials.tdding_http4k._4.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.ACCEPTED import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.lens.Path import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Jetty import org.http4k.server.asServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.util.Random object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } abstract class RecorderCdc { abstract val client : HttpHandler @Test fun `records answer` () { Recorder ( client ). record ( 123 ) checkAnswerRecorded () } open fun checkAnswerRecorded () {} } class FakeRecorderHttp : HttpHandler { val calls = mutableListOf < Int > () private val answer = Path . int (). of ( \"answer\" ) private val app = CatchLensFailure . then ( routes ( \"/{answer}\" bind POST to { request -> calls . add ( answer ( request )); Response ( ACCEPTED ) } ) ) override fun invoke ( request : Request ): Response = app ( request ) } class FakeRecorderTest : RecorderCdc () { override val client = FakeRecorderHttp () override fun checkAnswerRecorded () { assertThat ( client . calls , equalTo ( listOf ( 123 ))) } } @Disabled // this obviously doesn't exist, so we ignore it here class RealRecorderTest : RecorderCdc () { override val client = SetHostFrom ( Uri . of ( \"http://realrecorder\" )). then ( OkHttp ()) } class EndToEndTest { private val port = Random (). nextInt ( 1000 ) + 8000 private val recorderPort = port + 1 private val client = OkHttp () private val recorder = FakeRecorderHttp () private val server = MyMathServer ( 0 , Uri . of ( \"http://localhost: $ recorderPort \" )) private val recorderServer = recorder . asServer ( Jetty ( recorderPort )) @BeforeEach fun setup () { recorderServer . start () server . start () } @AfterEach fun teardown () { server . stop () recorderServer . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) client ( Request ( GET , \"http://localhost: ${ server . port () } /multiply?value=2&value=4\" ) ). answerShouldBe ( 8 ) } } class AppEnvironment { val recorder = FakeRecorderHttp () val client = MyMathsApp ( recorder ) } class AddFunctionalTest { private val env = AppEnvironment () @Test fun `adds values together` () { env . client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 3 ))) } @Test fun `answer is zero when no values` () { env . client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 0 ))) } @Test fun `bad request when some values are not numbers` () { assertThat ( env . client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) assertThat ( env . recorder . calls . isEmpty (), equalTo ( true )) } } class MultiplyFunctionalTest { private val env = AppEnvironment () @Test fun `products values together` () { env . client ( Request ( GET , \"/multiply?value=2&value=4\" )). answerShouldBe ( 8 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 8 ))) } @Test fun `answer is zero when no values` () { env . client ( Request ( GET , \"/multiply\" )). answerShouldBe ( 0 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 0 ))) } @Test fun `bad request when some values are not numbers` () { assertThat ( env . client ( Request ( GET , \"/multiply?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) assertThat ( env . recorder . calls . isEmpty (), equalTo ( true )) } } Production: \u00b6 package guide.tutorials.tdding_http4k._4 import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.ACCEPTED import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer class Recorder ( private val client : HttpHandler ) { fun record ( value : Int ) { val response = client ( Request ( POST , \"/ $ value \" )) if ( response . status != ACCEPTED ) throw RuntimeException ( \"recorder returned ${ response . status } \" ) } } fun MyMathsApp ( recorderHttp : HttpHandler ): HttpHandler { val recorder = Recorder ( recorderHttp ) return CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to calculate ( recorder ) { it . sum () }, \"/multiply\" bind GET to calculate ( recorder ) { it . fold ( 1 ) { memo , next -> memo * next } } ) ) } private fun calculate ( recorder : Recorder , fn : ( List < Int > ) -> Int ): ( Request ) -> Response { val values = Query . int (). multi . defaulted ( \"value\" , listOf ()) return { request : Request -> val valuesToCalc = values ( request ) val answer = if ( valuesToCalc . isEmpty ()) 0 else fn ( valuesToCalc ) recorder . record ( answer ) Response ( OK ). body ( answer . toString ()) } } fun MyMathServer ( port : Int , recorderBaseUri : Uri ): Http4kServer = MyMathsApp ( SetHostFrom ( recorderBaseUri ). then ( OkHttp ())). asServer ( Jetty ( port ))","title":"Part 4: Adding an external dependency"},{"location":"guide/tutorials/tdding_http4k/_4/#requirements","text":"Results from calculations should be POSTed via HTTP to another \"answer recording\" service.","title":"Requirements:"},{"location":"guide/tutorials/tdding_http4k/_4/#implementation_notes","text":"The following process is followed to us to the final state, whilst always allowing us to keep the build green: Determine the HTTP contract required by the Recorder (in this case an HTTP POST to /{answer} Create RecorderCdc and RealRecorderTest and make it pass for the real dependency by implementing the Recorder Create FakeRecorderTest and FakeRecorderHttp and make it pass for the fake. We can now use the Fake to implement our requirement Include the FakeRecorderHttp in the setup of EndToEndTest, starting and stopping the server (even though it's not doing anything) Pass the configuration of the Recorder (baseUri) into the MyMathServer, which uses it to create the recorder HttpHandler Factor AppEnvironment out of the functional tests. This is where all the setup of the functional testing environment will be done Introduce the recorder HttpHandler to MyMathApp, creating a FakeRecorderHttp in the AppEnvironment Alter the AddFunctionalTest and MultiplyFunctionalTest to set the expectations on the interactions recorder in FakeRecorderHttp In MyMathApp, create the Recorder business implementation (Recorder) and pass it to calculate(), then implement the call to record()","title":"Implementation Notes:"},{"location":"guide/tutorials/tdding_http4k/_4/#tests","text":"package guide.tutorials.tdding_http4k._4 import com.natpryce.hamkrest.and import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import guide.tutorials.tdding_http4k._4.Matchers.answerShouldBe import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.ACCEPTED import org.http4k.core.Status.Companion.BAD_REQUEST import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.hamkrest.hasBody import org.http4k.hamkrest.hasStatus import org.http4k.lens.Path import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Jetty import org.http4k.server.asServer import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import java.util.Random object Matchers { fun Response . answerShouldBe ( expected : Int ) { assertThat ( this , hasStatus ( OK ). and ( hasBody ( expected . toString ()))) } } abstract class RecorderCdc { abstract val client : HttpHandler @Test fun `records answer` () { Recorder ( client ). record ( 123 ) checkAnswerRecorded () } open fun checkAnswerRecorded () {} } class FakeRecorderHttp : HttpHandler { val calls = mutableListOf < Int > () private val answer = Path . int (). of ( \"answer\" ) private val app = CatchLensFailure . then ( routes ( \"/{answer}\" bind POST to { request -> calls . add ( answer ( request )); Response ( ACCEPTED ) } ) ) override fun invoke ( request : Request ): Response = app ( request ) } class FakeRecorderTest : RecorderCdc () { override val client = FakeRecorderHttp () override fun checkAnswerRecorded () { assertThat ( client . calls , equalTo ( listOf ( 123 ))) } } @Disabled // this obviously doesn't exist, so we ignore it here class RealRecorderTest : RecorderCdc () { override val client = SetHostFrom ( Uri . of ( \"http://realrecorder\" )). then ( OkHttp ()) } class EndToEndTest { private val port = Random (). nextInt ( 1000 ) + 8000 private val recorderPort = port + 1 private val client = OkHttp () private val recorder = FakeRecorderHttp () private val server = MyMathServer ( 0 , Uri . of ( \"http://localhost: $ recorderPort \" )) private val recorderServer = recorder . asServer ( Jetty ( recorderPort )) @BeforeEach fun setup () { recorderServer . start () server . start () } @AfterEach fun teardown () { server . stop () recorderServer . stop () } @Test fun `all endpoints are mounted correctly` () { assertThat ( client ( Request ( GET , \"http://localhost: ${ server . port () } /ping\" )), hasStatus ( OK ) ) client ( Request ( GET , \"http://localhost: ${ server . port () } /add?value=1&value=2\" ) ). answerShouldBe ( 3 ) client ( Request ( GET , \"http://localhost: ${ server . port () } /multiply?value=2&value=4\" ) ). answerShouldBe ( 8 ) } } class AppEnvironment { val recorder = FakeRecorderHttp () val client = MyMathsApp ( recorder ) } class AddFunctionalTest { private val env = AppEnvironment () @Test fun `adds values together` () { env . client ( Request ( GET , \"/add?value=1&value=2\" )). answerShouldBe ( 3 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 3 ))) } @Test fun `answer is zero when no values` () { env . client ( Request ( GET , \"/add\" )). answerShouldBe ( 0 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 0 ))) } @Test fun `bad request when some values are not numbers` () { assertThat ( env . client ( Request ( GET , \"/add?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) assertThat ( env . recorder . calls . isEmpty (), equalTo ( true )) } } class MultiplyFunctionalTest { private val env = AppEnvironment () @Test fun `products values together` () { env . client ( Request ( GET , \"/multiply?value=2&value=4\" )). answerShouldBe ( 8 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 8 ))) } @Test fun `answer is zero when no values` () { env . client ( Request ( GET , \"/multiply\" )). answerShouldBe ( 0 ) assertThat ( env . recorder . calls , equalTo ( listOf ( 0 ))) } @Test fun `bad request when some values are not numbers` () { assertThat ( env . client ( Request ( GET , \"/multiply?value=1&value=notANumber\" )), hasStatus ( BAD_REQUEST ) ) assertThat ( env . recorder . calls . isEmpty (), equalTo ( true )) } }","title":"Tests:"},{"location":"guide/tutorials/tdding_http4k/_4/#production","text":"package guide.tutorials.tdding_http4k._4 import org.http4k.client.OkHttp import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.ACCEPTED import org.http4k.core.Status.Companion.OK import org.http4k.core.Uri import org.http4k.core.then import org.http4k.filter.ClientFilters.SetHostFrom import org.http4k.filter.ServerFilters.CatchLensFailure import org.http4k.lens.Query import org.http4k.lens.int import org.http4k.routing.bind import org.http4k.routing.routes import org.http4k.server.Http4kServer import org.http4k.server.Jetty import org.http4k.server.asServer class Recorder ( private val client : HttpHandler ) { fun record ( value : Int ) { val response = client ( Request ( POST , \"/ $ value \" )) if ( response . status != ACCEPTED ) throw RuntimeException ( \"recorder returned ${ response . status } \" ) } } fun MyMathsApp ( recorderHttp : HttpHandler ): HttpHandler { val recorder = Recorder ( recorderHttp ) return CatchLensFailure . then ( routes ( \"/ping\" bind GET to { _ : Request -> Response ( OK ) }, \"/add\" bind GET to calculate ( recorder ) { it . sum () }, \"/multiply\" bind GET to calculate ( recorder ) { it . fold ( 1 ) { memo , next -> memo * next } } ) ) } private fun calculate ( recorder : Recorder , fn : ( List < Int > ) -> Int ): ( Request ) -> Response { val values = Query . int (). multi . defaulted ( \"value\" , listOf ()) return { request : Request -> val valuesToCalc = values ( request ) val answer = if ( valuesToCalc . isEmpty ()) 0 else fn ( valuesToCalc ) recorder . record ( answer ) Response ( OK ). body ( answer . toString ()) } } fun MyMathServer ( port : Int , recorderBaseUri : Uri ): Http4kServer = MyMathsApp ( SetHostFrom ( recorderBaseUri ). then ( OkHttp ())). asServer ( Jetty ( port ))","title":"Production:"},{"location":"guide/tutorials/your_first_http4k_app/","text":"Welcome to the world of http4k! In this guide, we'll run you through the steps required to get up and running with your first Kotlin server application. We'll generate the project, make some requests to it and then build it into a runnable application. By the end, you'll have a fully working app built packaged and tested using the Gradle build tool. Pre-requisites: \u00b6 An internet connection JDK 19 or greater installed and set up An IDE/Text-editor - (http4k is developed using IntelliJ) Step 1 \u00b6 First, we'll generate and download a project template from the http4k Toolbox. Point your browser at toolbox.http4k.org and select the Project Wizard from the menu (alternatively, you can use the CLI to run through the same process using the command http4k generate project ). The Toolbox is designed as a questionnaire which will help you configure a working project from all of the available http4k modules. There are several stages, but for this guide we're just going to generate the project using the defaults. We explain each of the parts in the remainder of this step, but if you just want to skip to the end, just keep hitting Next until the progress bar hits 100% then go to step 2 : a. First we select a deployment model for our App - Server or Serverless . Hit Next to select Server . b. We don't need any realtime components such as Websockets. Hit Next . c. The third page allows us to select whichever Server backend we want. The default option is SunHttp which is pre-bundled with the core module. Hit Next . d. Next, we can choose an HTTP client. The Java HTTP Client is also pre-bundled. Hit Next . e. Parts 2 and 3 allows us to select bolt-on functionality and testing modules. Keep hitting Next until you get to Part 4 . e. Now we can name the main class and package of our application. If you're happy with the defaults (later steps will assume you are), hit Next twice. f. Finally, we can choose a build tool and packaging for the app. We'll use the defaults of Gradle with ZIP distribution. Step 2 \u00b6 Once you're finished, the Toolbox will review your choices and you can hit Download . Unzip and import the project into your IDE. In Intellij, you choose: File -> New -> Project from existing sources . and select the build.gradle file inside the unpacked ZIP. Step 3 \u00b6 The project is fully formed and buildable, it consists of: a. Build files and scripts for gradle b. A runnable program containing the http4k app in the src/main/kotlin directory c. The src/test/kotlin directory containing a working test and a runnable client program for our app. Step 4 \u00b6 First, let's take a tour of the /src/main/kotlin/HelloWorld.kt file which contains our production app. Lines 14-18 defines our production application. It consists of a single HTTP endpoint binding all HTTP GET requests on the path /ping to an HttpHandler function. This function takes an implicit Request parameter it (which it ignores), and just constructs and returns a Response object with status and a static body string. The return type of the call to routes() is also an HttpHandler . Lines 20-26 form a runnable program which starts our application. Lines 21 decorates our app with a Request printing Filter . This returns another HttpHandler . Lines 23 binds the app onto an instance of the SunHttp Server backend and starts it on port 9000 . Hit the little green arrow and the application will run and start. Point your browser at http://localhost:9000/ping to check that it's working. Note that the Filter is printing each incoming request to the console. Step 5 \u00b6 In /src/test/kotlin/HelloWorldClient.kt there is an example of an HTTP client which we can use to call our running app. Lines 12 creates an HTTP client - note that it's type is also HttpHandler . Lines 14 decorates the client with a Response printing Filter . The result type of new client is also HttpHandler . Lines 16 constructs an HTTP Request and calls the client app with it, receiving a response. Try running the client by hitting the little green arrow on line 11 . You'll see the Response printed to the console by the Filter , followed by a repeat of the body content, which is printed by line 18 . Step 6 \u00b6 Modify the request in /src/test/kotlin/HelloWorldClient.kt to point at http://localhost:9000/pong instead. Run the client program again and note that a 404 is printed. This happens because we have not bound an HttpHandler to that endpoint. To bind an HttpHandler to the pong endpoint, modify /src/main/kotlin/HelloWorld.kt by adding lines 18-20 below: Lines 19-21 add an HTTP endpoint binding all HTTP GET requests on the path /pong to an HttpHandler function. To see this update in action, first rerun the main application by hitting the green arrow now on line 23 in /src/main/kotlin/HelloWorld.kt , and then running the client again using the green arrow on line 11 in /src/test/kotlin/HelloWorldClient.kt . You should see the new \"ping\" response printed to the console. Step 7 \u00b6 A test for our app is found in /src/test/kotlin/HelloWorldTest.kt . Run it with the green arrow on line 10 and it should pass. Cool things to notice about the test: As our app is just an HttpHandler function, it runs entirely in-memory. It is therefore super-fast and completely threadsafe. There is no custom core or other setup required for the test to run. Request and Response objects are immutable data classes, and can therefore be compared safely in tests. Step 8 \u00b6 Let's package our app into a runnable Application. From the IDE run the distZip task. This will create an standard application ZIP file with scripts to run the app and deposit it in the build/distributions directory. You'll find the task in the Gradle tab under: HelloWorld -> Tasks -> Dstribution -> distZip . Congratulations! \u00b6 You have successfully: Created an working http4k project using the http4k Toolbox. Bound a simple HttpHandler function to particular HTTP endpoint. Seen how the http4k HttpHandler and Filter model provide a simple set of composable building blocks to construct HTTP applications. Tested the application entirely in-memory with no custom libraries or code. Packaged your http4k app into a ZIP file. To see a similar application, you can check out the complete Hello World app from the http4k Examples repo (Ready for more? Let's move on to deploying your http4k app as a Serverless Lambda to AWS )","title":"Your first http4k app"},{"location":"guide/tutorials/your_first_http4k_app/#pre-requisites","text":"An internet connection JDK 19 or greater installed and set up An IDE/Text-editor - (http4k is developed using IntelliJ)","title":"Pre-requisites:"},{"location":"guide/tutorials/your_first_http4k_app/#step_1","text":"First, we'll generate and download a project template from the http4k Toolbox. Point your browser at toolbox.http4k.org and select the Project Wizard from the menu (alternatively, you can use the CLI to run through the same process using the command http4k generate project ). The Toolbox is designed as a questionnaire which will help you configure a working project from all of the available http4k modules. There are several stages, but for this guide we're just going to generate the project using the defaults. We explain each of the parts in the remainder of this step, but if you just want to skip to the end, just keep hitting Next until the progress bar hits 100% then go to step 2 : a. First we select a deployment model for our App - Server or Serverless . Hit Next to select Server . b. We don't need any realtime components such as Websockets. Hit Next . c. The third page allows us to select whichever Server backend we want. The default option is SunHttp which is pre-bundled with the core module. Hit Next . d. Next, we can choose an HTTP client. The Java HTTP Client is also pre-bundled. Hit Next . e. Parts 2 and 3 allows us to select bolt-on functionality and testing modules. Keep hitting Next until you get to Part 4 . e. Now we can name the main class and package of our application. If you're happy with the defaults (later steps will assume you are), hit Next twice. f. Finally, we can choose a build tool and packaging for the app. We'll use the defaults of Gradle with ZIP distribution.","title":"Step 1"},{"location":"guide/tutorials/your_first_http4k_app/#step_2","text":"Once you're finished, the Toolbox will review your choices and you can hit Download . Unzip and import the project into your IDE. In Intellij, you choose: File -> New -> Project from existing sources . and select the build.gradle file inside the unpacked ZIP.","title":"Step 2"},{"location":"guide/tutorials/your_first_http4k_app/#step_3","text":"The project is fully formed and buildable, it consists of: a. Build files and scripts for gradle b. A runnable program containing the http4k app in the src/main/kotlin directory c. The src/test/kotlin directory containing a working test and a runnable client program for our app.","title":"Step 3"},{"location":"guide/tutorials/your_first_http4k_app/#step_4","text":"First, let's take a tour of the /src/main/kotlin/HelloWorld.kt file which contains our production app. Lines 14-18 defines our production application. It consists of a single HTTP endpoint binding all HTTP GET requests on the path /ping to an HttpHandler function. This function takes an implicit Request parameter it (which it ignores), and just constructs and returns a Response object with status and a static body string. The return type of the call to routes() is also an HttpHandler . Lines 20-26 form a runnable program which starts our application. Lines 21 decorates our app with a Request printing Filter . This returns another HttpHandler . Lines 23 binds the app onto an instance of the SunHttp Server backend and starts it on port 9000 . Hit the little green arrow and the application will run and start. Point your browser at http://localhost:9000/ping to check that it's working. Note that the Filter is printing each incoming request to the console.","title":"Step 4"},{"location":"guide/tutorials/your_first_http4k_app/#step_5","text":"In /src/test/kotlin/HelloWorldClient.kt there is an example of an HTTP client which we can use to call our running app. Lines 12 creates an HTTP client - note that it's type is also HttpHandler . Lines 14 decorates the client with a Response printing Filter . The result type of new client is also HttpHandler . Lines 16 constructs an HTTP Request and calls the client app with it, receiving a response. Try running the client by hitting the little green arrow on line 11 . You'll see the Response printed to the console by the Filter , followed by a repeat of the body content, which is printed by line 18 .","title":"Step 5"},{"location":"guide/tutorials/your_first_http4k_app/#step_6","text":"Modify the request in /src/test/kotlin/HelloWorldClient.kt to point at http://localhost:9000/pong instead. Run the client program again and note that a 404 is printed. This happens because we have not bound an HttpHandler to that endpoint. To bind an HttpHandler to the pong endpoint, modify /src/main/kotlin/HelloWorld.kt by adding lines 18-20 below: Lines 19-21 add an HTTP endpoint binding all HTTP GET requests on the path /pong to an HttpHandler function. To see this update in action, first rerun the main application by hitting the green arrow now on line 23 in /src/main/kotlin/HelloWorld.kt , and then running the client again using the green arrow on line 11 in /src/test/kotlin/HelloWorldClient.kt . You should see the new \"ping\" response printed to the console.","title":"Step 6"},{"location":"guide/tutorials/your_first_http4k_app/#step_7","text":"A test for our app is found in /src/test/kotlin/HelloWorldTest.kt . Run it with the green arrow on line 10 and it should pass. Cool things to notice about the test: As our app is just an HttpHandler function, it runs entirely in-memory. It is therefore super-fast and completely threadsafe. There is no custom core or other setup required for the test to run. Request and Response objects are immutable data classes, and can therefore be compared safely in tests.","title":"Step 7"},{"location":"guide/tutorials/your_first_http4k_app/#step_8","text":"Let's package our app into a runnable Application. From the IDE run the distZip task. This will create an standard application ZIP file with scripts to run the app and deposit it in the build/distributions directory. You'll find the task in the Gradle tab under: HelloWorld -> Tasks -> Dstribution -> distZip .","title":"Step 8"},{"location":"guide/tutorials/your_first_http4k_app/#congratulations","text":"You have successfully: Created an working http4k project using the http4k Toolbox. Bound a simple HttpHandler function to particular HTTP endpoint. Seen how the http4k HttpHandler and Filter model provide a simple set of composable building blocks to construct HTTP applications. Tested the application entirely in-memory with no custom libraries or code. Packaged your http4k app into a ZIP file. To see a similar application, you can check out the complete Hello World app from the http4k Examples repo (Ready for more? Let's move on to deploying your http4k app as a Serverless Lambda to AWS )","title":"Congratulations!"},{"location":"in_action/","text":"Media: \u00b6 \"Exploring the Testing Hyperpyramid with Kotlin & http4k\" @ KotlinConf 2023 ( video / slides / repo ) \"Talking Kotlin ep. 99: HTTP as a function with http4k\" ( video / podcast ) \"http4k: Server as a Function\" ( video ), part of JetBrain's Webinar series \"Server as a Function. In Kotlin. _ _ _\" @ KotlinConf 2018 ( video / slides ), by David Denton and Ivan Sanchez \"Writing Test Driven Apps with http4k\" ( video ), by David Denton and Ivan Sanchez , presented at Kotliners 2020. \"Designing Microservices in Functional Style\" ( video ) by Uberto Barbini , presented at VirtualJUG 2020. \"Introduction to http4k\" ( video ) by Dmitry Kandalov , based on previous conference talks, recorded in 2021. \"Test Driven Gilded Rose in Kotlin\" ( video series ) by Duncan McGregor , recorded in 2021-22. Also, check out the YouTube Playlist for the latest collection of talks featuring http4k. See http4k in action in these example projects and repos: \u00b6 There is a repository of helpful examples at http4k/examples , covering how to build http4k applications using various features. Standalone projects... \u00b6 Each project is tagged with the http4k features it demonstrates: TDD'd example application \u00b6 Templates Testing JSON / Lenses CD pipeline Approval Testing OpenApi Static resources Dropbox clone in 70 lines of Kotlin \u00b6 Templates Testing Http Client Multipart AWS CD pipeline Serverless GraalVM Simple websocket driven chat-server in 30 lines of Kotlin \u00b6 Testing Websockets CD Pipeline Static resources Stage-by-stage example of London-style TDD development process \u00b6 Testing Todo backend (standard routing) \u00b6 Testing JSON / Lenses CD pipeline Todo backend (contract routing) \u00b6 Testing JSON / Lenses OpenApi CD pipeline Real World example (Medium clone) \u00b6 Testing Contracts","title":"http4k in action"},{"location":"in_action/#media","text":"\"Exploring the Testing Hyperpyramid with Kotlin & http4k\" @ KotlinConf 2023 ( video / slides / repo ) \"Talking Kotlin ep. 99: HTTP as a function with http4k\" ( video / podcast ) \"http4k: Server as a Function\" ( video ), part of JetBrain's Webinar series \"Server as a Function. In Kotlin. _ _ _\" @ KotlinConf 2018 ( video / slides ), by David Denton and Ivan Sanchez \"Writing Test Driven Apps with http4k\" ( video ), by David Denton and Ivan Sanchez , presented at Kotliners 2020. \"Designing Microservices in Functional Style\" ( video ) by Uberto Barbini , presented at VirtualJUG 2020. \"Introduction to http4k\" ( video ) by Dmitry Kandalov , based on previous conference talks, recorded in 2021. \"Test Driven Gilded Rose in Kotlin\" ( video series ) by Duncan McGregor , recorded in 2021-22. Also, check out the YouTube Playlist for the latest collection of talks featuring http4k.","title":"Media:"},{"location":"in_action/#see_http4k_in_action_in_these_example_projects_and_repos","text":"There is a repository of helpful examples at http4k/examples , covering how to build http4k applications using various features.","title":"See http4k in action in these example projects and repos:"},{"location":"in_action/#standalone_projects","text":"Each project is tagged with the http4k features it demonstrates:","title":"Standalone projects..."},{"location":"in_action/#tddd_example_application","text":"Templates Testing JSON / Lenses CD pipeline Approval Testing OpenApi Static resources","title":"TDD'd example application"},{"location":"in_action/#dropbox_clone_in_70_lines_of_kotlin","text":"Templates Testing Http Client Multipart AWS CD pipeline Serverless GraalVM","title":"Dropbox clone in 70 lines of Kotlin"},{"location":"in_action/#simple_websocket_driven_chat-server_in_30_lines_of_kotlin","text":"Testing Websockets CD Pipeline Static resources","title":"Simple websocket driven chat-server in 30 lines of Kotlin"},{"location":"in_action/#stage-by-stage_example_of_london-style_tdd_development_process","text":"Testing","title":"Stage-by-stage example of London-style TDD development process"},{"location":"in_action/#todo_backend_standard_routing","text":"Testing JSON / Lenses CD pipeline","title":"Todo backend (standard routing)"},{"location":"in_action/#todo_backend_contract_routing","text":"Testing JSON / Lenses OpenApi CD pipeline","title":"Todo backend (contract routing)"},{"location":"in_action/#real_world_example_medium_clone","text":"Testing Contracts","title":"Real World example (Medium clone)"},{"location":"performance/","text":"The http4k server-backend modules provide a very thin adapter layer over the raw APIs of the underlying servers, so generally performs at a very low overhead compared to the raw server. Tech Empower Benchmarks \u00b6 We have entered http4k into the prominent Tech Empower Framework Benchmarks project, which assesses frameworks over a series of realistic tests. For this benchmark, no customisation or performance tuning of the underlying servers is done - the default application HttpHandler is used which is then plugged into each custom backend, as below: fun main () { Http4kBenchmarkServer ( PostgresDatabase ()). start ( Undertow ( 9000 )) } Command-line JVM options, however, were tuned for the test to take advantage of various JVM features. The full implementation of the benchmark can be found here . Results - Round 22 \u00b6 Overall, http4k continues to do well in this round of benchmarking, placing 48/159 - especially considering that the ethos of the library is one of excellent Developer experience over and above high-end performance (which tends to result in less friendly APIs). Rankings below are filtered for JVM libraries: Composite ranking: results : \u00b6 *Top rank: 13/41 DB query + HTML rendering: results : \u00b6 Top rank: 25/146 - Apache backend Database driver used is PostgreSql backed by a Hikari pool. Rocker templating engine used for rendering. Multiple DB queries: results : \u00b6 Top rank: 23/145 - Jetty Loom backend Database driver used is Postgres Vertx Client backed by a Hikari pool. Single DB query: results : \u00b6 Top rank: 25/151 - Apache backend Database driver used is PostgreSql backed by a Hikari pool. Random DB updates: results : \u00b6 Top rank: 41/138 - Jetty Loom backend Database driver used is Postgres Vertx Client backed by a Hikari pool. JSON Serialization: results : \u00b6 Top rank: 59/152 - Netty backend The standard Argo JSON module used for JSON creation and marshalling. Plaintext pipelining: results : \u00b6 Top rank: 84/153 - Netty backend Recommendations \u00b6 Benchmark your own app's performance trying different engines if performance is critical. The Tech Empower benchmarks attempt to simulate simple real-world scenarios, but they can behave drastically different than your app. One other consideration is test time; some engines start up much faster than others.","title":"Performance"},{"location":"performance/#tech_empower_benchmarks","text":"We have entered http4k into the prominent Tech Empower Framework Benchmarks project, which assesses frameworks over a series of realistic tests. For this benchmark, no customisation or performance tuning of the underlying servers is done - the default application HttpHandler is used which is then plugged into each custom backend, as below: fun main () { Http4kBenchmarkServer ( PostgresDatabase ()). start ( Undertow ( 9000 )) } Command-line JVM options, however, were tuned for the test to take advantage of various JVM features. The full implementation of the benchmark can be found here .","title":"Tech Empower Benchmarks"},{"location":"performance/#results_-_round_22","text":"Overall, http4k continues to do well in this round of benchmarking, placing 48/159 - especially considering that the ethos of the library is one of excellent Developer experience over and above high-end performance (which tends to result in less friendly APIs). Rankings below are filtered for JVM libraries:","title":"Results - Round 22"},{"location":"performance/#composite_ranking_results","text":"*Top rank: 13/41","title":"Composite ranking: results:"},{"location":"performance/#db_query_html_rendering_results","text":"Top rank: 25/146 - Apache backend Database driver used is PostgreSql backed by a Hikari pool. Rocker templating engine used for rendering.","title":"DB query + HTML rendering: results:"},{"location":"performance/#multiple_db_queries_results","text":"Top rank: 23/145 - Jetty Loom backend Database driver used is Postgres Vertx Client backed by a Hikari pool.","title":"Multiple DB queries: results:"},{"location":"performance/#single_db_query_results","text":"Top rank: 25/151 - Apache backend Database driver used is PostgreSql backed by a Hikari pool.","title":"Single DB query: results:"},{"location":"performance/#random_db_updates_results","text":"Top rank: 41/138 - Jetty Loom backend Database driver used is Postgres Vertx Client backed by a Hikari pool.","title":"Random DB updates: results:"},{"location":"performance/#json_serialization_results","text":"Top rank: 59/152 - Netty backend The standard Argo JSON module used for JSON creation and marshalling.","title":"JSON Serialization: results:"},{"location":"performance/#plaintext_pipelining_results","text":"Top rank: 84/153 - Netty backend","title":"Plaintext pipelining: results:"},{"location":"performance/#recommendations","text":"Benchmark your own app's performance trying different engines if performance is critical. The Tech Empower benchmarks attempt to simulate simple real-world scenarios, but they can behave drastically different than your app. One other consideration is test time; some engines start up much faster than others.","title":"Recommendations"},{"location":"quickstart/","text":"Depending on your learning style, there are a number of options to get started with http4k ... I'm starting from scratch \u00b6 Follow a tutorial : There is a step-by-step beginner tutorial . This will get you up and running with a basic buildable project. I'd like a helping hand \u00b6 Generate a new project with the http4k Toolbox : We have developed a set of useful tools for Developers working with the http4k toolset to turbo-charge app development. Collectively, this is known as the http4k Toolbox . These tools include: A Project Wizard that generates entire bootstrap Server and Serverless project source folders - including fully working starter code, build tooling, extra modules and packaging options. From OpenAPI v2 & V3 specification JSON/YAML, generate an entire working http4k Server, Client and Model objects (generated from JSON Schema). Generate Kotlin Data Class definitions from an inputted JSON, YAML, or XML document. I'm already set up and just need to integrate! \u00b6 Add http4k into an existing project : This simple example demonstrates how to serve and consume HTTP services using http4k . To install, add these dependencies to your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-client-apache\" ) } The following creates a simple endpoint, binds it to a Undertow server then starts, queries, and stops it. package quickstart import org.http4k.client.ApacheClient import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val app = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val server = app . asServer ( Undertow ( 9000 )). start () val client = ApacheClient () val request = Request ( Method . GET , \"http://localhost:9000\" ). query ( \"name\" , \"John Doe\" ) println ( client ( request )) server . stop () } I want to see what http4k can do! \u00b6 See how it's done in the Examples Repo : For fully self-contained examples demonstrates the standout features of http4k , there is a GitHub repository at http4k/examples .","title":"Quickstart"},{"location":"quickstart/#im_starting_from_scratch","text":"Follow a tutorial : There is a step-by-step beginner tutorial . This will get you up and running with a basic buildable project.","title":"I'm starting from scratch"},{"location":"quickstart/#id_like_a_helping_hand","text":"Generate a new project with the http4k Toolbox : We have developed a set of useful tools for Developers working with the http4k toolset to turbo-charge app development. Collectively, this is known as the http4k Toolbox . These tools include: A Project Wizard that generates entire bootstrap Server and Serverless project source folders - including fully working starter code, build tooling, extra modules and packaging options. From OpenAPI v2 & V3 specification JSON/YAML, generate an entire working http4k Server, Client and Model objects (generated from JSON Schema). Generate Kotlin Data Class definitions from an inputted JSON, YAML, or XML document.","title":"I'd like a helping hand"},{"location":"quickstart/#im_already_set_up_and_just_need_to_integrate","text":"Add http4k into an existing project : This simple example demonstrates how to serve and consume HTTP services using http4k . To install, add these dependencies to your Gradle file: dependencies { implementation ( platform ( \"org.http4k:http4k-bom:5.27.0.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) implementation ( \"org.http4k:http4k-client-apache\" ) } The following creates a simple endpoint, binds it to a Undertow server then starts, queries, and stops it. package quickstart import org.http4k.client.ApacheClient import org.http4k.core.Method import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val app = { request : Request -> Response ( OK ). body ( \"Hello, ${ request . query ( \" name \" ) } !\" ) } val server = app . asServer ( Undertow ( 9000 )). start () val client = ApacheClient () val request = Request ( Method . GET , \"http://localhost:9000\" ). query ( \"name\" , \"John Doe\" ) println ( client ( request )) server . stop () }","title":"I'm already set up and just need to integrate!"},{"location":"quickstart/#i_want_to_see_what_http4k_can_do","text":"See how it's done in the Examples Repo : For fully self-contained examples demonstrates the standout features of http4k , there is a GitHub repository at http4k/examples .","title":"I want to see what http4k can do!"},{"location":"support/","text":"General Support \u00b6 In the first instance, try and see if there is a relevant example in the How-To Guides or FAQ - more and more are being added all the time! For issues, please describe giving as much detail as you can - including version and steps to recreate. Slack room: #http4k @ slack.kotlinlang.org Twitter: @http4k Note: As a general rule, you'll get a quicker response from the Slack channel. Commercial Support / Consulting \u00b6 We recognise that although organisations use happily Open Source Software such as http4k , they may gain additional peace of mind from having direct access to expert advice. For this purpose, the project creators offer a range of professional services to support library users in making the most of the powerful feature set which http4k provides, including: System architecture and design review Testing strategy and implementation review Project-level Developer and Team mentoring Troubleshooting integration issues and debugging Kotlin language Developer training Advice on extending or creating custom builds of http4k If your organisation is interested in getting support or consulting on this basis, please get in touch at: support@http4k.org http4k Training Courses \u00b6 The http4k project is a culmination of work on real-world projects in many different industries. Whilst the library is designed to be simple to learn and use, there are many advantages to learning from our mistakes when using it! To this end, the team have developed a significant amount of training materials which can be used to give teams new to http4k the best possible start, especially around the following areas: Advanced testing strategies for individual and entire fleets of services. Building, packaging and deploying http4k apps to on-premises and cloud-based environments. Advanced CI/CD design. Extending the http4k libraries. Configuration of http4k apps for use in data-sensitive environments (customer PII protection etc). These materials can be delivered both remotely and in on-site sessions. If you are interested in taking advantage of our mistakes, please get in touch at: support@http4k.org Logo Usage \u00b6 The http4k logo design and branding are copyright 2021 to the owners of http4k.org. If you'd like to use the http4k logo in a presentation or for other purposes, it's probably fine, but please reach out to let us know first on the channels above. We can then look at providing the relevant high-quality asset files.","title":"Getting support"},{"location":"support/#general_support","text":"In the first instance, try and see if there is a relevant example in the How-To Guides or FAQ - more and more are being added all the time! For issues, please describe giving as much detail as you can - including version and steps to recreate. Slack room: #http4k @ slack.kotlinlang.org Twitter: @http4k Note: As a general rule, you'll get a quicker response from the Slack channel.","title":"General Support"},{"location":"support/#commercial_support_consulting","text":"We recognise that although organisations use happily Open Source Software such as http4k , they may gain additional peace of mind from having direct access to expert advice. For this purpose, the project creators offer a range of professional services to support library users in making the most of the powerful feature set which http4k provides, including: System architecture and design review Testing strategy and implementation review Project-level Developer and Team mentoring Troubleshooting integration issues and debugging Kotlin language Developer training Advice on extending or creating custom builds of http4k If your organisation is interested in getting support or consulting on this basis, please get in touch at: support@http4k.org","title":"Commercial Support / Consulting"},{"location":"support/#http4k_training_courses","text":"The http4k project is a culmination of work on real-world projects in many different industries. Whilst the library is designed to be simple to learn and use, there are many advantages to learning from our mistakes when using it! To this end, the team have developed a significant amount of training materials which can be used to give teams new to http4k the best possible start, especially around the following areas: Advanced testing strategies for individual and entire fleets of services. Building, packaging and deploying http4k apps to on-premises and cloud-based environments. Advanced CI/CD design. Extending the http4k libraries. Configuration of http4k apps for use in data-sensitive environments (customer PII protection etc). These materials can be delivered both remotely and in on-site sessions. If you are interested in taking advantage of our mistakes, please get in touch at: support@http4k.org","title":"http4k Training Courses"},{"location":"support/#logo_usage","text":"The http4k logo design and branding are copyright 2021 to the owners of http4k.org. If you'd like to use the http4k logo in a presentation or for other purposes, it's probably fine, but please reach out to let us know first on the channels above. We can then look at providing the relevant high-quality asset files.","title":"Logo Usage"}]}
\ No newline at end of file
+{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"blog/","text":"http4k blog \u00b6 * 2020/11: http4k v4 Unleashed - There's a new major http4k release! Read about all the new stuff the team have been working on for http4k v4. 2021/2: Reassurance to http4k users regarding JCenter shutdown Regarding the JCenter shutdown. 2021/1: http4k v4: 17 platforms and counting... Announcement post for http4k v4. 2020/11: http4k Toolbox: Guns for show, knives for a pro Introduction to the http4k Toolbox website and CLI. 2020/10: Nanoservices: The Power of Composition You thought that microservices were a thing? Pah! The powerful abstractions in the http4k toolkit allow you to write entire useful apps which fit in a Tweet. 2020/09: A retrospective on http4k v3 In preparation for the upcoming release of v4, the http4k team thought we'd do a bit of a retrospective about all the things that have gone in the >260 releases of v3. 2019/05: Documenting http4k APIs with OpenAPI3 Overview of the OpenApi3 support available in the http4k library. 2018/11: Add typesafe 12-factor configuration to http4k apps with Environments Overview of how to configure http4k applications using the http4k-cloudnative module. 2018/02: TDDing http4k Step-by-step guide to TDDing a simple http4k application. 2017/12: Websockets. But typesafe. And testable. Without the Server Overview of typesafe Websocket support in http4k. 2017/11: Server as a Function. In Kotlin. Typesafe. Without the Server Overview of the Kotlin \"Server as a Function\" library, http4k.","title":"Overview"},{"location":"blog/#http4k_blog","text":"* 2020/11: http4k v4 Unleashed - There's a new major http4k release! Read about all the new stuff the team have been working on for http4k v4. 2021/2: Reassurance to http4k users regarding JCenter shutdown Regarding the JCenter shutdown. 2021/1: http4k v4: 17 platforms and counting... Announcement post for http4k v4. 2020/11: http4k Toolbox: Guns for show, knives for a pro Introduction to the http4k Toolbox website and CLI. 2020/10: Nanoservices: The Power of Composition You thought that microservices were a thing? Pah! The powerful abstractions in the http4k toolkit allow you to write entire useful apps which fit in a Tweet. 2020/09: A retrospective on http4k v3 In preparation for the upcoming release of v4, the http4k team thought we'd do a bit of a retrospective about all the things that have gone in the >260 releases of v3. 2019/05: Documenting http4k APIs with OpenAPI3 Overview of the OpenApi3 support available in the http4k library. 2018/11: Add typesafe 12-factor configuration to http4k apps with Environments Overview of how to configure http4k applications using the http4k-cloudnative module. 2018/02: TDDing http4k Step-by-step guide to TDDing a simple http4k application. 2017/12: Websockets. But typesafe. And testable. Without the Server Overview of typesafe Websocket support in http4k. 2017/11: Server as a Function. In Kotlin. Typesafe. Without the Server Overview of the Kotlin \"Server as a Function\" library, http4k.","title":"http4k blog"},{"location":"blog/documenting_apis_with_openapi/","text":"Documenting http4k APIs with OpenApi3 \u00b6 may 2019 / @daviddenton \u00b6 This post describes http4k support for fully describing and securing HTTP endpoints using version 3 of the OpenApi specification, providing typesafe JSON-schema documentation for messages and automatically validating incoming HTTP traffic. About OpenApi \u00b6 In microservice environments, some of the biggest challenges exist around the communications between processes that simply aren't present when you're doing monolith-based development. This manifests in many different operational ways such as monitoring, discovery and fault tolerance, but one of the key aspects is communicating the the HTTP contract provided by a particular service. There have been various efforts to standardise these aspects, and one of the most popular is OpenApi , which grew out of the original Swagger project. There are 3 key advantages to OpenApi: It provides a standardised way of documenting APIs, including routes, parameter optionality and format, security models and JSON Schema breakdown of JSON messages. It has standardised support from cloud providers such as Google Cloud Endpoints and AWS API Gateway . The OpenApi UI allows a very simple and developer-focused way of exploring and interacting with HTTP services from a browser environment. It is cross-platform and has good tooling support. Using OpenApi Generators , a specification document can be used to generate HTTP server stubs and working HTTP clients in a variety of languages, thus reducing integration efforts. Typesafe HTTP contracts with http4k-contract \u00b6 http4k has supported generating version 2 of OpenApi docs since all the way back in 2017 (v1.16) via it's http4k-contract module, and after a couple of releases ironing out the niggles (and some amazing help from the community), the team is now happy to announce OpenApi3 support with the release of http4k version 3.179.0. In line with the overall ethos of the project , http4k OpenApi support is done entirely through code and in a typesafe and refactorable way. This is somewhat of a departure from how most other libraries have implemented OpenApi (where often annotations and other compile-time magic are used) and means that in http4k the spec defined in code is the same one that is used to generate the API documentation and the same one used to validate incoming HTTP messages, meaning that it can never go stale. This focus on runtime code also allows for dynamic behaviours which would be very difficult to replicate at compile-time. Out of the box, http4k-contract the module now provides the following features when configured for OpenApi3: Automatic generation of route documentation in OpenApi v3 format, including the JSON Schema models for example incoming and outgoing messages (which arguably provide at least 50% of the value of using OpenApi). Complete auto-validation of the defined HTTP contract through the typesafe http4k Lens mechanism - violations are automatically detected and a BAD_REQUEST returned to the caller. This means that zero custom validation code is required to clutter up your routing layer and you can concentrate on working with meaningful domain types instead of primitives. Support/implementation of all defined OpenApi security models at both a global and per-route scope - BearerToken, ApiKey, OAuth and BasicAuth, although you can of course define and use custom implementations. Simple API for defining custom OpenApi extensions to extend the outputted specification document, for example using http4k in with AWS API Gateway or Google Cloud Endpoints So, how does we do all this using the http4k API? Let's find out with a worked example. 1. Your first endpoint \u00b6 After importing the http4k-core and http4k-contract dependencies into your project, we can write a new endpoint aka ContractRoute . The first thing to note is that we will be using a slightly different routing DSL the standard http4k one, one which provides a richer way to document endpoints - but don't worry - at it's core it utilises the same simple http4k building blocks of HttpHandler and Filter , as well as leveraging the http4k Lens API to automatically extract and convert incoming parameters into richer domain types. As ever, routes can (and should) be written and testing independently, which aids code decomposition and reuse. In this simple example, we're going to use a path with two dynamic parameters; name - a String, and the Integer age - which will be extracted and \"mapped\" into the constructor of a simple validated domain wrapper type. If the basic format of the path or the values for these path parameters cannot be extracted correctly, the endpoint fails to match and is skipped - this allows for several different variations of the same URI path signature to co-exist. Once the values have been extracted, they are passed as arguments to a function which will return a pre-configured HttpHandler for that call: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.div import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.int data class Age ( val value : Int ) { init { require ( value >= 0 ) } } fun basicHandler ( name : String , age : Age ): HttpHandler = { req : Request -> val beverage = if ( age . value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } val basicRoute : ContractRoute = \"/greet\" / Path . of ( \"name\" ) / Path . int (). map ( :: Age ). of ( \"age\" ) bindContract GET to :: basicHandler And here's a unit test for that endpoint - the good news is that it's no more complex than a standard http4k unit test because ContractRoute is also an HttpHandler so can just be invoked as a function. Here, we're also leveraging the http4k-testing-hamkrest module to supply Hamkrest Matchers for validating the response message: package blog.documenting_apis_with_openapi import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.hamkrest.hasBody import org.junit.jupiter.api.Test class BasicGreetingRouteTest { @Test fun `greets an adult` () { assertThat ( basicRoute ( Request ( GET , \"/greet/Bob/21\" )), hasBody ( \"Hello Bob, would you like some beer?\" ) ) } } 2. Defining an HTTP contract \u00b6 Now that we've got our endpoint, we want to be able to actually serve it with the OpenApi documentation. For contract-based routing, we use the contract {} routing DSL which allows us to specify a richer set of details about the API definition, but exposes exactly the same API semantics as the standard routes() block - it is also an HttpHandler and can therefore be composed together to form standard route-matching trees. For rendering the API documentation, we configure an OpenApi object, supplying a standard http4k JSON adapter instance - the recommended one to use is Jackson from the http4k-format-jackson module, so we'll need to import that module into our project as well. Whilst all of the settings used in this DSL above are optional (and default to sensible values if not overridden), here we are updating the URL where the OpenApi spec is served and supplying an instance of Security that we will use to protect our routes (more about that later). package blog.documenting_apis_with_openapi import org.http4k.contract.contract import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.BasicAuthSecurity import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.format.Jackson import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val http : HttpHandler = contract { renderer = OpenApi3 ( ApiInfo ( \"my secure api\" , \"v1.0\" , \"API description\" ), Jackson ) descriptionPath = \"/reference/api/swagger.json\" security = BasicAuthSecurity ( \"realm\" , Credentials ( \"user\" , \"password\" )) routes += basicRoute } http . asServer ( Undertow ( 9000 )). start () } Now we've got a complete contract, we can simply start the server and browse to http://localhost:9000/api/swagger.json to see the basic API spec in the OpenApi UI (or see the online version here ) to see how the endpoint contract looks and how the process of supplying credentials is done through the UI by clicking Authorize . This covers the very basics of generating API docs, but there is still a lot more http4k can do for us... 3. Auto-validating incoming HTTP messages \u00b6 For a better standard of API docs, we should add more details to the endpoint definition. The OpenAPI spec allows us to add this detail, but this normally comes with a maintenance cost - especially when the documentation is static or disparate from the location of the actual code serving requests, and we want to minimise the risk of stale documentation. In http4k, the extended contract metadata is kept close to the endpoint code and mostly type-checked by the compiler, so this threat is minimised as far as practical. Metadata for endpoints can be supplied via inserting a meta {} DSL block, which contains a mixture of 2 main types of property: Informational properties - such as summary , description and tags simply improve the experience of the user of the UI. Contractual properties define parameters using the http4k Lens API (in the same way as we used for the path) for the Query , Header or Body parts of the request. Once added to the contract, these items will also be auto-validated for form and presence before the contract HttpHandler is invoked, thus eliminating the need for any custom validation code to be written. We can then use the same lenses to confidently extract those values inside our HttpHandler code. Let's demonstrate by writing a slightly different version of the same endpoint, but move age to be a required query parameter, and also add the option to override the drink we offer: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int data class Drink ( val name : String ) { init { require ( name . isNotEmpty ()) } } fun Greetings (): ContractRoute { val age = Query . int (). map ( :: Age ). required ( \"age\" , \"Your age\" ) val favouriteDrink = Query . map ( :: Drink ). optional ( \"drink\" , \"Your favourite beverage\" ) fun handler ( name : String ): HttpHandler = { req : Request -> val drinkToOffer : Drink? = favouriteDrink ( req ) val beverage : String = drinkToOffer ?. name ?: if ( age ( req ). value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } return \"/greet\" / Path . of ( \"name\" , \"Your name\" ) meta { summary = \"Send greetings\" description = \"Greets the stupid human by offering them a beverage suitable for their age\" tags += Tag ( \"query\" ) queries += favouriteDrink queries += age produces += TEXT_PLAIN returning ( OK to \"A successful offer of a drink to the lowly meatbag.\" ) } bindContract GET to :: handler } If we then add the Greetings endpoint to the contract and make a call omitting age ... http://localhost:9000/greet/Bob?drink=cola ... the contract validation will fail and a HTTP Bad Request (400) returned to the client with a JSON body describing the error: HTTP / 1.1 400 Bad Request content - type : application / json ; charset = utf - 8 { \"message\" : \"Missing/invalid parameters\" , \"params\" : [ { \"name\" : \"age\" , \"type\" : \"query\" , \"datatype\" : \"integer\" , \"required\" : true , \"reason\" : \"Missing\" } ] } We can see the updated OpenApi UI here . Note that because request parameters are validated before sending, we cannot replicate the above invalid request in the UI. 4. Modelling HTTP body messages \u00b6 The most exciting part http4k supporting OpenApi3 is the ability to represent HTTP messages in JSON Schema form in the documentation. This facility is what unlocks the true cross-language support and takes the usefulness of the OpenApi UI to another level, for both exploratory and support functions. Request and response messages can both be specified in the meta {} block using overloads of the receiving() and returning() functions. By using these functions, we can supply an example object to the DSL - this is what drives the generation of the JSON Schema and, more importantly, ensures that the documentation cannot go stale as it is driven by code. Lets add another route to the mix which returns a JSON body object modelled with a Kotlin Data class and once again using the http4k Lens API . Here, the lens not only provides the validating (de)serialisation mechanism, but also activates the Content-Type header injection and parsing behaviour - this will ensure that all incoming and outgoing messages have the correct headers. For JSON bodies, the lens is created with Body.auto<>().toLens() ( auto() is an extension function imported from Jackson ) which provides the typed injection and extraction functions. Notice here that for injection we are using the more fluent API with() and of() extension functions, as opposed to the standard lens injection function (X, HttpMessage) -> HttpMessage : package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.lens.Path data class Person ( val name : String , val age : Age , val children : List < Person > = emptyList ()) fun Family (): ContractRoute { val familyData = Person ( \"Bob\" , Age ( 85 ), listOf ( Person ( \"Anita\" , Age ( 55 )), Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))) ) ) val responseLens = Body . auto < Person > ( \"The matched family tree\" ). toLens () fun handler ( queryName : String ): HttpHandler = { fun Person . search (): Person? = when ( name ) { queryName -> this else -> children . firstOrNull { it . search () != null } } familyData . search () ?. let { Response ( OK ). with ( responseLens of it ) } ?: Response ( NOT_FOUND ) } return \"/search\" / Path . of ( \"name\" , \"The name to search for in the tree\" ) meta { summary = \"Search family tree\" description = \"Given a name, returns a sub family tree starting with that person\" tags += Tag ( \"query\" ) returning ( OK , responseLens to Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))), \"Cut down family tree\" ) returning ( NOT_FOUND to \"That person does not exist the family\" ) } bindContract GET to :: handler } Taking a final look at the OpenApi UI here shows that not just has the UI been updated with the new route, but that example entries for the expected response are now displayed, as well as JSON Schema entries for the Person and Age classes in the Schemas section at the bottom. Wrapping up... \u00b6 Once we have the final specification document available, users of our API can use the various OpenApi Generators to generate HTTP clients in various languages for interacting with it, or to generate fake services that provide our API in their own environments (and thus enabling more simple end-to-end testing). The \"Fake HTTP services\" technique also enables the creation of Consumer-Driven-Contract style tests, and opens up possibilities for all kinds of interesting Chaos/failure-mode testing (you can even use the http4k-testing-chaos module to help with this \ud83d\ude09). The full source for this tutorial can be found here , or for a sense of how this all looks in when mixed into a complete http4k project, check out the http4k-by-example repo, which contains an entire TDD'd project showcasing a multitude of http4k features and testing styles.","title":"Documenting http4k apps with OpenApi3"},{"location":"blog/documenting_apis_with_openapi/#documenting_http4k_apis_with_openapi3","text":"","title":"Documenting http4k APIs with OpenApi3"},{"location":"blog/documenting_apis_with_openapi/#may_2019_daviddenton","text":"This post describes http4k support for fully describing and securing HTTP endpoints using version 3 of the OpenApi specification, providing typesafe JSON-schema documentation for messages and automatically validating incoming HTTP traffic.","title":"may 2019 / @daviddenton"},{"location":"blog/documenting_apis_with_openapi/#about_openapi","text":"In microservice environments, some of the biggest challenges exist around the communications between processes that simply aren't present when you're doing monolith-based development. This manifests in many different operational ways such as monitoring, discovery and fault tolerance, but one of the key aspects is communicating the the HTTP contract provided by a particular service. There have been various efforts to standardise these aspects, and one of the most popular is OpenApi , which grew out of the original Swagger project. There are 3 key advantages to OpenApi: It provides a standardised way of documenting APIs, including routes, parameter optionality and format, security models and JSON Schema breakdown of JSON messages. It has standardised support from cloud providers such as Google Cloud Endpoints and AWS API Gateway . The OpenApi UI allows a very simple and developer-focused way of exploring and interacting with HTTP services from a browser environment. It is cross-platform and has good tooling support. Using OpenApi Generators , a specification document can be used to generate HTTP server stubs and working HTTP clients in a variety of languages, thus reducing integration efforts.","title":"About OpenApi"},{"location":"blog/documenting_apis_with_openapi/#typesafe_http_contracts_with_http4k-contract","text":"http4k has supported generating version 2 of OpenApi docs since all the way back in 2017 (v1.16) via it's http4k-contract module, and after a couple of releases ironing out the niggles (and some amazing help from the community), the team is now happy to announce OpenApi3 support with the release of http4k version 3.179.0. In line with the overall ethos of the project , http4k OpenApi support is done entirely through code and in a typesafe and refactorable way. This is somewhat of a departure from how most other libraries have implemented OpenApi (where often annotations and other compile-time magic are used) and means that in http4k the spec defined in code is the same one that is used to generate the API documentation and the same one used to validate incoming HTTP messages, meaning that it can never go stale. This focus on runtime code also allows for dynamic behaviours which would be very difficult to replicate at compile-time. Out of the box, http4k-contract the module now provides the following features when configured for OpenApi3: Automatic generation of route documentation in OpenApi v3 format, including the JSON Schema models for example incoming and outgoing messages (which arguably provide at least 50% of the value of using OpenApi). Complete auto-validation of the defined HTTP contract through the typesafe http4k Lens mechanism - violations are automatically detected and a BAD_REQUEST returned to the caller. This means that zero custom validation code is required to clutter up your routing layer and you can concentrate on working with meaningful domain types instead of primitives. Support/implementation of all defined OpenApi security models at both a global and per-route scope - BearerToken, ApiKey, OAuth and BasicAuth, although you can of course define and use custom implementations. Simple API for defining custom OpenApi extensions to extend the outputted specification document, for example using http4k in with AWS API Gateway or Google Cloud Endpoints So, how does we do all this using the http4k API? Let's find out with a worked example.","title":"Typesafe HTTP contracts with http4k-contract"},{"location":"blog/documenting_apis_with_openapi/#1_your_first_endpoint","text":"After importing the http4k-core and http4k-contract dependencies into your project, we can write a new endpoint aka ContractRoute . The first thing to note is that we will be using a slightly different routing DSL the standard http4k one, one which provides a richer way to document endpoints - but don't worry - at it's core it utilises the same simple http4k building blocks of HttpHandler and Filter , as well as leveraging the http4k Lens API to automatically extract and convert incoming parameters into richer domain types. As ever, routes can (and should) be written and testing independently, which aids code decomposition and reuse. In this simple example, we're going to use a path with two dynamic parameters; name - a String, and the Integer age - which will be extracted and \"mapped\" into the constructor of a simple validated domain wrapper type. If the basic format of the path or the values for these path parameters cannot be extracted correctly, the endpoint fails to match and is skipped - this allows for several different variations of the same URI path signature to co-exist. Once the values have been extracted, they are passed as arguments to a function which will return a pre-configured HttpHandler for that call: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.div import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.int data class Age ( val value : Int ) { init { require ( value >= 0 ) } } fun basicHandler ( name : String , age : Age ): HttpHandler = { req : Request -> val beverage = if ( age . value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } val basicRoute : ContractRoute = \"/greet\" / Path . of ( \"name\" ) / Path . int (). map ( :: Age ). of ( \"age\" ) bindContract GET to :: basicHandler And here's a unit test for that endpoint - the good news is that it's no more complex than a standard http4k unit test because ContractRoute is also an HttpHandler so can just be invoked as a function. Here, we're also leveraging the http4k-testing-hamkrest module to supply Hamkrest Matchers for validating the response message: package blog.documenting_apis_with_openapi import com.natpryce.hamkrest.assertion.assertThat import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.hamkrest.hasBody import org.junit.jupiter.api.Test class BasicGreetingRouteTest { @Test fun `greets an adult` () { assertThat ( basicRoute ( Request ( GET , \"/greet/Bob/21\" )), hasBody ( \"Hello Bob, would you like some beer?\" ) ) } }","title":"1. Your first endpoint"},{"location":"blog/documenting_apis_with_openapi/#2_defining_an_http_contract","text":"Now that we've got our endpoint, we want to be able to actually serve it with the OpenApi documentation. For contract-based routing, we use the contract {} routing DSL which allows us to specify a richer set of details about the API definition, but exposes exactly the same API semantics as the standard routes() block - it is also an HttpHandler and can therefore be composed together to form standard route-matching trees. For rendering the API documentation, we configure an OpenApi object, supplying a standard http4k JSON adapter instance - the recommended one to use is Jackson from the http4k-format-jackson module, so we'll need to import that module into our project as well. Whilst all of the settings used in this DSL above are optional (and default to sensible values if not overridden), here we are updating the URL where the OpenApi spec is served and supplying an instance of Security that we will use to protect our routes (more about that later). package blog.documenting_apis_with_openapi import org.http4k.contract.contract import org.http4k.contract.openapi.ApiInfo import org.http4k.contract.openapi.v3.OpenApi3 import org.http4k.contract.security.BasicAuthSecurity import org.http4k.core.Credentials import org.http4k.core.HttpHandler import org.http4k.format.Jackson import org.http4k.server.Undertow import org.http4k.server.asServer fun main () { val http : HttpHandler = contract { renderer = OpenApi3 ( ApiInfo ( \"my secure api\" , \"v1.0\" , \"API description\" ), Jackson ) descriptionPath = \"/reference/api/swagger.json\" security = BasicAuthSecurity ( \"realm\" , Credentials ( \"user\" , \"password\" )) routes += basicRoute } http . asServer ( Undertow ( 9000 )). start () } Now we've got a complete contract, we can simply start the server and browse to http://localhost:9000/api/swagger.json to see the basic API spec in the OpenApi UI (or see the online version here ) to see how the endpoint contract looks and how the process of supplying credentials is done through the UI by clicking Authorize . This covers the very basics of generating API docs, but there is still a lot more http4k can do for us...","title":"2. Defining an HTTP contract"},{"location":"blog/documenting_apis_with_openapi/#3_auto-validating_incoming_http_messages","text":"For a better standard of API docs, we should add more details to the endpoint definition. The OpenAPI spec allows us to add this detail, but this normally comes with a maintenance cost - especially when the documentation is static or disparate from the location of the actual code serving requests, and we want to minimise the risk of stale documentation. In http4k, the extended contract metadata is kept close to the endpoint code and mostly type-checked by the compiler, so this threat is minimised as far as practical. Metadata for endpoints can be supplied via inserting a meta {} DSL block, which contains a mixture of 2 main types of property: Informational properties - such as summary , description and tags simply improve the experience of the user of the UI. Contractual properties define parameters using the http4k Lens API (in the same way as we used for the path) for the Query , Header or Body parts of the request. Once added to the contract, these items will also be auto-validated for form and presence before the contract HttpHandler is invoked, thus eliminating the need for any custom validation code to be written. We can then use the same lenses to confidently extract those values inside our HttpHandler code. Let's demonstrate by writing a slightly different version of the same endpoint, but move age to be a required query parameter, and also add the option to override the drink we offer: package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.ContentType.Companion.TEXT_PLAIN import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.lens.Path import org.http4k.lens.Query import org.http4k.lens.int data class Drink ( val name : String ) { init { require ( name . isNotEmpty ()) } } fun Greetings (): ContractRoute { val age = Query . int (). map ( :: Age ). required ( \"age\" , \"Your age\" ) val favouriteDrink = Query . map ( :: Drink ). optional ( \"drink\" , \"Your favourite beverage\" ) fun handler ( name : String ): HttpHandler = { req : Request -> val drinkToOffer : Drink? = favouriteDrink ( req ) val beverage : String = drinkToOffer ?. name ?: if ( age ( req ). value >= 18 ) \"beer\" else \"lemonade\" Response ( OK ). body ( \"Hello $ name , would you like some $ beverage ?\" ) } return \"/greet\" / Path . of ( \"name\" , \"Your name\" ) meta { summary = \"Send greetings\" description = \"Greets the stupid human by offering them a beverage suitable for their age\" tags += Tag ( \"query\" ) queries += favouriteDrink queries += age produces += TEXT_PLAIN returning ( OK to \"A successful offer of a drink to the lowly meatbag.\" ) } bindContract GET to :: handler } If we then add the Greetings endpoint to the contract and make a call omitting age ... http://localhost:9000/greet/Bob?drink=cola ... the contract validation will fail and a HTTP Bad Request (400) returned to the client with a JSON body describing the error: HTTP / 1.1 400 Bad Request content - type : application / json ; charset = utf - 8 { \"message\" : \"Missing/invalid parameters\" , \"params\" : [ { \"name\" : \"age\" , \"type\" : \"query\" , \"datatype\" : \"integer\" , \"required\" : true , \"reason\" : \"Missing\" } ] } We can see the updated OpenApi UI here . Note that because request parameters are validated before sending, we cannot replicate the above invalid request in the UI.","title":"3. Auto-validating incoming HTTP messages"},{"location":"blog/documenting_apis_with_openapi/#4_modelling_http_body_messages","text":"The most exciting part http4k supporting OpenApi3 is the ability to represent HTTP messages in JSON Schema form in the documentation. This facility is what unlocks the true cross-language support and takes the usefulness of the OpenApi UI to another level, for both exploratory and support functions. Request and response messages can both be specified in the meta {} block using overloads of the receiving() and returning() functions. By using these functions, we can supply an example object to the DSL - this is what drives the generation of the JSON Schema and, more importantly, ensures that the documentation cannot go stale as it is driven by code. Lets add another route to the mix which returns a JSON body object modelled with a Kotlin Data class and once again using the http4k Lens API . Here, the lens not only provides the validating (de)serialisation mechanism, but also activates the Content-Type header injection and parsing behaviour - this will ensure that all incoming and outgoing messages have the correct headers. For JSON bodies, the lens is created with Body.auto<>().toLens() ( auto() is an extension function imported from Jackson ) which provides the typed injection and extraction functions. Notice here that for injection we are using the more fluent API with() and of() extension functions, as opposed to the standard lens injection function (X, HttpMessage) -> HttpMessage : package blog.documenting_apis_with_openapi import org.http4k.contract.ContractRoute import org.http4k.contract.Tag import org.http4k.contract.div import org.http4k.contract.meta import org.http4k.core.Body import org.http4k.core.HttpHandler import org.http4k.core.Method.GET import org.http4k.core.Response import org.http4k.core.Status.Companion.NOT_FOUND import org.http4k.core.Status.Companion.OK import org.http4k.core.with import org.http4k.format.Jackson.auto import org.http4k.lens.Path data class Person ( val name : String , val age : Age , val children : List < Person > = emptyList ()) fun Family (): ContractRoute { val familyData = Person ( \"Bob\" , Age ( 85 ), listOf ( Person ( \"Anita\" , Age ( 55 )), Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))) ) ) val responseLens = Body . auto < Person > ( \"The matched family tree\" ). toLens () fun handler ( queryName : String ): HttpHandler = { fun Person . search (): Person? = when ( name ) { queryName -> this else -> children . firstOrNull { it . search () != null } } familyData . search () ?. let { Response ( OK ). with ( responseLens of it ) } ?: Response ( NOT_FOUND ) } return \"/search\" / Path . of ( \"name\" , \"The name to search for in the tree\" ) meta { summary = \"Search family tree\" description = \"Given a name, returns a sub family tree starting with that person\" tags += Tag ( \"query\" ) returning ( OK , responseLens to Person ( \"Donald\" , Age ( 52 ), listOf ( Person ( \"Don Jr\" , Age ( 21 )))), \"Cut down family tree\" ) returning ( NOT_FOUND to \"That person does not exist the family\" ) } bindContract GET to :: handler } Taking a final look at the OpenApi UI here shows that not just has the UI been updated with the new route, but that example entries for the expected response are now displayed, as well as JSON Schema entries for the Person and Age classes in the Schemas section at the bottom.","title":"4. Modelling HTTP body messages"},{"location":"blog/documenting_apis_with_openapi/#wrapping_up","text":"Once we have the final specification document available, users of our API can use the various OpenApi Generators to generate HTTP clients in various languages for interacting with it, or to generate fake services that provide our API in their own environments (and thus enabling more simple end-to-end testing). The \"Fake HTTP services\" technique also enables the creation of Consumer-Driven-Contract style tests, and opens up possibilities for all kinds of interesting Chaos/failure-mode testing (you can even use the http4k-testing-chaos module to help with this \ud83d\ude09). The full source for this tutorial can be found here , or for a sense of how this all looks in when mixed into a complete http4k project, check out the http4k-by-example repo, which contains an entire TDD'd project showcasing a multitude of http4k features and testing styles.","title":"Wrapping up..."},{"location":"blog/http4k_v4/","text":"http4k v4: 17 platforms and counting... \u00b6 january 2021 / the http4k team \u00b6 Well, at last it's here - after 3 years - http4k v4! Following on from the retrospective that we did on version 3, we've been busy polishing, tidying up the edges, and pushing out a bunch of changes to make the project sparkle. Ready? Then let's dive into the good stuff that's been going on at http4k Towers. Four digits good, three digits bad. The new http4k versioning scheme \u00b6 Ah yes - versioning - everyone's favourite topic. Part of the reason that http4k v3 has been around so long is that we've somewhat been abusing the Semantic versioning system, something which we've been unhappy with. Here's how it should work: For Version .. A = We broke something on purpose. (Breaking API change) B = Profit. (Feature / Improvement) C = We broke something by accident. (Bug) Up until now, both breaking and non-breaking API changes on v3 have been done through the second (B) digit of the version - which doesn't allow API users to know if they are expecting a break. At the same time, we wanted to keep major (A) version changes for when there's a big \"marketing\" release. To get around this, we are introducing a new versioning scheme based on 4 digits: For Version ... A = There's something we'd like the world to know. (Major change / Marketing) B = We broke something on purpose. (Breaking API change) C = Profit. (Feature / Improvement) D = We broke something by accident. (Bug) As you can see, for our users we'll be concentrating on changes in numbers A (occasional) and C (standard). \"Platforms, Guv! Thousands of 'em!\" (well, more than a few...) \u00b6 When http4k v3 was released, we only supported 3 JVM Server backends and 1 Serverless platform. Since then, we've added a bunch, and are now up to a very respectable 17 standard deployment options for http4k apps... 10 JVM Backends - Apache 4 & 5, Jetty, Ktor CIO & Netty, Netty, Ratpack, SunHttp and Undertow (+ any Servlet container) 6 Serverless platforms - Alibaba, AWS Lamba, Azure, Google Cloud, OpenWhisk (IBM/Adobe/Nimbella/Cloudstation), Tencent 1 Native platform - GraalVM (+ Quarkus) Switching between all platforms is super easy - just plug the standard HttpHandler into the the relevant http4k module class with a single line of code. Serverless modules all require just one more line, plus configuring the Serverless platform to call the relevant function. Here's examples for both: val app : HttpHandler = { req : Request -> Response ( OK ). body ( \"hello world!\" ) } val jvmApp = app . asServer ( Netty ( 8080 )). start () class MyServerlessFunction : GoogleCloudFunction ( app ) The even better news is that testing your http4k apps locally (regardless of platform) is simple - and as ever there's no magic involved - just test them entirely in-memory, or bind them to a standard backend Server. http4k Toolbox: your new Swiss Army Knife \u00b6 As documented in the Toolbox announcement post , we've been busy consolidating a bunch of handy tools for generating code to work with http4k projects, and we christened this the http4k Toolbox and it's available in both online and a CLI flavours (available from Brew and SDKMan!). From Project Generation to our own more sophisticated OpenAPI3 Generator , we hope that this become an essential tool in every http4k developer's pocket. Infinirouting \u00b6 The v3 routing scheme was pretty good as you could bind routes on static or dynamic paths and HTTP verbs, but being rampant power seekers, we wanted it to be better. We reasoned that if we could route traffic to HttpHandlers based on those things, then why not be able to route on any part of the request? We'd like to be able to do complicated matching - so for instance: \"Match the /name path, but only when the host header is http4k.org . Then add 2 submatches, one where there is a query parameter named queryName , the other where the body is > 50 bytes long.\" val app = routes ( \"/{name}\" bind POST to ( header ( \"host\" ) { it == \"http4k.org\" } bind routes ( queries ( \"queryName\" ) bind { Response ( OK ). body ( \"i had a query\" ) }, body { body : String -> body . length > 50 } bind { Response ( OK ). body ( \"I was long\" ) } )) ) So after a lot of clattering and banging of heads, we cracked it - and in doing so managed to rewrite the entire of the http4k routing layer in terms of these predicate Routers . It's really neat, infinitely(ish) nestable, and makes us feel just a little bit smug for getting it working. Graph power \u00b6 Traditionally, http4k has concentrated on providing routing for REST-style APIs. However, there is this new thing called GraphQL that has suddenly sprung up overnight and seems quite popular. Not wanting our users to miss out on anything, we've added support for simply integrating http4k with the official Java implementation of the library, GraphQL-Java . This module allows you to both serve and consume GQL APIs, and as per tradition allows you to test your APIs entirely in-memory making for super-fast test suites. OpenTelemetry: Monitor all the things! \u00b6 The OpenTelemetry project describes itself as... \"... a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior.\" - OpenTelemetry.io It's a great project run by the CNCF and very well fits in with the ethos that the http4k team believes in. As with all http4k integration modules, we want to enable http4k developers to be able to plug in their apps as simply as possible - in this case, just configure the OpenTelemetry API or Java-agent, then just add some simple Filters to your code to start collecting Distributed Traces or Metrics. Several tracing schemes are supported, including Amazon XRay, Jaeger and Zipkin. For more docs on how to get it all working, head over to the docs . Upgrading & library API changes \u00b6 Like the neat little worker bunnies we are, we've also taken the opportunity to clean up the http4k source code. All previously deprecated code has been removed, leaving the codebase nice and tidy. If you are upgrading, the best idea is to first upgrade to the last v3 version (v3.285.2), deal with any existing deprecations in place, then simply upgrade again to v4.0.0.0. http4k.org \u00b6 One of the things that our users feedback about was that the structure of the docs in http4k.org could be improved, so we've begun overhauling the site to simplify the content. Expect this to be a continual improvement thing, but on the whole the content will be organised as follows: Concepts will contain descriptions of the underlying concepts in and around the http4k libraries. Tutorials will be step-by-step guides to getting up and running for various use-cases. How-tos will contain extended examples of how to accomplish particular tasks. eg. provide a custom ServerConfig implementation. Code here will generally be complete and contain runnable examples. Module Reference will contain descriptions of the various features in the different http4k modules. Code in the guide will be snippet-based. Additionally, the Examples repo hosts fully self-contained, runnable projects that can be used as a baseline for particular features - eg. how to write and run an app on Quarkus or use the cloudnative module to enable typesafe configuration Support & training \u00b6 There has been a decent amount of interest lately from our users to come to us to ask for advice about how we can help teams get the best out of http4k. In that vein, we have also been busy building training materials which we can deliver to teams either new to (or experienced in) the library, or to visit teams (currently virtually) to help them out. If your team would also like to take advantage of our experience in delivering projects using http4k, then please visit the support/training page, reach out and we'd love to see how we can help. http4k Connect - Flyweight 3rd party adapters \u00b6 http4k-connect is the team's newest side project, the purpose of which is to eventually standardise patterns for building 3rd party system adapters to various backend services, and for building your own Fakes (backed by data-stores such as InMemory, S3 or Redis). So far (v2.8.0.0), http4k-connect supports at least the common-use case actions for the following systems (and the API is easily extendable for non-supplied actions): AWS KMS: Key Management Service AWS Lambda AWS S3: Simple Storage Service AWS Secrets Manager AWS SQS: Simple Queue Service AWS SSM: Systems Manager AWS STS: Security Token Service Google Analytics Mostly, the existence of the project is has been driven by 2 factors: to reduce dependency weight of bringing in SDK modules, especially when in a Serverless context. The AWS service SDKs are especially heavy for dependency weight. Using http4k-connect instead of official SDKs, overall Serverless Function distribution size should be reduced by at least an order of magnitude. to avoid us having to reinvent the same things again and again! (Because we're very very lazy developers!) It's pretty hot off the press, but will be receiving a lot of attention over the coming weeks and months, and we'll be documenting the mechanisms in both web and live talks. That's all folks... (for the moment) \u00b6 We're pretty excited about this release and hope the library will continue to provide powerful tools to make all of our existing (and new!) users' lives easier in creating kick ass and rock solid HTTP applications. In the meantime, if you are using http4k, please consider sponsoring the project to help offset the costs of development, documentation, and support. If you're using it commercially, we offer Commercial Support and Consulting to ensure you're getting maximum value from the toolkit and its related techniques. As ever, we'd love to hear how we're doing, so please drop into the comm channels to get in touch. Peace out. // the http4k team \u00b6","title":"http4k v4 - 17 platforms and counting..."},{"location":"blog/http4k_v4/#http4k_v4_17_platforms_and_counting","text":"","title":"http4k v4: 17 platforms and counting..."},{"location":"blog/http4k_v4/#january_2021_the_http4k_team","text":"Well, at last it's here - after 3 years - http4k v4! Following on from the retrospective that we did on version 3, we've been busy polishing, tidying up the edges, and pushing out a bunch of changes to make the project sparkle. Ready? Then let's dive into the good stuff that's been going on at http4k Towers.","title":"january 2021 / the http4k team"},{"location":"blog/http4k_v4/#four_digits_good_three_digits_bad_the_new_http4k_versioning_scheme","text":"Ah yes - versioning - everyone's favourite topic. Part of the reason that http4k v3 has been around so long is that we've somewhat been abusing the Semantic versioning system, something which we've been unhappy with. Here's how it should work: For Version .. A = We broke something on purpose. (Breaking API change) B = Profit. (Feature / Improvement) C = We broke something by accident. (Bug) Up until now, both breaking and non-breaking API changes on v3 have been done through the second (B) digit of the version - which doesn't allow API users to know if they are expecting a break. At the same time, we wanted to keep major (A) version changes for when there's a big \"marketing\" release. To get around this, we are introducing a new versioning scheme based on 4 digits: For Version ... A = There's something we'd like the world to know. (Major change / Marketing) B = We broke something on purpose. (Breaking API change) C = Profit. (Feature / Improvement) D = We broke something by accident. (Bug) As you can see, for our users we'll be concentrating on changes in numbers A (occasional) and C (standard).","title":"Four digits good, three digits bad. The new http4k versioning scheme"},{"location":"blog/http4k_v4/#platforms_guv_thousands_of_em_well_more_than_a_few","text":"When http4k v3 was released, we only supported 3 JVM Server backends and 1 Serverless platform. Since then, we've added a bunch, and are now up to a very respectable 17 standard deployment options for http4k apps... 10 JVM Backends - Apache 4 & 5, Jetty, Ktor CIO & Netty, Netty, Ratpack, SunHttp and Undertow (+ any Servlet container) 6 Serverless platforms - Alibaba, AWS Lamba, Azure, Google Cloud, OpenWhisk (IBM/Adobe/Nimbella/Cloudstation), Tencent 1 Native platform - GraalVM (+ Quarkus) Switching between all platforms is super easy - just plug the standard HttpHandler into the the relevant http4k module class with a single line of code. Serverless modules all require just one more line, plus configuring the Serverless platform to call the relevant function. Here's examples for both: val app : HttpHandler = { req : Request -> Response ( OK ). body ( \"hello world!\" ) } val jvmApp = app . asServer ( Netty ( 8080 )). start () class MyServerlessFunction : GoogleCloudFunction ( app ) The even better news is that testing your http4k apps locally (regardless of platform) is simple - and as ever there's no magic involved - just test them entirely in-memory, or bind them to a standard backend Server.","title":"\"Platforms, Guv! Thousands of 'em!\" (well, more than a few...)"},{"location":"blog/http4k_v4/#http4k_toolbox_your_new_swiss_army_knife","text":"As documented in the Toolbox announcement post , we've been busy consolidating a bunch of handy tools for generating code to work with http4k projects, and we christened this the http4k Toolbox and it's available in both online and a CLI flavours (available from Brew and SDKMan!). From Project Generation to our own more sophisticated OpenAPI3 Generator , we hope that this become an essential tool in every http4k developer's pocket.","title":"http4k Toolbox: your new Swiss Army Knife"},{"location":"blog/http4k_v4/#infinirouting","text":"The v3 routing scheme was pretty good as you could bind routes on static or dynamic paths and HTTP verbs, but being rampant power seekers, we wanted it to be better. We reasoned that if we could route traffic to HttpHandlers based on those things, then why not be able to route on any part of the request? We'd like to be able to do complicated matching - so for instance: \"Match the /name path, but only when the host header is http4k.org . Then add 2 submatches, one where there is a query parameter named queryName , the other where the body is > 50 bytes long.\" val app = routes ( \"/{name}\" bind POST to ( header ( \"host\" ) { it == \"http4k.org\" } bind routes ( queries ( \"queryName\" ) bind { Response ( OK ). body ( \"i had a query\" ) }, body { body : String -> body . length > 50 } bind { Response ( OK ). body ( \"I was long\" ) } )) ) So after a lot of clattering and banging of heads, we cracked it - and in doing so managed to rewrite the entire of the http4k routing layer in terms of these predicate Routers . It's really neat, infinitely(ish) nestable, and makes us feel just a little bit smug for getting it working.","title":"Infinirouting"},{"location":"blog/http4k_v4/#graph_power","text":"Traditionally, http4k has concentrated on providing routing for REST-style APIs. However, there is this new thing called GraphQL that has suddenly sprung up overnight and seems quite popular. Not wanting our users to miss out on anything, we've added support for simply integrating http4k with the official Java implementation of the library, GraphQL-Java . This module allows you to both serve and consume GQL APIs, and as per tradition allows you to test your APIs entirely in-memory making for super-fast test suites.","title":"Graph power"},{"location":"blog/http4k_v4/#opentelemetry_monitor_all_the_things","text":"The OpenTelemetry project describes itself as... \"... a collection of tools, APIs, and SDKs. You use it to instrument, generate, collect, and export telemetry data (metrics, logs, and traces) for analysis in order to understand your software's performance and behavior.\" - OpenTelemetry.io It's a great project run by the CNCF and very well fits in with the ethos that the http4k team believes in. As with all http4k integration modules, we want to enable http4k developers to be able to plug in their apps as simply as possible - in this case, just configure the OpenTelemetry API or Java-agent, then just add some simple Filters to your code to start collecting Distributed Traces or Metrics. Several tracing schemes are supported, including Amazon XRay, Jaeger and Zipkin. For more docs on how to get it all working, head over to the docs .","title":"OpenTelemetry: Monitor all the things!"},{"location":"blog/http4k_v4/#upgrading_library_api_changes","text":"Like the neat little worker bunnies we are, we've also taken the opportunity to clean up the http4k source code. All previously deprecated code has been removed, leaving the codebase nice and tidy. If you are upgrading, the best idea is to first upgrade to the last v3 version (v3.285.2), deal with any existing deprecations in place, then simply upgrade again to v4.0.0.0.","title":"Upgrading & library API changes"},{"location":"blog/http4k_v4/#http4korg","text":"One of the things that our users feedback about was that the structure of the docs in http4k.org could be improved, so we've begun overhauling the site to simplify the content. Expect this to be a continual improvement thing, but on the whole the content will be organised as follows: Concepts will contain descriptions of the underlying concepts in and around the http4k libraries. Tutorials will be step-by-step guides to getting up and running for various use-cases. How-tos will contain extended examples of how to accomplish particular tasks. eg. provide a custom ServerConfig implementation. Code here will generally be complete and contain runnable examples. Module Reference will contain descriptions of the various features in the different http4k modules. Code in the guide will be snippet-based. Additionally, the Examples repo hosts fully self-contained, runnable projects that can be used as a baseline for particular features - eg. how to write and run an app on Quarkus or use the cloudnative module to enable typesafe configuration","title":"http4k.org"},{"location":"blog/http4k_v4/#support_training","text":"There has been a decent amount of interest lately from our users to come to us to ask for advice about how we can help teams get the best out of http4k. In that vein, we have also been busy building training materials which we can deliver to teams either new to (or experienced in) the library, or to visit teams (currently virtually) to help them out. If your team would also like to take advantage of our experience in delivering projects using http4k, then please visit the support/training page, reach out and we'd love to see how we can help.","title":"Support & training"},{"location":"blog/http4k_v4/#http4k_connect_-_flyweight_3rd_party_adapters","text":"http4k-connect is the team's newest side project, the purpose of which is to eventually standardise patterns for building 3rd party system adapters to various backend services, and for building your own Fakes (backed by data-stores such as InMemory, S3 or Redis). So far (v2.8.0.0), http4k-connect supports at least the common-use case actions for the following systems (and the API is easily extendable for non-supplied actions): AWS KMS: Key Management Service AWS Lambda AWS S3: Simple Storage Service AWS Secrets Manager AWS SQS: Simple Queue Service AWS SSM: Systems Manager AWS STS: Security Token Service Google Analytics Mostly, the existence of the project is has been driven by 2 factors: to reduce dependency weight of bringing in SDK modules, especially when in a Serverless context. The AWS service SDKs are especially heavy for dependency weight. Using http4k-connect instead of official SDKs, overall Serverless Function distribution size should be reduced by at least an order of magnitude. to avoid us having to reinvent the same things again and again! (Because we're very very lazy developers!) It's pretty hot off the press, but will be receiving a lot of attention over the coming weeks and months, and we'll be documenting the mechanisms in both web and live talks.","title":"http4k Connect - Flyweight 3rd party adapters"},{"location":"blog/http4k_v4/#thats_all_folks_for_the_moment","text":"We're pretty excited about this release and hope the library will continue to provide powerful tools to make all of our existing (and new!) users' lives easier in creating kick ass and rock solid HTTP applications. In the meantime, if you are using http4k, please consider sponsoring the project to help offset the costs of development, documentation, and support. If you're using it commercially, we offer Commercial Support and Consulting to ensure you're getting maximum value from the toolkit and its related techniques. As ever, we'd love to hear how we're doing, so please drop into the comm channels to get in touch. Peace out.","title":"That's all folks... (for the moment)"},{"location":"blog/http4k_v4/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/http4k_v5/","text":"http4k Platform v5: New Servers, Loom, TracerBullet, OpenAI plugin SDK and more \u00b6 june 2023 / the http4k team \u00b6 We\u2019re thrilled to announce the next major release of http4k! Since the last major release, the team has been busy enhancing existing features, adding new capabilities with help from our amazing community, things that we've needed on our own real world projects, and a sprinkling of new magic to keep pushing the boundaries of what's possible with the http4k technology - all 127 modules of it. The first thing to tell you is that we've hit a significant milestone and are now getting about 1 million downloads per month from Maven Central. From these numbers and our interactions with the global http4k community, it makes us very happy that so many teams are seeing the power of the Server-as-a-Function model. Partnership programme \u00b6 Another thing to celebrate is that http4k now has it's first major corporate partner - the wonderful SpringerNature Technology , where http4k powers a sizeable percentage of their global content delivery platform. The http4k team thanks all the folks at SN Technology for their ongoing collaboration and support - undoubtedly the project would not be where it is now without them! If your organisation is using the http4k platform and would like to explore potential partnership options - including access to priority support channels, training opportunities, and input into the development plan, then please reach out and we'll be in touch to see how we can work together. With those things out of the way, let's get on to what you really came for - features! Introducing the http4k Platform: 127 modules and counting \u00b6 Before we dive into the details of the library updates themselves, we're announcing today that for the simplicity (to both the http4k community and the team!), we are unifying all http4k ecosystem projects (currently http4k and http4k-connect) under a single major platform version number - starting with v5. In addition, to keep pace with Java's own rapidly evolving ecosystem, we're going to be switching to aligning major releases of \"the Platform\" with every JDK release - currently scheduled for every 6 months. This will allow us to track major-version support as features are added and removed from Java and provide an easy way for our users to track the this compatibility. For the moment, http4k and http4k-connect will still have different release cadence and individual release numbers and which are now developed using Java 20 as a base, but in the future there are plans to consolidate the projects under a single platform version. In terms of Java compatibility - for the foreseeable future, we are still going to compile http4k for older Java versions (we still target Java 8 as a base). Over time though, we plan to adjust our standard of support for ancient versions and introduce a paid support program for those who still need to run http4k against legacy versions of Java and require updates such as security patches. If that\u2019s already the case for your team or project, be sure to get in touch with the http4k team to discuss your particular needs. Now - 127 is a pretty big number of modules, especially for what started out with a modest 43 line spike . But we're definitely not done yet - http4k is a solid foundation - it's arguably one of the simplest and most test-driven web libraries on the planet. We've also proven in http4k-connect that those parts can be composed into an powerful integration layer. Now the http4k team intend to continue that journey - and v5 is just the start. http4k Updates \u00b6 In v4, we shipped a massive 197 releases (an average of 1.5 per week!) and introduced 12 new modules in a variety of areas: deployment ( serverless-lambda-runtime ), wire formats (moshi-yaml, jackson-csv, kondor-json), security ( digest ), testing ( strikt ), and around simpler usages of OpenAPI (Redoc/SwaggerUI). http4k 5 introduces 5 new modules and tidies up the decks for future developments. http4k v5 is available right now from the Toolbox , where you can configure an download a fully compilable project with any combination of the 70 available http4k modules. Loom support \u00b6 We\u2019re excited to see the re-introduction of virtual threads in Java and the performance improvements that can bring to well-established servers. Loom goes gold in Java 21 and we're going to be ready for our users to use it straight away. For this new major version, we\u2019re introducing three new server backends taking advantage of Java Loom virtual threads: the SDK built-in SunHttp, Jetty , and Helidon . Servers are only half of the story though - http4k will also add support for Loom-friendy HTTP clients as they get released, and is introducing support for the Loom-native Helidon client in this release. We'll be sure to keep up-to-date with the other major HTTP client libraries as they update to support Loom. TracerBullet: a brand new way of getting test feedback and documenting your systems \u00b6 We're incredibly proud to be introducing TracerBullet , an innovative testing add-on that integrates with the http4k events system. TracerBullet enables teams to focus on how their services work, not just if they work. This powerful tool will change how you approach testing and help you gain deeper insights into your services. As a side-effect of introducing TracerBullet, http4k can automatically generate sequence and interaction diagrams , taking advantage of existing tools such as PlantUML , Mermaid , and d2 to create living documentation for your services after each test run! The http4k team were excited to be invited back to KotlinConf 2023 , where we presented how we used the TracerBullet along with Hexagonal Design to visually document multi-service tests. You can check out the video of the presentation here or see a full example of how it works in the demo repo . Remodelling of Websocket and SSE support \u00b6 Various http4k backends have supported SSE and Websockets for a while, but whilst the innovation was that you could unit-test them entirely in-memory (a world first?), the models did have some restrictions in handling request filtering. As such we have reworked the support to be more consistent with the HTTP handling. This is a breaking change, but it very important for us to be able to more fully support these protocols. Removal of deprecated and unsupported features \u00b6 As part of the major release cycle, we\u2019re removing all code marked as deprecated in v4 of http4k. We have also removed the http4k-templates-dust module due to the removal of Nashorn from the Java distribution. We understand this may impact some users, and we recommend seeking alternative solutions in the long term, but you can continue to use the existing http4k-template-dust assets which are still binary compatible with v5 of http4k. Upgrading from http4k v4 \u00b6 If you are upgrading, the best idea is to first upgrade to the last v4 version, deal with any existing deprecations in place, then simply upgrade again the latest v5 version and repeat. http4k-connect Updates \u00b6 http4k-connect was conceived as a library of providing client adapters for popular cloud services based on the innovative Connect pattern . v3 of the library added 20 different modules, and covered a number of popular AWS services, as well as integrating with Kafka via HTTP. The power of http4k-connect is it's ability to leverage Kotlin to provide modular adapters based around a common core. Each client endpoint is modeled as a separate \"Action\"? Is the action you want not supported by the core library, or do you want less information to be parsed? Simple - just write your own endpoint adapter and plug it in! Today with the v5 release, we're expanding the focus slightly - http4k-connect will now be the home of all official http4k platform integrations with third party services, to cover: Featherweight, zero-reflection adapters, for HTTP integrations with cloud services (eg. AWS, Confluent) Fake implementations of cloud services, which allow lightning-fast in-memory testing or local testing without Docker/LocalStack. A pluggable unified Storage interface with five different storage backends - including in-memory, Redis and S3, as well as a StorageExplorer that provides an OpenAPI UI to allow interaction with the storage via browser. Plugin SDKs for building integrations with popular platforms - starting with... OpenAI/ChatGPT support \u00b6 It's 2023, and the new hotness that everyone is talking about is AI - and who are we to go against the weight of the hypemachine? After playing with GPT-4, one of the most frustrating things we found was the slowness of the responses and the effect that had on our testing-cycle, so the http4k team started to build a client adapter and fake OpenAI server so that Kotlin developer teams could integrate and test with OpenAI APIs quickly without waiting ages for the model to respond, and burning through those precious GPT-4 tokens and API limits. And thus http4k-connect-openai was born. The Fake server even allows you to generate images. A little later, the team received an early access invite to the ChatGPT plugin programme and it immediately occured that the requirements for developing plugins were all already supported within existing http4k libraries. So we set out to develop a plugin SDK, and http4k-connect-openai-plugin was born. The SDK provides a simple API for developers to simply compose their plugins by supplying some config and a standard http4k-contract endpoints, which generate the required OpenAPI specifications. The SDK does the rest for you, providing the required OpenAI plugin manifest, and then protects the API with one of 4 security models - including the ability to \"login with ChatGPT\" and OAuth into your plugin. Once created, plugins can be installed into the FakeOpenAI server, and expose a standard http4k HttpHandler which means they can be tested in memory, run locally standalone, or composed into part of a larger http4k application. It's pretty neat and we can't wait to see what the community uses it for! There's a full example of how to build a plugin with the http4k-connect-openai-plugin SDK and deploy it to AWS Lambda with Pulumi - see the repo for details. Removal of deprecated and unsupported features \u00b6 As part of the major release cycle, we\u2019re removing all code marked as deprecated in v3 of http4k-connect. Upgrading from http4k-connect v3 \u00b6 If you are upgrading, the best idea is to first upgrade to the last v3 version, deal with any existing deprecations in place, then simply upgrade again to the latest v5 version and repeat. And that's it (for now!). We hope you're as excited about these updates to the http4k platform as we are! This release brings a wealth of new features and improvements, and we believe it will make your experience using http4k technologies even better. Cheers // the http4k team \u00b6","title":"http4k Platform v5 - New Servers, Loom, TracerBullet, OpenAI plugin SDK and more"},{"location":"blog/http4k_v5/#http4k_platform_v5_new_servers_loom_tracerbullet_openai_plugin_sdk_and_more","text":"","title":"http4k Platform v5: New Servers, Loom, TracerBullet, OpenAI plugin SDK and more"},{"location":"blog/http4k_v5/#june_2023_the_http4k_team","text":"We\u2019re thrilled to announce the next major release of http4k! Since the last major release, the team has been busy enhancing existing features, adding new capabilities with help from our amazing community, things that we've needed on our own real world projects, and a sprinkling of new magic to keep pushing the boundaries of what's possible with the http4k technology - all 127 modules of it. The first thing to tell you is that we've hit a significant milestone and are now getting about 1 million downloads per month from Maven Central. From these numbers and our interactions with the global http4k community, it makes us very happy that so many teams are seeing the power of the Server-as-a-Function model.","title":"june 2023 / the http4k team"},{"location":"blog/http4k_v5/#partnership_programme","text":"Another thing to celebrate is that http4k now has it's first major corporate partner - the wonderful SpringerNature Technology , where http4k powers a sizeable percentage of their global content delivery platform. The http4k team thanks all the folks at SN Technology for their ongoing collaboration and support - undoubtedly the project would not be where it is now without them! If your organisation is using the http4k platform and would like to explore potential partnership options - including access to priority support channels, training opportunities, and input into the development plan, then please reach out and we'll be in touch to see how we can work together. With those things out of the way, let's get on to what you really came for - features!","title":"Partnership programme"},{"location":"blog/http4k_v5/#introducing_the_http4k_platform_127_modules_and_counting","text":"Before we dive into the details of the library updates themselves, we're announcing today that for the simplicity (to both the http4k community and the team!), we are unifying all http4k ecosystem projects (currently http4k and http4k-connect) under a single major platform version number - starting with v5. In addition, to keep pace with Java's own rapidly evolving ecosystem, we're going to be switching to aligning major releases of \"the Platform\" with every JDK release - currently scheduled for every 6 months. This will allow us to track major-version support as features are added and removed from Java and provide an easy way for our users to track the this compatibility. For the moment, http4k and http4k-connect will still have different release cadence and individual release numbers and which are now developed using Java 20 as a base, but in the future there are plans to consolidate the projects under a single platform version. In terms of Java compatibility - for the foreseeable future, we are still going to compile http4k for older Java versions (we still target Java 8 as a base). Over time though, we plan to adjust our standard of support for ancient versions and introduce a paid support program for those who still need to run http4k against legacy versions of Java and require updates such as security patches. If that\u2019s already the case for your team or project, be sure to get in touch with the http4k team to discuss your particular needs. Now - 127 is a pretty big number of modules, especially for what started out with a modest 43 line spike . But we're definitely not done yet - http4k is a solid foundation - it's arguably one of the simplest and most test-driven web libraries on the planet. We've also proven in http4k-connect that those parts can be composed into an powerful integration layer. Now the http4k team intend to continue that journey - and v5 is just the start.","title":"Introducing the http4k Platform: 127 modules and counting"},{"location":"blog/http4k_v5/#http4k_updates","text":"In v4, we shipped a massive 197 releases (an average of 1.5 per week!) and introduced 12 new modules in a variety of areas: deployment ( serverless-lambda-runtime ), wire formats (moshi-yaml, jackson-csv, kondor-json), security ( digest ), testing ( strikt ), and around simpler usages of OpenAPI (Redoc/SwaggerUI). http4k 5 introduces 5 new modules and tidies up the decks for future developments. http4k v5 is available right now from the Toolbox , where you can configure an download a fully compilable project with any combination of the 70 available http4k modules.","title":"http4k Updates"},{"location":"blog/http4k_v5/#loom_support","text":"We\u2019re excited to see the re-introduction of virtual threads in Java and the performance improvements that can bring to well-established servers. Loom goes gold in Java 21 and we're going to be ready for our users to use it straight away. For this new major version, we\u2019re introducing three new server backends taking advantage of Java Loom virtual threads: the SDK built-in SunHttp, Jetty , and Helidon . Servers are only half of the story though - http4k will also add support for Loom-friendy HTTP clients as they get released, and is introducing support for the Loom-native Helidon client in this release. We'll be sure to keep up-to-date with the other major HTTP client libraries as they update to support Loom.","title":"Loom support"},{"location":"blog/http4k_v5/#tracerbullet_a_brand_new_way_of_getting_test_feedback_and_documenting_your_systems","text":"We're incredibly proud to be introducing TracerBullet , an innovative testing add-on that integrates with the http4k events system. TracerBullet enables teams to focus on how their services work, not just if they work. This powerful tool will change how you approach testing and help you gain deeper insights into your services. As a side-effect of introducing TracerBullet, http4k can automatically generate sequence and interaction diagrams , taking advantage of existing tools such as PlantUML , Mermaid , and d2 to create living documentation for your services after each test run! The http4k team were excited to be invited back to KotlinConf 2023 , where we presented how we used the TracerBullet along with Hexagonal Design to visually document multi-service tests. You can check out the video of the presentation here or see a full example of how it works in the demo repo .","title":"TracerBullet: a brand new way of getting test feedback and documenting your systems"},{"location":"blog/http4k_v5/#remodelling_of_websocket_and_sse_support","text":"Various http4k backends have supported SSE and Websockets for a while, but whilst the innovation was that you could unit-test them entirely in-memory (a world first?), the models did have some restrictions in handling request filtering. As such we have reworked the support to be more consistent with the HTTP handling. This is a breaking change, but it very important for us to be able to more fully support these protocols.","title":"Remodelling of Websocket and SSE support"},{"location":"blog/http4k_v5/#removal_of_deprecated_and_unsupported_features","text":"As part of the major release cycle, we\u2019re removing all code marked as deprecated in v4 of http4k. We have also removed the http4k-templates-dust module due to the removal of Nashorn from the Java distribution. We understand this may impact some users, and we recommend seeking alternative solutions in the long term, but you can continue to use the existing http4k-template-dust assets which are still binary compatible with v5 of http4k.","title":"Removal of deprecated and unsupported features"},{"location":"blog/http4k_v5/#upgrading_from_http4k_v4","text":"If you are upgrading, the best idea is to first upgrade to the last v4 version, deal with any existing deprecations in place, then simply upgrade again the latest v5 version and repeat.","title":"Upgrading from http4k v4"},{"location":"blog/http4k_v5/#http4k-connect_updates","text":"http4k-connect was conceived as a library of providing client adapters for popular cloud services based on the innovative Connect pattern . v3 of the library added 20 different modules, and covered a number of popular AWS services, as well as integrating with Kafka via HTTP. The power of http4k-connect is it's ability to leverage Kotlin to provide modular adapters based around a common core. Each client endpoint is modeled as a separate \"Action\"? Is the action you want not supported by the core library, or do you want less information to be parsed? Simple - just write your own endpoint adapter and plug it in! Today with the v5 release, we're expanding the focus slightly - http4k-connect will now be the home of all official http4k platform integrations with third party services, to cover: Featherweight, zero-reflection adapters, for HTTP integrations with cloud services (eg. AWS, Confluent) Fake implementations of cloud services, which allow lightning-fast in-memory testing or local testing without Docker/LocalStack. A pluggable unified Storage interface with five different storage backends - including in-memory, Redis and S3, as well as a StorageExplorer that provides an OpenAPI UI to allow interaction with the storage via browser. Plugin SDKs for building integrations with popular platforms - starting with...","title":"http4k-connect Updates"},{"location":"blog/http4k_v5/#openaichatgpt_support","text":"It's 2023, and the new hotness that everyone is talking about is AI - and who are we to go against the weight of the hypemachine? After playing with GPT-4, one of the most frustrating things we found was the slowness of the responses and the effect that had on our testing-cycle, so the http4k team started to build a client adapter and fake OpenAI server so that Kotlin developer teams could integrate and test with OpenAI APIs quickly without waiting ages for the model to respond, and burning through those precious GPT-4 tokens and API limits. And thus http4k-connect-openai was born. The Fake server even allows you to generate images. A little later, the team received an early access invite to the ChatGPT plugin programme and it immediately occured that the requirements for developing plugins were all already supported within existing http4k libraries. So we set out to develop a plugin SDK, and http4k-connect-openai-plugin was born. The SDK provides a simple API for developers to simply compose their plugins by supplying some config and a standard http4k-contract endpoints, which generate the required OpenAPI specifications. The SDK does the rest for you, providing the required OpenAI plugin manifest, and then protects the API with one of 4 security models - including the ability to \"login with ChatGPT\" and OAuth into your plugin. Once created, plugins can be installed into the FakeOpenAI server, and expose a standard http4k HttpHandler which means they can be tested in memory, run locally standalone, or composed into part of a larger http4k application. It's pretty neat and we can't wait to see what the community uses it for! There's a full example of how to build a plugin with the http4k-connect-openai-plugin SDK and deploy it to AWS Lambda with Pulumi - see the repo for details.","title":"OpenAI/ChatGPT support"},{"location":"blog/http4k_v5/#removal_of_deprecated_and_unsupported_features_1","text":"As part of the major release cycle, we\u2019re removing all code marked as deprecated in v3 of http4k-connect.","title":"Removal of deprecated and unsupported features"},{"location":"blog/http4k_v5/#upgrading_from_http4k-connect_v3","text":"If you are upgrading, the best idea is to first upgrade to the last v3 version, deal with any existing deprecations in place, then simply upgrade again to the latest v5 version and repeat. And that's it (for now!). We hope you're as excited about these updates to the http4k platform as we are! This release brings a wealth of new features and improvements, and we believe it will make your experience using http4k technologies even better. Cheers","title":"Upgrading from http4k-connect v3"},{"location":"blog/http4k_v5/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/meet_http4k/","text":"Server as a Function. In Kotlin. Typesafe. Without the Server. \u00b6 november 2017 / @daviddenton \u00b6 Meet http4k \u00b6 http4k is an HTTP toolkit written in Kotlin that enables the serving and consuming of HTTP services in a functional and consistent way. Whenever (yet another) new JVM HTTP framework is released, the inevitable question that rightly get asked is \"How it this different to X?\" . In this post, I'm going to briefly cover what http4k is, how we think it's different, and address some of those bold claims from the title of this post. Here's a quick rundown of what we think those differences are: http4k is small. Written in pure, functional Kotlin, with zero dependencies. http4k is simple. Like, really simple. No static API magic, no annotations, no reflection. http4k is immutable. It relies on an immutable HTTP model, which makes it a snap to test and debug. http4k is symmetric. It supports remote calls as a first-class concern, and the remote HTTP model is identical to the incoming HTTP model. http4k is typesafe. Say goodbye to all your validation and marshalling boilerplate and hello to automatic request validation and data class-based contracts for HTTP bodies using the Lens API. http4k is serverless. Or rather - server independent. Test an app out of container and then deploy it into any supported local container with 1 LOC - or as a function into AWS Lambda. Oh god, not another framework! Why does this even exist?!? \u00b6 Firstly - we don't consider http4k to be a framework - it's a set of libraries providing a functional toolkit to serve and consume HTTP services, focusing on simple, consistent, and testable APIs. Hence, whilst it does provide support for various APIs relevant to serving and consuming HTTP , it does not provide every integration under the sun - merely simple points to allow those integrations to be hooked in. Another thing to say is that (not very much) of http4k is new - it's rather the distillation of 15 years worth of experience of using various server-side libraries and hence most of the good ideas are stolen. For instance - the routing module is inspired by UtterlyIdle , the basic \"Server as a function\" model is stolen from Finagle , and the contract module OpenApi/Swagger generator is ported from Fintrospect . With the growing adoption of Kotlin, we wanted something that would fully leverage the functional features of the language and it felt like a good time to start something from scratch, whilst avoiding the magic that plagues other frameworks. Hence, http4k is primarily designed to be a Kotlin-first library. Claim A: Small, simple, immutable. \u00b6 Based on the awesome \"Your Server as a Function\" paper from Twitter, http4k apps are modelled by composing 2 types of simple, independent function. Function 1: HttpHandler \u00b6 An HttpHandler represents an HTTP endpoint. It's not even an Interface, modelled merely as a Typealias : typealias HttpHandler = ( Request ) -> Response Below is a entire http4k application that echoes the request body back in the response. It only relies on the http4k-core module, which itself has zero dependencies: val app = { request : Request -> Response ( OK ). body ( request . body ) } val server = app . asServer ( SunHttp ( 8000 )). start () The Request and Response objects in there are immutable data classes/POKOs, so testing the app requires absolutely no extra infrastructure - just call the function, it's as easy as: class AppTest { @Test fun `echoes request body` () { assertThat ( app ( Request ( POST , \"/\" ). body ( \"hello\" )), equalTo ( Response ( OK ). body ( \"hello\" ))) } } To plug it into a different Server-backend, just depend on the relevant module (Jetty, Undertow, Netty, Apache (httpcore), Ktor CIO, Ktor Netty, and SunHttp are available) and change the call to asServer() . Function 2: Filter \u00b6 Filters provides pre and post Request processing and are simply: interface Filter : ( HttpHandler ) -> HttpHandler For API conciseness and discoverability reasons this is modelled as an Interface and not a Typealias - it also has a couple of Kotlin extension methods to allow you to compose Filters with HttpHandlers and other Filters : val setContentType = Filter { next -> { request -> next ( request ). header ( \"Content-Type\" , \"text/plain\" ) } } val repeatBody = Filter { next -> { request -> next ( request . body ( request . bodyString () + request . bodyString ())) } } val composedFilter : Filter = repeatBody . then ( setContentType ) val decoratedApp : HttpHandler = composedFilter . then ( app ) Filters are also trivial to test independently, because they are generally just stateless functions. Routing \u00b6 http4k's nestable routing looks a lot like every other Sinatra-style framework these days, and allows for infinitely nesting HttpHandlers - this just exposes another HttpHandler so you can easily extract, test and reuse sets of routes as easily as you could with one: val app : HttpHandler = routes ( \"/app\" bind GET to decoratedApp , \"/other\" bind routes ( \"/delete\" bind DELETE to { _ : Request -> Response ( OK ) }, \"/post/{name}\" bind POST to { request : Request -> Response ( OK ). body ( \"you POSTed to ${ request . path ( \" name \" ) } \" ) } ) ) And that it - those functions are everything you need to know to write a simple http4k application. The http4k-core module rocks in at about 700kb, and has zero dependencies (other than the Kotlin language itself). Additionally, everything in the core is functional and predictable - there is no static API magic going on under the covers (making it difficult to have multiple apps in the same JVM), no annotations, no compiler-plugins, and no reflection. Claim B. Symmetric HTTP \u00b6 Out of the multitude of JVM http frameworks out there, not many actually consider how you app talks to other services, yet in this Microservice\u2122 world that's an absolutely massive part of what many apps do! As per a core principle behind \"Server as a Function\", http4k provides a symmetric API for HTTP clients - ie. it's exactly the same API as is exposed in http4k server applications - the HttpHandler . Here's that entire API again, just in case you've forgotten: typealias HttpHandler = ( Request ) -> Response What does that mean in practice? Well - for one thing, it's less for your brain to learn because you already know the API: val client : HttpHandler = ApacheClient () val response : Response = client ( Request ( GET , \"http://server/path\" )) For another, it means that since clients are just function s you can easily stub them for testing, and since applications and clients are interchangeable, they can be plugged together in memory without putting them on the network - which makes testing insanely fast: fun MyApp1 (): HttpHandler = { Response ( OK ) } fun MyApp2 ( app1 : HttpHandler ): HttpHandler = { app1 ( it ) } val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 ) http4k provides a HTTP client adapters for both Apache and OkHttp , all with streaming support. Claim C. Typesafe HTTP with Lenses \u00b6 The immutable http4k model for HTTP objects contains all the usual suspect methods for getting values from the messages. For instance, if we are expecting a search parameter with a query containing a page number: val request = Request ( GET , \"http://server/search?page=123\" ) val page : Int = request . query ( \"page\" ) !! . toInt ...but we also want to ensure that the expected values are both present and valid, since the above example will fail if either of those things is not true. For this purpose, we can use a Lens to enforce the expected HTTP contract. The use of Lenses in http4k applications can remove the need for writing any parsing or validation code for all incoming data (including Forms), as validations are taken care of by the library. Lens basics \u00b6 A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional) and the type. For the above example, we could use the Query Lens builder and then invoke() the Lens on the message to extract the target value: val pageLens = Query . int (). required ( \"page\" ) val page : Int = pageLens ( Request ( GET , \"http://server/search?page=123\" )) If the query parameter is missing or not an Int, the lens extraction operation will fail. There are similar Lens builder functions for all parts of the HTTP message ( Header , Path , Body , FormField etc..), and functions for all common JVM primitive types. They are all completely typesafe - there is no reflection or magic going on - just marshalling of the various entities (in this case String to Int conversion). In the case of failure, we need to apply a Filter to detect the errors and convert them to a BAD_REQUEST response: val queryName = Query . string (). required ( \"name\" ) val app : HttpHandler = routes ( \"/post\" bind POST to { request : Request -> Response ( OK ). body ( queryName ( request )) } ) val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" )) Lenses can also be applied with a correctly typed value (via invoke() ) to set it onto a target object - and as HTTP messages in http4k are immutable, this results in a copy of the modified message: val pageSizeLens = Header . int (). required ( \"page\" ) val page : Response = pageLens ( Response ( OK ), 123 ) // or apply multiple lenses using with() val updated : Request = Request ( GET , \"/foo\" ). with ( pageLens of 123 , pageSizeLens of 10 ) Securely extracting JDK primitives from HTTP messages is great, but we really want to avoid primitives entirely and go straight to domain types. During construction of Lens, the builders allow mapping to occur so we can leverage Kotlin data classes. This works for both get and set operations: data class MyDate ( val value : LocalDate ) val dateQuery = Query . localDate (). map ( :: MyDate , MyDate :: value ). required ( \"date\" ) val myDate : MyDate = dateQuery ( Request ( GET , \"http://server/search?date=2000-01-01\" )) Lensing HTTP bodies with Data classes \u00b6 Some of the supported message libraries (eg. GSON, Jackson, Moshi, XML) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection (oops - looks like we broke our reflection promise - but technically we're not doing it ;) !). This behaviour is supported in http4k Lenses through the use of the auto() method, which will marshall objects to/from HTTP messages: data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) val messageLens = Body . auto < Message > (). toLens () val body = \"\"\"{\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}}\"\"\" val message : Message = messageLens ( Request ( GET , \"/\" ). body ( body )) This mechanism works for all incoming and outgoing JSON and XML Requests and Responses. To assist with developing whilst using this type of auto-marshalling, we have created a tool to automatically generate a set of data classes for a given messages. Claim D. Serverless \u00b6 Ah yes - Serverless - the latest in the Cool Kids Club and killer fodder for the resume. Well, since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. In order to achieve this, only a single interface AppLoader needs to be implemented - this is responsible for creating the HttpHandler which is adapted to the API of the ApiGatewayProxyRequest/ApiGatewayProxyResponse used by AWS, and then a single class ApiGatewayV2LambdaFunction extended. As this is AWS, there is a fair amount of configuration required to make this possible, but the only http4k specific config is to set the function execution to call org.http4k.MyLambdaFunction Here's a simple example: object TweetEcho : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 140 )) } } class MyLambdaFunction : ApiGatewayV2LambdaFunction ( TweetEcho ) Since http4k is very dependency-light, full binary uploads of these AWS Lambdas tend to be very small - and by utilising Proguard we've seen the size of a Lambda UberJar go as small as 150kb. Introduced in v3.0.0, this support is available in the http4k-serverless-lambda module. The final word(s)! \u00b6 As pointed out above, http4k-core module has zero dependencies. It is also small, even though it also provides: Support for static file-serving with HotReload. A bunch of useful Filters for stuff like Zipkin Request Tracing. Support for Request Contexts. Facilities to record and replay HTTP traffic. There are also a bunch of other modules available, all presented with the same concentration on Testability, API simplicity and consistency: ViewModel driven templating engine support (Handlerbars etc) with HotReload. Popular JSON/XML (Gson, Jackson, Moshi, etc) library support for HTTP bodies. Typesafe HTML Form and Multipart Forms processing, with support for Streaming uploads to a storage service. Forms can also be configured to collect errors instead of just rejecting outright. Typesafe contract module, providing live [OpenApi v2 & v3] documentation. AWS request signing. Resilience4j integration, including Circuit Breakers & Rate Limiting. Testing support via Hamkrest matchers and an in-memory WebDriver implementation. Finally, http4k is proven in production , it has been adopted in at least 2 global investment banks and is serving the vast majority of traffic for a major publishing website (in the top 1000 sites globally according to alexa.com - ie. easily serving 10s of million hits per day on a few nodes) since March 2017. You can see a few example applications here , including a bootstrap project for creating a Github -> Travis -> Heroku CD pipeline in a single command. Well, that's it for this whirlwind tour - we hope you found it worth reading this far! We'd love you to try out http4k and feedback why you love/hate/are indifferent to it :) . And if you want to get involved or chat to the authors, we hang out in the friendly #http4k channel @ slack.kotlinlang,org . Footnotes \u00b6 \"But... but... but... asynchronous! And Webscale!\" , I heard them froth . Yes, you are correct - \"Server as a Function\" is based on asynchronous functions and http4k exposes a synchronous API. However, our experience suggests that for the vast majority of apps, this actually makes API integration harder unless you've got async all the way down - and that is assuming that async clients are actually available for all your various remote dependencies. We found that this plainly didn't matter for our use-cases so went for Simple API\u2122 instead... it's possible however that Kotlin co-routines will allow us to revisit this decision. (UPDATE) Websockets? Yep - simple, testable, and now available in v3.2.1! See the introductory blog post for details!","title":"Meet http4k"},{"location":"blog/meet_http4k/#server_as_a_function_in_kotlin_typesafe_without_the_server","text":"","title":"Server as a Function. In Kotlin. Typesafe. Without the Server."},{"location":"blog/meet_http4k/#november_2017_daviddenton","text":"","title":"november 2017 / @daviddenton"},{"location":"blog/meet_http4k/#meet_http4k","text":"http4k is an HTTP toolkit written in Kotlin that enables the serving and consuming of HTTP services in a functional and consistent way. Whenever (yet another) new JVM HTTP framework is released, the inevitable question that rightly get asked is \"How it this different to X?\" . In this post, I'm going to briefly cover what http4k is, how we think it's different, and address some of those bold claims from the title of this post. Here's a quick rundown of what we think those differences are: http4k is small. Written in pure, functional Kotlin, with zero dependencies. http4k is simple. Like, really simple. No static API magic, no annotations, no reflection. http4k is immutable. It relies on an immutable HTTP model, which makes it a snap to test and debug. http4k is symmetric. It supports remote calls as a first-class concern, and the remote HTTP model is identical to the incoming HTTP model. http4k is typesafe. Say goodbye to all your validation and marshalling boilerplate and hello to automatic request validation and data class-based contracts for HTTP bodies using the Lens API. http4k is serverless. Or rather - server independent. Test an app out of container and then deploy it into any supported local container with 1 LOC - or as a function into AWS Lambda.","title":"Meet http4k"},{"location":"blog/meet_http4k/#oh_god_not_another_framework_why_does_this_even_exist","text":"Firstly - we don't consider http4k to be a framework - it's a set of libraries providing a functional toolkit to serve and consume HTTP services, focusing on simple, consistent, and testable APIs. Hence, whilst it does provide support for various APIs relevant to serving and consuming HTTP , it does not provide every integration under the sun - merely simple points to allow those integrations to be hooked in. Another thing to say is that (not very much) of http4k is new - it's rather the distillation of 15 years worth of experience of using various server-side libraries and hence most of the good ideas are stolen. For instance - the routing module is inspired by UtterlyIdle , the basic \"Server as a function\" model is stolen from Finagle , and the contract module OpenApi/Swagger generator is ported from Fintrospect . With the growing adoption of Kotlin, we wanted something that would fully leverage the functional features of the language and it felt like a good time to start something from scratch, whilst avoiding the magic that plagues other frameworks. Hence, http4k is primarily designed to be a Kotlin-first library.","title":"Oh god, not another framework! Why does this even exist?!?"},{"location":"blog/meet_http4k/#claim_a_small_simple_immutable","text":"Based on the awesome \"Your Server as a Function\" paper from Twitter, http4k apps are modelled by composing 2 types of simple, independent function.","title":"Claim A: Small, simple, immutable."},{"location":"blog/meet_http4k/#function_1_httphandler","text":"An HttpHandler represents an HTTP endpoint. It's not even an Interface, modelled merely as a Typealias : typealias HttpHandler = ( Request ) -> Response Below is a entire http4k application that echoes the request body back in the response. It only relies on the http4k-core module, which itself has zero dependencies: val app = { request : Request -> Response ( OK ). body ( request . body ) } val server = app . asServer ( SunHttp ( 8000 )). start () The Request and Response objects in there are immutable data classes/POKOs, so testing the app requires absolutely no extra infrastructure - just call the function, it's as easy as: class AppTest { @Test fun `echoes request body` () { assertThat ( app ( Request ( POST , \"/\" ). body ( \"hello\" )), equalTo ( Response ( OK ). body ( \"hello\" ))) } } To plug it into a different Server-backend, just depend on the relevant module (Jetty, Undertow, Netty, Apache (httpcore), Ktor CIO, Ktor Netty, and SunHttp are available) and change the call to asServer() .","title":"Function 1: HttpHandler"},{"location":"blog/meet_http4k/#function_2_filter","text":"Filters provides pre and post Request processing and are simply: interface Filter : ( HttpHandler ) -> HttpHandler For API conciseness and discoverability reasons this is modelled as an Interface and not a Typealias - it also has a couple of Kotlin extension methods to allow you to compose Filters with HttpHandlers and other Filters : val setContentType = Filter { next -> { request -> next ( request ). header ( \"Content-Type\" , \"text/plain\" ) } } val repeatBody = Filter { next -> { request -> next ( request . body ( request . bodyString () + request . bodyString ())) } } val composedFilter : Filter = repeatBody . then ( setContentType ) val decoratedApp : HttpHandler = composedFilter . then ( app ) Filters are also trivial to test independently, because they are generally just stateless functions.","title":"Function 2: Filter"},{"location":"blog/meet_http4k/#routing","text":"http4k's nestable routing looks a lot like every other Sinatra-style framework these days, and allows for infinitely nesting HttpHandlers - this just exposes another HttpHandler so you can easily extract, test and reuse sets of routes as easily as you could with one: val app : HttpHandler = routes ( \"/app\" bind GET to decoratedApp , \"/other\" bind routes ( \"/delete\" bind DELETE to { _ : Request -> Response ( OK ) }, \"/post/{name}\" bind POST to { request : Request -> Response ( OK ). body ( \"you POSTed to ${ request . path ( \" name \" ) } \" ) } ) ) And that it - those functions are everything you need to know to write a simple http4k application. The http4k-core module rocks in at about 700kb, and has zero dependencies (other than the Kotlin language itself). Additionally, everything in the core is functional and predictable - there is no static API magic going on under the covers (making it difficult to have multiple apps in the same JVM), no annotations, no compiler-plugins, and no reflection.","title":"Routing"},{"location":"blog/meet_http4k/#claim_b_symmetric_http","text":"Out of the multitude of JVM http frameworks out there, not many actually consider how you app talks to other services, yet in this Microservice\u2122 world that's an absolutely massive part of what many apps do! As per a core principle behind \"Server as a Function\", http4k provides a symmetric API for HTTP clients - ie. it's exactly the same API as is exposed in http4k server applications - the HttpHandler . Here's that entire API again, just in case you've forgotten: typealias HttpHandler = ( Request ) -> Response What does that mean in practice? Well - for one thing, it's less for your brain to learn because you already know the API: val client : HttpHandler = ApacheClient () val response : Response = client ( Request ( GET , \"http://server/path\" )) For another, it means that since clients are just function s you can easily stub them for testing, and since applications and clients are interchangeable, they can be plugged together in memory without putting them on the network - which makes testing insanely fast: fun MyApp1 (): HttpHandler = { Response ( OK ) } fun MyApp2 ( app1 : HttpHandler ): HttpHandler = { app1 ( it ) } val app1 : HttpHandler = MyApp1 () val app2 : HttpHandler = MyApp2 ( app1 ) http4k provides a HTTP client adapters for both Apache and OkHttp , all with streaming support.","title":"Claim B. Symmetric HTTP"},{"location":"blog/meet_http4k/#claim_c_typesafe_http_with_lenses","text":"The immutable http4k model for HTTP objects contains all the usual suspect methods for getting values from the messages. For instance, if we are expecting a search parameter with a query containing a page number: val request = Request ( GET , \"http://server/search?page=123\" ) val page : Int = request . query ( \"page\" ) !! . toInt ...but we also want to ensure that the expected values are both present and valid, since the above example will fail if either of those things is not true. For this purpose, we can use a Lens to enforce the expected HTTP contract. The use of Lenses in http4k applications can remove the need for writing any parsing or validation code for all incoming data (including Forms), as validations are taken care of by the library.","title":"Claim C. Typesafe HTTP with Lenses"},{"location":"blog/meet_http4k/#lens_basics","text":"A Lens is a bi-directional entity which can be used to either get or set a particular value from/onto an HTTP message. http4k provides a DSL to configure these lenses to target particular parts of the message, whilst at the same time specifying the requirement for those parts (i.e. mandatory or optional) and the type. For the above example, we could use the Query Lens builder and then invoke() the Lens on the message to extract the target value: val pageLens = Query . int (). required ( \"page\" ) val page : Int = pageLens ( Request ( GET , \"http://server/search?page=123\" )) If the query parameter is missing or not an Int, the lens extraction operation will fail. There are similar Lens builder functions for all parts of the HTTP message ( Header , Path , Body , FormField etc..), and functions for all common JVM primitive types. They are all completely typesafe - there is no reflection or magic going on - just marshalling of the various entities (in this case String to Int conversion). In the case of failure, we need to apply a Filter to detect the errors and convert them to a BAD_REQUEST response: val queryName = Query . string (). required ( \"name\" ) val app : HttpHandler = routes ( \"/post\" bind POST to { request : Request -> Response ( OK ). body ( queryName ( request )) } ) val app = ServerFilters . CatchLensFailure . then ( handler )( Request ( GET , \"/hello/2000-01-01?myCustomType=someValue\" )) Lenses can also be applied with a correctly typed value (via invoke() ) to set it onto a target object - and as HTTP messages in http4k are immutable, this results in a copy of the modified message: val pageSizeLens = Header . int (). required ( \"page\" ) val page : Response = pageLens ( Response ( OK ), 123 ) // or apply multiple lenses using with() val updated : Request = Request ( GET , \"/foo\" ). with ( pageLens of 123 , pageSizeLens of 10 ) Securely extracting JDK primitives from HTTP messages is great, but we really want to avoid primitives entirely and go straight to domain types. During construction of Lens, the builders allow mapping to occur so we can leverage Kotlin data classes. This works for both get and set operations: data class MyDate ( val value : LocalDate ) val dateQuery = Query . localDate (). map ( :: MyDate , MyDate :: value ). required ( \"date\" ) val myDate : MyDate = dateQuery ( Request ( GET , \"http://server/search?date=2000-01-01\" ))","title":"Lens basics"},{"location":"blog/meet_http4k/#lensing_http_bodies_with_data_classes","text":"Some of the supported message libraries (eg. GSON, Jackson, Moshi, XML) provide the mechanism to automatically marshall data objects to/from JSON and XML using reflection (oops - looks like we broke our reflection promise - but technically we're not doing it ;) !). This behaviour is supported in http4k Lenses through the use of the auto() method, which will marshall objects to/from HTTP messages: data class Email ( val value : String ) data class Message ( val subject : String , val from : Email , val to : Email ) val messageLens = Body . auto < Message > (). toLens () val body = \"\"\"{\"subject\":\"hello\",\"from\":{\"value\":\"bob@git.com\"},\"to\":{\"value\":\"sue@git.com\"}}\"\"\" val message : Message = messageLens ( Request ( GET , \"/\" ). body ( body )) This mechanism works for all incoming and outgoing JSON and XML Requests and Responses. To assist with developing whilst using this type of auto-marshalling, we have created a tool to automatically generate a set of data classes for a given messages.","title":"Lensing HTTP bodies with Data classes"},{"location":"blog/meet_http4k/#claim_d_serverless","text":"Ah yes - Serverless - the latest in the Cool Kids Club and killer fodder for the resume. Well, since http4k is server independent, it turns out to be fairly trivial to deploy full applications to AWS Lambda , and then call them by setting up the API Gateway to proxy requests to the function. Effectively, the combination of these two services become just another Server back-end supported by the library. In order to achieve this, only a single interface AppLoader needs to be implemented - this is responsible for creating the HttpHandler which is adapted to the API of the ApiGatewayProxyRequest/ApiGatewayProxyResponse used by AWS, and then a single class ApiGatewayV2LambdaFunction extended. As this is AWS, there is a fair amount of configuration required to make this possible, but the only http4k specific config is to set the function execution to call org.http4k.MyLambdaFunction Here's a simple example: object TweetEcho : AppLoader { override fun invoke ( env : Map < String , String > ): HttpHandler = { Response ( OK ). body ( it . bodyString (). take ( 140 )) } } class MyLambdaFunction : ApiGatewayV2LambdaFunction ( TweetEcho ) Since http4k is very dependency-light, full binary uploads of these AWS Lambdas tend to be very small - and by utilising Proguard we've seen the size of a Lambda UberJar go as small as 150kb. Introduced in v3.0.0, this support is available in the http4k-serverless-lambda module.","title":"Claim D. Serverless"},{"location":"blog/meet_http4k/#the_final_words","text":"As pointed out above, http4k-core module has zero dependencies. It is also small, even though it also provides: Support for static file-serving with HotReload. A bunch of useful Filters for stuff like Zipkin Request Tracing. Support for Request Contexts. Facilities to record and replay HTTP traffic. There are also a bunch of other modules available, all presented with the same concentration on Testability, API simplicity and consistency: ViewModel driven templating engine support (Handlerbars etc) with HotReload. Popular JSON/XML (Gson, Jackson, Moshi, etc) library support for HTTP bodies. Typesafe HTML Form and Multipart Forms processing, with support for Streaming uploads to a storage service. Forms can also be configured to collect errors instead of just rejecting outright. Typesafe contract module, providing live [OpenApi v2 & v3] documentation. AWS request signing. Resilience4j integration, including Circuit Breakers & Rate Limiting. Testing support via Hamkrest matchers and an in-memory WebDriver implementation. Finally, http4k is proven in production , it has been adopted in at least 2 global investment banks and is serving the vast majority of traffic for a major publishing website (in the top 1000 sites globally according to alexa.com - ie. easily serving 10s of million hits per day on a few nodes) since March 2017. You can see a few example applications here , including a bootstrap project for creating a Github -> Travis -> Heroku CD pipeline in a single command. Well, that's it for this whirlwind tour - we hope you found it worth reading this far! We'd love you to try out http4k and feedback why you love/hate/are indifferent to it :) . And if you want to get involved or chat to the authors, we hang out in the friendly #http4k channel @ slack.kotlinlang,org .","title":"The final word(s)!"},{"location":"blog/meet_http4k/#footnotes","text":"\"But... but... but... asynchronous! And Webscale!\" , I heard them froth . Yes, you are correct - \"Server as a Function\" is based on asynchronous functions and http4k exposes a synchronous API. However, our experience suggests that for the vast majority of apps, this actually makes API integration harder unless you've got async all the way down - and that is assuming that async clients are actually available for all your various remote dependencies. We found that this plainly didn't matter for our use-cases so went for Simple API\u2122 instead... it's possible however that Kotlin co-routines will allow us to revisit this decision. (UPDATE) Websockets? Yep - simple, testable, and now available in v3.2.1! See the introductory blog post for details!","title":"Footnotes"},{"location":"blog/nanoservices/","text":"Nanoservices: The Power of Composition \u00b6 october 2020 / @daviddenton \u00b6 http4k is a small library with a zero dependencies (apart from Kotlin StdLib), but what really makes it shine is the power afforded by the combination of the \"Server as a Function\" concepts of HttpHandler and Filter . Skeptical? We would be disappointed if you weren't! Hence, we decided to prove the types of things that can be accomplished with the APIs provided by http4k and a little ingenuity. For each of the examples below, there is a fully formed http4k application declared inside a function, and the scaffolding to demonstrating it working in an accompanying main() using one of the swappable server backends. Even better, each of app's code (excluding import statements \ud83d\ude42 ) fits in a single Tweet. 1. Build a simple proxy \u00b6 Requires: http4k-core This simple proxy converts HTTP requests to HTTPS. Because of the symmetrical server/client HttpHandler signature, we can simply pipe an HTTP Client onto a server, then add a ProxyHost filter to do the protocol conversion. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `simple proxy` () = JavaHttpClient () . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `simple proxy` (). use { println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" ))) } } 2. Report latency through a proxy \u00b6 Requires: http4k-core Building on the Simple Proxy example, we can simply layer on extra filters to add features to the proxy, in this case reporting the latency of each call. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportRouteLatency import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency reporting proxy` () = ProxyHost ( Https ) . then ( ReportRouteLatency { req , ms -> println ( \" $ req took $ ms \" ) }) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency reporting proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) } } 3. Build a Wireshark to sniff inter-service traffic \u00b6 Requires: http4k-core Applying a DebuggingFilter to the HTTP calls in a proxy dumps the entire contents out to StdOut (or other stream). package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `wire sniffing proxy` () = ProxyHost ( Https ) . then ( PrintRequestAndResponse ()) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `wire sniffing proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) } } 4. Build a ticking Websocket clock \u00b6 Requires: http4k-core , http4k-server-netty Like HTTP handlers, Websockets in http4k can be modelled as simple functions that can be mounted onto a Server, or combined with path patterns if required. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.time.Instant fun `ticking websocket clock` () = { ws : Websocket -> while ( true ) { ws . send ( WsMessage ( Instant . now (). toString ())) Thread . sleep ( 1000 ) } }. asServer ( Netty ()). start () fun main () { `ticking websocket clock` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } } 5. Build a web cache \u00b6 Requires: http4k-core , http4k-server-ktorcio Recording all traffic to disk can be achieved by just creating a ReadWriteCache and then adding a couple of pre-supplied Filters to a proxy. When running this example you can see that only the first request is audited. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.filter.TrafficFilters.ServeCachedFrom import org.http4k.server.Http4kServer import org.http4k.server.KtorCIO import org.http4k.server.asServer import org.http4k.traffic.ReadWriteCache import java.io.File fun `disk cache!` ( dir : String ): Http4kServer { val cache = ReadWriteCache . Disk ( dir ) return ProxyHost ( Https ) . then ( RecordTo ( cache )) . then ( ServeCachedFrom ( cache )) . then ( ReportHttpTransaction { println ( it . request . uri ) }) . then ( JavaHttpClient ()) . asServer ( KtorCIO ()) . start () } fun main () { System . setProperty ( \"http.proxyHost\" , \"localhost\" ) System . setProperty ( \"http.proxyPort\" , \"8000\" ) System . setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) val client = JavaHttpClient () val dir = \"store\" File ( dir ). deleteRecursively () `disk cache!` ( dir ). use { val request = Request ( GET , \"http://api.github.com/users/http4k\" ) println ( client ( request ). bodyString ()) // this request is served from the cache, so will not generate a call println ( client ( request ). bodyString ()) } } 6. Record all traffic to disk and replay it later \u00b6 Requires: http4k-core This example contains two apps. The first is a proxy which captures streams of traffic and records it to a directory on disk. The second app is configured to replay the requests from that disk store at the original server. This kind of traffic capture/replay is very useful for load testing or for tracking down hard-to-diagnose bugs - and it's easy to write other other stores such as an S3 bucket etc. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.traffic.ReadWriteStream.Companion.Disk import java.lang.System.setProperty fun `recording traffic to disk proxy` () = ProxyHost ( Https ) . then ( RecordTo ( Disk ( \"store\" ))) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun `replay previously recorded traffic from a disk store` () = JavaHttpClient (). let { client -> Disk ( \"store\" ). requests () . forEach { println ( it ) client ( it ) } } fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `recording traffic to disk proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k/http4k\" )) } `replay previously recorded traffic from a disk store` () } 7. Watch your FS for file changes \u00b6 Requires: http4k-core , http4k-server-undertow Back to Websockets, we can watch the file system for changes and subscribe to the event feed. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.nio.file.FileSystems.getDefault import java.nio.file.Paths import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY fun `file watcher` () = { ws : Websocket -> val w = getDefault (). newWatchService () Paths . get ( \"\" ). register ( w , ENTRY_MODIFY ) val key = w . take () while ( true ) key . pollEvents () . forEach { ws . send ( WsMessage ( it . context (). toString ())) } }. asServer ( Jetty ()). start () fun main () { `file watcher` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } } 8. Serve static files from disk \u00b6 Requires: http4k-core , http4k-server-undertow Longer than the Python SimpleHttpServer , but still pretty small! package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.routing.ResourceLoader.Companion.Directory import org.http4k.routing.static import org.http4k.server.Undertow import org.http4k.server.asServer fun `static file server` () = static ( Directory ()) . asServer ( Undertow ()) . start () fun main () { `static file server` (). use { // by default, static servers will only serve known file types, // or those registered on construction println ( JavaHttpClient ()( Request ( GET , \"http://localhost:8000/version.json\" ))) } } 9. Build your own ChaosMonkey \u00b6 Requires: http4k-core , http4k-testing-chaos As per the [Principles of Chaos], this proxy adds Chaotic behaviour to a remote service, which is useful for modelling how a system might behave under various failure modes. Chaos can be dynamically injected via an OpenApi documented set of RPC endpoints. package blog.nanoservices import org.http4k.chaos.ChaosBehaviours.Latency import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency injection proxy (between 100ms-500ms)` () = ProxyHost ( Https ) . then ( JavaHttpClient ()) . withChaosApi ( ChaosEngine ( Latency ()). enable ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency injection proxy (between 100ms-500ms)` (). use { println ( JavaHttpClient ()( Request ( POST , \"http://localhost:8000/chaos/activate\" ))) println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" )). header ( \"X-http4k-chaos\" )) } } 10. Build a remote terminal! \u00b6 Requires: http4k-core , http4k-server-netty Use Websockets to remote control a terminal!* Run the example and just type commands into the prompt to have them magicked to the server backend *Obviously this is, in general, a really (really) bad idea. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.lang.Runtime.getRuntime import java.util.Scanner @Suppress ( \"DEPRECATION\" ) fun `websocket terminal` () = { ws : Websocket -> ws . onMessage { val text = getRuntime (). exec ( it . bodyString ()) . inputStream . reader () . readText () ws . send ( WsMessage ( text )) } }. asServer ( Netty ()). start () fun main () { `websocket terminal` () val ws = WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )) ws . onMessage { println ( it . bodyString ()) } val scan = Scanner ( System . `in` ) while ( true ) { ws . send ( WsMessage ( scan . nextLine ())) } } Obviously we haven't thought of everything here. We'd love to hear your ideas about other clever uses of the http4k building blocks, or to take PRs to integrate them into the library for wider use. You can get in touch through GitHub or the usual channels . Principles of Chaos","title":"Nanoservices"},{"location":"blog/nanoservices/#nanoservices_the_power_of_composition","text":"","title":"Nanoservices: The Power of Composition"},{"location":"blog/nanoservices/#october_2020_daviddenton","text":"http4k is a small library with a zero dependencies (apart from Kotlin StdLib), but what really makes it shine is the power afforded by the combination of the \"Server as a Function\" concepts of HttpHandler and Filter . Skeptical? We would be disappointed if you weren't! Hence, we decided to prove the types of things that can be accomplished with the APIs provided by http4k and a little ingenuity. For each of the examples below, there is a fully formed http4k application declared inside a function, and the scaffolding to demonstrating it working in an accompanying main() using one of the swappable server backends. Even better, each of app's code (excluding import statements \ud83d\ude42 ) fits in a single Tweet.","title":"october 2020 / @daviddenton"},{"location":"blog/nanoservices/#1_build_a_simple_proxy","text":"Requires: http4k-core This simple proxy converts HTTP requests to HTTPS. Because of the symmetrical server/client HttpHandler signature, we can simply pipe an HTTP Client onto a server, then add a ProxyHost filter to do the protocol conversion. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `simple proxy` () = JavaHttpClient () . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `simple proxy` (). use { println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" ))) } }","title":"1. Build a simple proxy"},{"location":"blog/nanoservices/#2_report_latency_through_a_proxy","text":"Requires: http4k-core Building on the Simple Proxy example, we can simply layer on extra filters to add features to the proxy, in this case reporting the latency of each call. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportRouteLatency import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency reporting proxy` () = ProxyHost ( Https ) . then ( ReportRouteLatency { req , ms -> println ( \" $ req took $ ms \" ) }) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency reporting proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) } }","title":"2. Report latency through a proxy"},{"location":"blog/nanoservices/#3_build_a_wireshark_to_sniff_inter-service_traffic","text":"Requires: http4k-core Applying a DebuggingFilter to the HTTP calls in a proxy dumps the entire contents out to StdOut (or other stream). package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.DebuggingFilters.PrintRequestAndResponse import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `wire sniffing proxy` () = ProxyHost ( Https ) . then ( PrintRequestAndResponse ()) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `wire sniffing proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) } }","title":"3. Build a Wireshark to sniff inter-service traffic"},{"location":"blog/nanoservices/#4_build_a_ticking_websocket_clock","text":"Requires: http4k-core , http4k-server-netty Like HTTP handlers, Websockets in http4k can be modelled as simple functions that can be mounted onto a Server, or combined with path patterns if required. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.time.Instant fun `ticking websocket clock` () = { ws : Websocket -> while ( true ) { ws . send ( WsMessage ( Instant . now (). toString ())) Thread . sleep ( 1000 ) } }. asServer ( Netty ()). start () fun main () { `ticking websocket clock` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } }","title":"4. Build a ticking Websocket clock"},{"location":"blog/nanoservices/#5_build_a_web_cache","text":"Requires: http4k-core , http4k-server-ktorcio Recording all traffic to disk can be achieved by just creating a ReadWriteCache and then adding a couple of pre-supplied Filters to a proxy. When running this example you can see that only the first request is audited. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.ResponseFilters.ReportHttpTransaction import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.filter.TrafficFilters.ServeCachedFrom import org.http4k.server.Http4kServer import org.http4k.server.KtorCIO import org.http4k.server.asServer import org.http4k.traffic.ReadWriteCache import java.io.File fun `disk cache!` ( dir : String ): Http4kServer { val cache = ReadWriteCache . Disk ( dir ) return ProxyHost ( Https ) . then ( RecordTo ( cache )) . then ( ServeCachedFrom ( cache )) . then ( ReportHttpTransaction { println ( it . request . uri ) }) . then ( JavaHttpClient ()) . asServer ( KtorCIO ()) . start () } fun main () { System . setProperty ( \"http.proxyHost\" , \"localhost\" ) System . setProperty ( \"http.proxyPort\" , \"8000\" ) System . setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) val client = JavaHttpClient () val dir = \"store\" File ( dir ). deleteRecursively () `disk cache!` ( dir ). use { val request = Request ( GET , \"http://api.github.com/users/http4k\" ) println ( client ( request ). bodyString ()) // this request is served from the cache, so will not generate a call println ( client ( request ). bodyString ()) } }","title":"5. Build a web cache"},{"location":"blog/nanoservices/#6_record_all_traffic_to_disk_and_replay_it_later","text":"Requires: http4k-core This example contains two apps. The first is a proxy which captures streams of traffic and records it to a directory on disk. The second app is configured to replay the requests from that disk store at the original server. This kind of traffic capture/replay is very useful for load testing or for tracking down hard-to-diagnose bugs - and it's easy to write other other stores such as an S3 bucket etc. package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.filter.TrafficFilters.RecordTo import org.http4k.server.SunHttp import org.http4k.server.asServer import org.http4k.traffic.ReadWriteStream.Companion.Disk import java.lang.System.setProperty fun `recording traffic to disk proxy` () = ProxyHost ( Https ) . then ( RecordTo ( Disk ( \"store\" ))) . then ( JavaHttpClient ()) . asServer ( SunHttp ()) . start () fun `replay previously recorded traffic from a disk store` () = JavaHttpClient (). let { client -> Disk ( \"store\" ). requests () . forEach { println ( it ) client ( it ) } } fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `recording traffic to disk proxy` (). use { JavaHttpClient ()( Request ( GET , \"http://github.com/\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k\" )) JavaHttpClient ()( Request ( GET , \"http://github.com/http4k/http4k\" )) } `replay previously recorded traffic from a disk store` () }","title":"6. Record all traffic to disk and replay it later"},{"location":"blog/nanoservices/#7_watch_your_fs_for_file_changes","text":"Requires: http4k-core , http4k-server-undertow Back to Websockets, we can watch the file system for changes and subscribe to the event feed. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Jetty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.nio.file.FileSystems.getDefault import java.nio.file.Paths import java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY fun `file watcher` () = { ws : Websocket -> val w = getDefault (). newWatchService () Paths . get ( \"\" ). register ( w , ENTRY_MODIFY ) val key = w . take () while ( true ) key . pollEvents () . forEach { ws . send ( WsMessage ( it . context (). toString ())) } }. asServer ( Jetty ()). start () fun main () { `file watcher` () WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )). onMessage { println ( it ) } }","title":"7. Watch your FS for file changes"},{"location":"blog/nanoservices/#8_serve_static_files_from_disk","text":"Requires: http4k-core , http4k-server-undertow Longer than the Python SimpleHttpServer , but still pretty small! package blog.nanoservices import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.routing.ResourceLoader.Companion.Directory import org.http4k.routing.static import org.http4k.server.Undertow import org.http4k.server.asServer fun `static file server` () = static ( Directory ()) . asServer ( Undertow ()) . start () fun main () { `static file server` (). use { // by default, static servers will only serve known file types, // or those registered on construction println ( JavaHttpClient ()( Request ( GET , \"http://localhost:8000/version.json\" ))) } }","title":"8. Serve static files from disk"},{"location":"blog/nanoservices/#9_build_your_own_chaosmonkey","text":"Requires: http4k-core , http4k-testing-chaos As per the [Principles of Chaos], this proxy adds Chaotic behaviour to a remote service, which is useful for modelling how a system might behave under various failure modes. Chaos can be dynamically injected via an OpenApi documented set of RPC endpoints. package blog.nanoservices import org.http4k.chaos.ChaosBehaviours.Latency import org.http4k.chaos.ChaosEngine import org.http4k.chaos.withChaosApi import org.http4k.client.JavaHttpClient import org.http4k.core.Method.GET import org.http4k.core.Method.POST import org.http4k.core.Request import org.http4k.core.then import org.http4k.filter.RequestFilters.ProxyHost import org.http4k.filter.RequestFilters.ProxyProtocolMode.Https import org.http4k.server.SunHttp import org.http4k.server.asServer import java.lang.System.setProperty fun `latency injection proxy (between 100ms-500ms)` () = ProxyHost ( Https ) . then ( JavaHttpClient ()) . withChaosApi ( ChaosEngine ( Latency ()). enable ()) . asServer ( SunHttp ()) . start () fun main () { setProperty ( \"http.proxyHost\" , \"localhost\" ) setProperty ( \"http.proxyPort\" , \"8000\" ) setProperty ( \"http.nonProxyHosts\" , \"localhost\" ) `latency injection proxy (between 100ms-500ms)` (). use { println ( JavaHttpClient ()( Request ( POST , \"http://localhost:8000/chaos/activate\" ))) println ( JavaHttpClient ()( Request ( GET , \"http://github.com/\" )). header ( \"X-http4k-chaos\" )) } }","title":"9. Build your own ChaosMonkey"},{"location":"blog/nanoservices/#10_build_a_remote_terminal","text":"Requires: http4k-core , http4k-server-netty Use Websockets to remote control a terminal!* Run the example and just type commands into the prompt to have them magicked to the server backend *Obviously this is, in general, a really (really) bad idea. package blog.nanoservices import org.http4k.client.WebsocketClient import org.http4k.core.Uri import org.http4k.server.Netty import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsMessage import java.lang.Runtime.getRuntime import java.util.Scanner @Suppress ( \"DEPRECATION\" ) fun `websocket terminal` () = { ws : Websocket -> ws . onMessage { val text = getRuntime (). exec ( it . bodyString ()) . inputStream . reader () . readText () ws . send ( WsMessage ( text )) } }. asServer ( Netty ()). start () fun main () { `websocket terminal` () val ws = WebsocketClient . nonBlocking ( Uri . of ( \"http://localhost:8000\" )) ws . onMessage { println ( it . bodyString ()) } val scan = Scanner ( System . `in` ) while ( true ) { ws . send ( WsMessage ( scan . nextLine ())) } } Obviously we haven't thought of everything here. We'd love to hear your ideas about other clever uses of the http4k building blocks, or to take PRs to integrate them into the library for wider use. You can get in touch through GitHub or the usual channels . Principles of Chaos","title":"10. Build a remote terminal!"},{"location":"blog/regarding_jcenter/","text":"Reassurance to http4k users regarding JCenter shutdown \u00b6 feb 2021 / the http4k team \u00b6 It was announced this week that the JCenter artifact repository would be shutting down in May 2021 . As JCenter was a superset of the Maven Central repository, this obviously comes as disappointing and worrying news regarding the future of Open Source software distribution for the JVM. Many builds will undoubtedly break as a result of this move. The http4k project currently primarily builds and distributes our 50+ artifacts to Bintray and then syncs them to Maven Central automatically. As a result of this announcement, we have totally removed any dependency on JCenter from our builds and have verified that all of our dependencies resolve correctly without it. Hence we can say with absolute confidence that: As of v4.3.0.0, http4k users will be unaffected by the JCenter shutdown. http4k has always worked on the principle of being as lightweight as possible with respect to dependencies, and this situation has rather vindicated our position. Lots of Open Source projects will not be in such a fortunate position. We will be investigating alternatives to JCenter to keep our build pipelines working as efficiently as possible, but the artifacts will always be available in the Maven Central and this is where we will continue to source our dependencies from. On a more personal note, we would remind you that now would be an excellent opportunity to show appreciation for the entirely voluntary efforts of the http4k team and sponsor the project so we can keep on supporting our users. // the http4k team \u00b6","title":"JCenter Shutdown"},{"location":"blog/regarding_jcenter/#reassurance_to_http4k_users_regarding_jcenter_shutdown","text":"","title":"Reassurance to http4k users regarding JCenter shutdown"},{"location":"blog/regarding_jcenter/#feb_2021_the_http4k_team","text":"It was announced this week that the JCenter artifact repository would be shutting down in May 2021 . As JCenter was a superset of the Maven Central repository, this obviously comes as disappointing and worrying news regarding the future of Open Source software distribution for the JVM. Many builds will undoubtedly break as a result of this move. The http4k project currently primarily builds and distributes our 50+ artifacts to Bintray and then syncs them to Maven Central automatically. As a result of this announcement, we have totally removed any dependency on JCenter from our builds and have verified that all of our dependencies resolve correctly without it. Hence we can say with absolute confidence that: As of v4.3.0.0, http4k users will be unaffected by the JCenter shutdown. http4k has always worked on the principle of being as lightweight as possible with respect to dependencies, and this situation has rather vindicated our position. Lots of Open Source projects will not be in such a fortunate position. We will be investigating alternatives to JCenter to keep our build pipelines working as efficiently as possible, but the artifacts will always be available in the Maven Central and this is where we will continue to source our dependencies from. On a more personal note, we would remind you that now would be an excellent opportunity to show appreciation for the entirely voluntary efforts of the http4k team and sponsor the project so we can keep on supporting our users.","title":"feb 2021 / the http4k team"},{"location":"blog/regarding_jcenter/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/retrospective_v3/","text":"A retrospective on http4k v3 \u00b6 september 2020 / the http4k team \u00b6 It's been quite a long time since we released version 3 of http4k all the way back in November 2017. Wow - that's over 1000 days in fact! Still, that doesn't mean that we've been sitting on our hands over in http4k Towers - far from it, we've been busier than ever making sure that we'll remember 2020 for more than just hibernating away in a bunker. In fact, the current interesting situation did give us an idea for a pretty original piece of swag... The eagle-eyed amongst you may have noticed that the project branding has undergone a bit of cosmetic surgery - we thought we'd treat ourselves to a professional job as opposed to the one we knocked up on the cheap way back at the start of the project. We're planning to do an entire refit of the content over the next few months, hopefully to make everything a little easier to find and to provide a few more pointers about where to start with the library. Stay tuned... Anyway, I digress. We thought that we'd do a bit of a retrospective on our version 3 journey. But first off, here are some numbers about what has gone on in those 1000 days in the http4k codebase: over 2500 commits! 268 releases 257 PRs merged 139 issues fixed 71 new amazing contributors 28 new http4k library modules 5 newly supported HTTP client libraries 5 new server engines 4 new messaging format modules 4 new testing integrations 2 new serverless backends 2 new templating libraries supported 0 new dependencies added to the core module \ud83d\ude09 If you'd like to check out the old version in the GitHub time machine, here is how the code looked all that time ago. Community involvement \u00b6 One of the most important things to us when we were developing was to create a library that we loved using, and we're thrilled that our users are so positive about http4k as well. The community around the library is just the type that we envisaged - a friendly, helpful and collaborative space, and some of the very best code (be it new modules or and fixes) have come directly from there. Our favourite piece of feedback was this post to our Slack channel: \"I find the entire http4k project quite exemplary in both function and style/form. At first glance (long time ago) my impression was that it must be deficient because the code-base was too small and the style - simple and elegant -- to the point I was skeptical it would actually work in 'the real world'. I have verified to my satisfaction that I was totally wrong. The apparent simplicity is actually elegance -- a direct representative of the overall design architecture. So thank you -- not just for a great library but also an inspiration and example of excellent engineering we could all strive to follow as a model of something done well.\" - David A. Lee This post made us very happy and all of our efforts feel completely worthwhile. We hope you all feel the same. \ud83d\ude0a As for community reach, we've been in touch with http4k users from all corners of the planet from all kinds of different projects and industries - from COVID tracking apps (relevant!) to Academic Publishers (that one was us) to Investment and Challenger Banks (also us), to No-code platforms. If Twitter is to be believed, the library seems pretty popular in Japan, but no-one on the core team speaks Japanese so we don't actually know why! Globe-trotting \u00b6 In 2018, we were lucky enough to be invited to KotlinConf in Amsterdam to talk about the development of http4k, and this led to us presenting at a total of 10 international conferences, meetups and privately hosted company events spanning across 5 timezones to talk about the library and it's development story. As (apparent) ambassadors for how successful Kotlin can be in the serverside arena, so watch this space in the latter part of 2020 and beyond... On the radar \u00b6 Another high point for us was having http4k featured in the Thoughtworks Technology Radar , which is a quarterly publication highlighting tools, techniques and languages that the well-known consultancy have been using to successfully deliver projects across the globe. ThoughtWorks called out the test-driven nature of http4k, citing: \"Apart from its elegance and simplicity, we also like its emphasis on testability \u2014 given that the entities in the libraries are immutable and the routes in the app, as well as the app itself, are just functions, they're super easy to test.\" - TW TechRadar As Kotlin cements its reputation as a solid choice for serverside (and eventually everywhere!) development, and continues to receive backing from successful projects across the proprietary and open source ecosystems, we hope to be able to do our bit to nudge it ever further upwards in the \"most loved language\" tables. Performing to a crowd \u00b6 One of the most frequent questions that we get asked about http4k is \"how does it perform?\". We attempted to answer that question by entering our baby into the TechEmpower benchmarks, which is a suite of tests which pits each library against the rest of the pack in a set of real-world-esque scenarios to see how it performs including JSON serialisation, database access and HTML templating generation. Overall, we were thrilled (and continue to be) with the results of the benchmarks. Since the first round (16), http4k has been the best performing pure Kotlin web library across the contenders. The most important factor to us that there were no special tricks involved in the implementations - ie. the endpoints under test were written exactly as they would be on a \"real\" project and no custom tuning other than standard JVM options applied. In terms of the performance of the server backends, Apache HttpComponents (version 4) has been consistently the strongest performer in the previous benchmarks, although there have been performance improvements to the Netty backend implementations that we are hopeful in the upcoming round 20 might make it a real contender for the http4k \ud83d\udc51. Enter the platform! \u00b6 When we conceived http4k, it was a simple 40 line shim over the top of another Java-based framework, bashed together on a HackDay. Never did we realise that a few years later it would be a very popular library with over 40 modules! For this reason (and to save on typing), we introduced the http4k-bom module (learn more about BOM here , allowing users to use a single version of all http4k libraries and then just import the modules they need. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:3.259.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) } Cloudy-wowdy stuff \u00b6 Just as in every codebase there is a package called \"utils\", this also happens with libraries - useful code that doesn't quite fit anywhere else, yet you just always end up needing. For http4k, these utils were about the ancillary stuff that goes around an application to make it support 12-factor ideals such as configuration and relative primitives. We didn't want to put this stuff into the http4k-core module as we felt it wasn't absolutely necessary (and we wanted to continue to keep the binary size of the core module down). Thus, http4k-cloudnative was born! The major feature of this module is loading typesafe Environmental configuration of applications, using - what else... Yep - the reusing of the existing http4k lens system to cover configuring your apps, meaning that it's now impossible to make easy mistakes such as setting a 10-second timeout as 10-milliseconds! You can check out exactly how to use the feature in the docs . Testing modules \u00b6 It's no secret that the http4k team love testing - it's part of our core DNA and the simplicity of core design would be worthless unless we could test apps built with the library simply and effectively. To that end, some of the most exciting additions that we've added to the library have been in the arena of testing: Approval Testing is a technique for simplifying complex assertions that might otherwise be more effectively checked by eye. http4k-testing-approval provides the primitives and tooling for supporting this style of testing, Chaos Testing was made famous by Netflix for proving out how systems react when everything heads south. http4k-testing-chaos adds transparent, programmable failure-generation to any http4k app using only a simple Filter . Service Virtualization enables API test contracts to be encoded and then shipped, simplifying the process of proving that apps retain compatibility, http4k-testing-servirtium provides the basis for recording and replaying contracts from disk or from other sources such as GitHub. OpenAPI FTW \u00b6 One of the most popular and standout http4k features is the support for the OpenApi specification. Originally supporting Swagger 2 spec via the http4k-contract module, we rewrote the implementation to add support for much more complete (and consistent!) version 3 of specification in May 2019. The module will now generate fully compliant OpenAPI3 documentation, including full JSON Schema breakdowns for class models and taking advantage of Kotlin class features such as enums and nullability. Powered by the http4k lens API, this runtime system allows developers to avoid concerning themselves with tediously documenting API models which can easily go stale. Serverless turnabout \u00b6 The major http4k feature in version 3.0.0 was the addition of support for Serverless backends - namely the granddaddy of Serverless - AWS Lambda. And you know what they say about the first implementation of something? They say that it's probably wrong. Well, turns out they were right (again). When we got to introducing the second and third implementations of Serverless (Google Cloud Functions and OpenWhisk), we realised that the approach taken for AWS wasn't very dev friendly... it relied on reflection to solve the problem of loading the Lambda function class. This actually broke one of our own cardinal rules that we set for the http4k project: \"Absolutely no magic involved: No reflection. No annotations.\" - 5th Commandment of http4k So - we did what any good dev team would do and replaced the magic function loading mechanism with a more developer friendly API working by class extension. Fear not readers - the guilty parties have been appropriately punished, and it (probably) won't happen again. \ud83d\ude09 One other piece of interesting research which came out and somewhat vindicated the dependency-lite approach of http4k was Cold Start War , which performed a lot of experiments and concluded that: \"As expected, the dependencies slow the loading down. You should keep your Functions lean, otherwise, you will pay in seconds for every cold start.\" - Mikhail Shilkov For production deployments, we continue to recommend the use of a tool such as Proguard or R8 to massively reduce the size of packaged Serverless Function JAR file. The http4k serverless modules also ship with zero or minimal dependencies to avoid any transitive bloat that might occur. Going native \u00b6 Apart from Serverless, one of the most exciting things happening in JVM-land right now is the advent of native technologies such as Quarkus and GraalVM, giving the possibility of compiling apps direct to binaries - which give a massive performance boost. It's a young technology and often involves various amounts of trickery to get around limitations of the native system regarding areas such as reflection. Luckily for us (and you!), http4k operates on an anti-magic principle (see the 5th commandment above) and it was a nice surprise when it occurred that, with the correct server engine (Apache for the curious), http4k applications can be packaged out-of-the-box to this format with absolutely no modifications of aforementioned trickery. We look forward to further supporting these technologies as they develop, and will endeavour to provide custom modules to suppport teams who want to take advantage of them. Future echoes... \u00b6 \u201cAnd the open road rolled out in front of us.\u201d - Alexandra Bracken, In The Afterlight So what's next? Well, we've got a load of good stuff coming up for post version 4 of http4k, the rest of 2020 and beyond. As well as the website improvements, we've got a bunch of tools in the works to make the library more quick-start friendly, including the ability to autogenerate advanced http4k Clients and Server stubs from OpenApi specifications - both from the command-line and from the browser. We're also looking at improvements in the versioning scheme to better communicate breaking changes, and rolling out modules to enable better Serverless platform support. Whatever happens though, the focus of http4k will always be on providing a best-in-class Developer and Testing experience. We'd love to hear how we're doing, so please drop into the comm channels to get in touch. Here's to the future. Stay safe out there and we'll see you in it. // the http4k team \u00b6","title":"Retrospective on v3"},{"location":"blog/retrospective_v3/#a_retrospective_on_http4k_v3","text":"","title":"A retrospective on http4k v3"},{"location":"blog/retrospective_v3/#september_2020_the_http4k_team","text":"It's been quite a long time since we released version 3 of http4k all the way back in November 2017. Wow - that's over 1000 days in fact! Still, that doesn't mean that we've been sitting on our hands over in http4k Towers - far from it, we've been busier than ever making sure that we'll remember 2020 for more than just hibernating away in a bunker. In fact, the current interesting situation did give us an idea for a pretty original piece of swag... The eagle-eyed amongst you may have noticed that the project branding has undergone a bit of cosmetic surgery - we thought we'd treat ourselves to a professional job as opposed to the one we knocked up on the cheap way back at the start of the project. We're planning to do an entire refit of the content over the next few months, hopefully to make everything a little easier to find and to provide a few more pointers about where to start with the library. Stay tuned... Anyway, I digress. We thought that we'd do a bit of a retrospective on our version 3 journey. But first off, here are some numbers about what has gone on in those 1000 days in the http4k codebase: over 2500 commits! 268 releases 257 PRs merged 139 issues fixed 71 new amazing contributors 28 new http4k library modules 5 newly supported HTTP client libraries 5 new server engines 4 new messaging format modules 4 new testing integrations 2 new serverless backends 2 new templating libraries supported 0 new dependencies added to the core module \ud83d\ude09 If you'd like to check out the old version in the GitHub time machine, here is how the code looked all that time ago.","title":"september 2020 / the http4k team"},{"location":"blog/retrospective_v3/#community_involvement","text":"One of the most important things to us when we were developing was to create a library that we loved using, and we're thrilled that our users are so positive about http4k as well. The community around the library is just the type that we envisaged - a friendly, helpful and collaborative space, and some of the very best code (be it new modules or and fixes) have come directly from there. Our favourite piece of feedback was this post to our Slack channel: \"I find the entire http4k project quite exemplary in both function and style/form. At first glance (long time ago) my impression was that it must be deficient because the code-base was too small and the style - simple and elegant -- to the point I was skeptical it would actually work in 'the real world'. I have verified to my satisfaction that I was totally wrong. The apparent simplicity is actually elegance -- a direct representative of the overall design architecture. So thank you -- not just for a great library but also an inspiration and example of excellent engineering we could all strive to follow as a model of something done well.\" - David A. Lee This post made us very happy and all of our efforts feel completely worthwhile. We hope you all feel the same. \ud83d\ude0a As for community reach, we've been in touch with http4k users from all corners of the planet from all kinds of different projects and industries - from COVID tracking apps (relevant!) to Academic Publishers (that one was us) to Investment and Challenger Banks (also us), to No-code platforms. If Twitter is to be believed, the library seems pretty popular in Japan, but no-one on the core team speaks Japanese so we don't actually know why!","title":"Community involvement"},{"location":"blog/retrospective_v3/#globe-trotting","text":"In 2018, we were lucky enough to be invited to KotlinConf in Amsterdam to talk about the development of http4k, and this led to us presenting at a total of 10 international conferences, meetups and privately hosted company events spanning across 5 timezones to talk about the library and it's development story. As (apparent) ambassadors for how successful Kotlin can be in the serverside arena, so watch this space in the latter part of 2020 and beyond...","title":"Globe-trotting"},{"location":"blog/retrospective_v3/#on_the_radar","text":"Another high point for us was having http4k featured in the Thoughtworks Technology Radar , which is a quarterly publication highlighting tools, techniques and languages that the well-known consultancy have been using to successfully deliver projects across the globe. ThoughtWorks called out the test-driven nature of http4k, citing: \"Apart from its elegance and simplicity, we also like its emphasis on testability \u2014 given that the entities in the libraries are immutable and the routes in the app, as well as the app itself, are just functions, they're super easy to test.\" - TW TechRadar As Kotlin cements its reputation as a solid choice for serverside (and eventually everywhere!) development, and continues to receive backing from successful projects across the proprietary and open source ecosystems, we hope to be able to do our bit to nudge it ever further upwards in the \"most loved language\" tables.","title":"On the radar"},{"location":"blog/retrospective_v3/#performing_to_a_crowd","text":"One of the most frequent questions that we get asked about http4k is \"how does it perform?\". We attempted to answer that question by entering our baby into the TechEmpower benchmarks, which is a suite of tests which pits each library against the rest of the pack in a set of real-world-esque scenarios to see how it performs including JSON serialisation, database access and HTML templating generation. Overall, we were thrilled (and continue to be) with the results of the benchmarks. Since the first round (16), http4k has been the best performing pure Kotlin web library across the contenders. The most important factor to us that there were no special tricks involved in the implementations - ie. the endpoints under test were written exactly as they would be on a \"real\" project and no custom tuning other than standard JVM options applied. In terms of the performance of the server backends, Apache HttpComponents (version 4) has been consistently the strongest performer in the previous benchmarks, although there have been performance improvements to the Netty backend implementations that we are hopeful in the upcoming round 20 might make it a real contender for the http4k \ud83d\udc51.","title":"Performing to a crowd"},{"location":"blog/retrospective_v3/#enter_the_platform","text":"When we conceived http4k, it was a simple 40 line shim over the top of another Java-based framework, bashed together on a HackDay. Never did we realise that a few years later it would be a very popular library with over 40 modules! For this reason (and to save on typing), we introduced the http4k-bom module (learn more about BOM here , allowing users to use a single version of all http4k libraries and then just import the modules they need. dependencies { implementation ( platform ( \"org.http4k:http4k-bom:3.259.0\" )) implementation ( \"org.http4k:http4k-core\" ) implementation ( \"org.http4k:http4k-server-undertow\" ) }","title":"Enter the platform!"},{"location":"blog/retrospective_v3/#cloudy-wowdy_stuff","text":"Just as in every codebase there is a package called \"utils\", this also happens with libraries - useful code that doesn't quite fit anywhere else, yet you just always end up needing. For http4k, these utils were about the ancillary stuff that goes around an application to make it support 12-factor ideals such as configuration and relative primitives. We didn't want to put this stuff into the http4k-core module as we felt it wasn't absolutely necessary (and we wanted to continue to keep the binary size of the core module down). Thus, http4k-cloudnative was born! The major feature of this module is loading typesafe Environmental configuration of applications, using - what else... Yep - the reusing of the existing http4k lens system to cover configuring your apps, meaning that it's now impossible to make easy mistakes such as setting a 10-second timeout as 10-milliseconds! You can check out exactly how to use the feature in the docs .","title":"Cloudy-wowdy stuff"},{"location":"blog/retrospective_v3/#testing_modules","text":"It's no secret that the http4k team love testing - it's part of our core DNA and the simplicity of core design would be worthless unless we could test apps built with the library simply and effectively. To that end, some of the most exciting additions that we've added to the library have been in the arena of testing: Approval Testing is a technique for simplifying complex assertions that might otherwise be more effectively checked by eye. http4k-testing-approval provides the primitives and tooling for supporting this style of testing, Chaos Testing was made famous by Netflix for proving out how systems react when everything heads south. http4k-testing-chaos adds transparent, programmable failure-generation to any http4k app using only a simple Filter . Service Virtualization enables API test contracts to be encoded and then shipped, simplifying the process of proving that apps retain compatibility, http4k-testing-servirtium provides the basis for recording and replaying contracts from disk or from other sources such as GitHub.","title":"Testing modules"},{"location":"blog/retrospective_v3/#openapi_ftw","text":"One of the most popular and standout http4k features is the support for the OpenApi specification. Originally supporting Swagger 2 spec via the http4k-contract module, we rewrote the implementation to add support for much more complete (and consistent!) version 3 of specification in May 2019. The module will now generate fully compliant OpenAPI3 documentation, including full JSON Schema breakdowns for class models and taking advantage of Kotlin class features such as enums and nullability. Powered by the http4k lens API, this runtime system allows developers to avoid concerning themselves with tediously documenting API models which can easily go stale.","title":"OpenAPI FTW"},{"location":"blog/retrospective_v3/#serverless_turnabout","text":"The major http4k feature in version 3.0.0 was the addition of support for Serverless backends - namely the granddaddy of Serverless - AWS Lambda. And you know what they say about the first implementation of something? They say that it's probably wrong. Well, turns out they were right (again). When we got to introducing the second and third implementations of Serverless (Google Cloud Functions and OpenWhisk), we realised that the approach taken for AWS wasn't very dev friendly... it relied on reflection to solve the problem of loading the Lambda function class. This actually broke one of our own cardinal rules that we set for the http4k project: \"Absolutely no magic involved: No reflection. No annotations.\" - 5th Commandment of http4k So - we did what any good dev team would do and replaced the magic function loading mechanism with a more developer friendly API working by class extension. Fear not readers - the guilty parties have been appropriately punished, and it (probably) won't happen again. \ud83d\ude09 One other piece of interesting research which came out and somewhat vindicated the dependency-lite approach of http4k was Cold Start War , which performed a lot of experiments and concluded that: \"As expected, the dependencies slow the loading down. You should keep your Functions lean, otherwise, you will pay in seconds for every cold start.\" - Mikhail Shilkov For production deployments, we continue to recommend the use of a tool such as Proguard or R8 to massively reduce the size of packaged Serverless Function JAR file. The http4k serverless modules also ship with zero or minimal dependencies to avoid any transitive bloat that might occur.","title":"Serverless turnabout"},{"location":"blog/retrospective_v3/#going_native","text":"Apart from Serverless, one of the most exciting things happening in JVM-land right now is the advent of native technologies such as Quarkus and GraalVM, giving the possibility of compiling apps direct to binaries - which give a massive performance boost. It's a young technology and often involves various amounts of trickery to get around limitations of the native system regarding areas such as reflection. Luckily for us (and you!), http4k operates on an anti-magic principle (see the 5th commandment above) and it was a nice surprise when it occurred that, with the correct server engine (Apache for the curious), http4k applications can be packaged out-of-the-box to this format with absolutely no modifications of aforementioned trickery. We look forward to further supporting these technologies as they develop, and will endeavour to provide custom modules to suppport teams who want to take advantage of them.","title":"Going native"},{"location":"blog/retrospective_v3/#future_echoes","text":"\u201cAnd the open road rolled out in front of us.\u201d - Alexandra Bracken, In The Afterlight So what's next? Well, we've got a load of good stuff coming up for post version 4 of http4k, the rest of 2020 and beyond. As well as the website improvements, we've got a bunch of tools in the works to make the library more quick-start friendly, including the ability to autogenerate advanced http4k Clients and Server stubs from OpenApi specifications - both from the command-line and from the browser. We're also looking at improvements in the versioning scheme to better communicate breaking changes, and rolling out modules to enable better Serverless platform support. Whatever happens though, the focus of http4k will always be on providing a best-in-class Developer and Testing experience. We'd love to hear how we're doing, so please drop into the comm channels to get in touch. Here's to the future. Stay safe out there and we'll see you in it.","title":"Future echoes..."},{"location":"blog/retrospective_v3/#the_http4k_team","text":"","title":"// the http4k team"},{"location":"blog/toolbox/","text":"http4k Toolbox: Guns for show, knives for a pro \u00b6 november 2020 / @daviddenton \u00b6 Over the years of creating apps with http4k, we've collated several tools that we use to turbo charge our development activities. While working on v4 of the library, and with a little time on our hands, we decided to bring all of these tools together into a single place so that all of our users could get their benefits. These tools are mostly based around code generation of some sort or another and are mainly used to shortcut repetitive or mundane development tasks that you might encounter when developing and testing applications with http4k. For development, we made extensive use of the excellent http4k web library (have you heard of it? it's really rather good!), as well as KotlinPoet and Bunting4k , a new CLI flags library that we created for this task. A lot of the credit for the creation of the Toolbox has to go to the amazing Albert Latacz , who not only created large parts of the functionality, but also investigated build and deployment options. Web toolbox \u00b6 The Toolbox is now the main starting point for working with http4k The main concept behind each of the functions is to allow the user to preview any generated code before they download it. We have also set up automated pipelines to update the Toolbox whenever we do a release of http4k so it should always be up to date. It's worth noting that the Toolbox is also entirely stateless, so no user content is recorded in any form. The http4k Toolbox is available @ toolbox.http4k.org CLI \u00b6 The http4k team are massive fans of automation, so we also wanted to enable teams to use the functions of the Toolbox in a shell. Hence, we decided to also make a CLI binary for the Toolbox, which is available to install via both Brew and SDKMan . You can find instructions about how to install it on the Toolbox homepage . Toolbox functionality guide \u00b6 Where we introduce each of the functions of the Toolbox , with a rationale and a bit of background on each. Generating a project \u00b6 This is quite a common one for various libraries, but they tend to be quite basic - and we thought we could do better. The http4k Project Generator is an intelligent wizard which allows the user to select their backend, extra modules, and build system. It then crunches all the code from these choices together into a coherent project configuration (called a \"Stack\"), and provides a reusable link for regenerating the same project configuration again. The questionnaire is dynamically generated by providing options for all 17+ supported Server and Serverless backends, and the further 30+ http4k integration modules, including examples of the different types of tests that you can write for apps. We can also add other \"features\" to be mixed in to the project structure. As well as downloading the project, you can also browse the generated code in a browser. Try out the project generator here . OpenAPI3 Server, Client, & Models \u00b6 http4k already supports generating OpenAPI3 specification documents from your code, but one thing that you often come across is when you need to generate an HTTP server or Client for someone else's application which is documented with using the same format. Initially we did look at the existing OpenAPI generator projects available for other libraries, but they are mostly based around static templates, and once again we thought we could do something a little more sophisticated. The OpenAPI generator . accepts a JSON or YAML specification (or URL), and generates: Data Classes for JSON API model schemas - The message objects are generated using a \"best guess\" algorithm from the JSON Schema information in the specification document, mapping the base JSON types to their Kotlin equivalents. Template http4k Server implementation containing a set of generated API endpoint functions and Lens implementations for all defined HTTP contract parts (path, query etc), as well as auto-marshalled JSON request and response bodies. Security implementations (API Key, Basic, Bearer) are also generated. http4k API client implementation with functions generated for each API endpoint. Lens implementations are generated and HTTP contract parts (path, query etc) are marshalled both into and out of the Request and Response messages. As well as downloading the files as a ZIP archive, you can also browse the generated code in a browser. The OpenAPI generator is available here . Generating Data Classes from JSON, XML and YAML \u00b6 Most of the time when working with API models in http4k, you have example messages in the native format which need to be auto-marshalled into Kotlin using Jackson or one of the other supported format libraries. This results in a very boring exercise in conversion, so we wrote this converter that will generate Data Classes to support reading/writing of JSON, XML and YAML messages. For example, this JSON... { \"jsonRoot\" : { \"child\" : [ \"hello\" , \"there\" ], \"num\" : 123 } } ... results in these Data Classes: data class JsonRoot ( val child : List < String >? , val num : Number?) data class Base ( val jsonRoot : JsonRoot?) Convert your JSON/XML/Yaml here . HTTP -> HTTP message builder \u00b6 Occasionally you have the raw version of an HTTP message and want to create an identical http4k HTTP message object from it. This tool converts a raw message to the syntax. For example: POST /example/index.html?query1=abc&query2=def HTTP/1.1 Host: toolbox.http4k.org Accept: image/gif, image/jpeg, */* Content-Type: text/plain hello from http4k ... results in this HTTP message builder code: fun request (): Request = Request ( Method . POST , \"/example/index.html\" ) . query ( \"query1\" , \"abc\" ) . query ( \"query2\" , \"def\" ) . header ( \"Host\" , \"toolbox.http4k.org\" ) . header ( \"Accept\" , \"image/gif, image/jpeg, */*\" ) . header ( \"Content-Type\" , \"text/plain\" ) . body ( \"\"\"hello from http4k\"\"\" ) Build HTTP messages from raw HTTP here . IntelliJ Live Templates \u00b6 One of our most favourite power features of IntelliJ is Live Templates, which allow you to add code generation macros to your IDE and activate them with a shortcut + tab. For http4k development, we got bored with typing things longhand, so decided to create a bunch of templates and share them with our favourite people (our users!). Even better, anyone can use our shortcuts in their IDE by adding a read-only \"Settings Repository\". Install the http4k live templates (and save your fingers!) by following the instructions at the bottom of this page . Wrapping up \u00b6 We hope you'll find the various tools that we've built into the Toolbox useful to either get started with http4k or to make your life even more productive. As ever, let us know how we're doing, and if there are any other tools that you think might be helpful then we'd love to hear about them!","title":"Toolbox: Guns for show, knives for a pro"},{"location":"blog/toolbox/#http4k_toolbox_guns_for_show_knives_for_a_pro","text":"","title":"http4k Toolbox: Guns for show, knives for a pro"},{"location":"blog/toolbox/#november_2020_daviddenton","text":"Over the years of creating apps with http4k, we've collated several tools that we use to turbo charge our development activities. While working on v4 of the library, and with a little time on our hands, we decided to bring all of these tools together into a single place so that all of our users could get their benefits. These tools are mostly based around code generation of some sort or another and are mainly used to shortcut repetitive or mundane development tasks that you might encounter when developing and testing applications with http4k. For development, we made extensive use of the excellent http4k web library (have you heard of it? it's really rather good!), as well as KotlinPoet and Bunting4k , a new CLI flags library that we created for this task. A lot of the credit for the creation of the Toolbox has to go to the amazing Albert Latacz , who not only created large parts of the functionality, but also investigated build and deployment options.","title":"november 2020 / @daviddenton"},{"location":"blog/toolbox/#web_toolbox","text":"The Toolbox is now the main starting point for working with http4k The main concept behind each of the functions is to allow the user to preview any generated code before they download it. We have also set up automated pipelines to update the Toolbox whenever we do a release of http4k so it should always be up to date. It's worth noting that the Toolbox is also entirely stateless, so no user content is recorded in any form. The http4k Toolbox is available @ toolbox.http4k.org","title":"Web toolbox"},{"location":"blog/toolbox/#cli","text":"The http4k team are massive fans of automation, so we also wanted to enable teams to use the functions of the Toolbox in a shell. Hence, we decided to also make a CLI binary for the Toolbox, which is available to install via both Brew and SDKMan . You can find instructions about how to install it on the Toolbox homepage .","title":"CLI"},{"location":"blog/toolbox/#toolbox_functionality_guide","text":"Where we introduce each of the functions of the Toolbox , with a rationale and a bit of background on each.","title":"Toolbox functionality guide"},{"location":"blog/toolbox/#generating_a_project","text":"This is quite a common one for various libraries, but they tend to be quite basic - and we thought we could do better. The http4k Project Generator is an intelligent wizard which allows the user to select their backend, extra modules, and build system. It then crunches all the code from these choices together into a coherent project configuration (called a \"Stack\"), and provides a reusable link for regenerating the same project configuration again. The questionnaire is dynamically generated by providing options for all 17+ supported Server and Serverless backends, and the further 30+ http4k integration modules, including examples of the different types of tests that you can write for apps. We can also add other \"features\" to be mixed in to the project structure. As well as downloading the project, you can also browse the generated code in a browser. Try out the project generator here .","title":"Generating a project"},{"location":"blog/toolbox/#openapi3_server_client_models","text":"http4k already supports generating OpenAPI3 specification documents from your code, but one thing that you often come across is when you need to generate an HTTP server or Client for someone else's application which is documented with using the same format. Initially we did look at the existing OpenAPI generator projects available for other libraries, but they are mostly based around static templates, and once again we thought we could do something a little more sophisticated. The OpenAPI generator . accepts a JSON or YAML specification (or URL), and generates: Data Classes for JSON API model schemas - The message objects are generated using a \"best guess\" algorithm from the JSON Schema information in the specification document, mapping the base JSON types to their Kotlin equivalents. Template http4k Server implementation containing a set of generated API endpoint functions and Lens implementations for all defined HTTP contract parts (path, query etc), as well as auto-marshalled JSON request and response bodies. Security implementations (API Key, Basic, Bearer) are also generated. http4k API client implementation with functions generated for each API endpoint. Lens implementations are generated and HTTP contract parts (path, query etc) are marshalled both into and out of the Request and Response messages. As well as downloading the files as a ZIP archive, you can also browse the generated code in a browser. The OpenAPI generator is available here .","title":"OpenAPI3 Server, Client, & Models"},{"location":"blog/toolbox/#generating_data_classes_from_json_xml_and_yaml","text":"Most of the time when working with API models in http4k, you have example messages in the native format which need to be auto-marshalled into Kotlin using Jackson or one of the other supported format libraries. This results in a very boring exercise in conversion, so we wrote this converter that will generate Data Classes to support reading/writing of JSON, XML and YAML messages. For example, this JSON... { \"jsonRoot\" : { \"child\" : [ \"hello\" , \"there\" ], \"num\" : 123 } } ... results in these Data Classes: data class JsonRoot ( val child : List < String >? , val num : Number?) data class Base ( val jsonRoot : JsonRoot?) Convert your JSON/XML/Yaml here .","title":"Generating Data Classes from JSON, XML and YAML"},{"location":"blog/toolbox/#http_-_http_message_builder","text":"Occasionally you have the raw version of an HTTP message and want to create an identical http4k HTTP message object from it. This tool converts a raw message to the syntax. For example: POST /example/index.html?query1=abc&query2=def HTTP/1.1 Host: toolbox.http4k.org Accept: image/gif, image/jpeg, */* Content-Type: text/plain hello from http4k ... results in this HTTP message builder code: fun request (): Request = Request ( Method . POST , \"/example/index.html\" ) . query ( \"query1\" , \"abc\" ) . query ( \"query2\" , \"def\" ) . header ( \"Host\" , \"toolbox.http4k.org\" ) . header ( \"Accept\" , \"image/gif, image/jpeg, */*\" ) . header ( \"Content-Type\" , \"text/plain\" ) . body ( \"\"\"hello from http4k\"\"\" ) Build HTTP messages from raw HTTP here .","title":"HTTP -> HTTP message builder"},{"location":"blog/toolbox/#intellij_live_templates","text":"One of our most favourite power features of IntelliJ is Live Templates, which allow you to add code generation macros to your IDE and activate them with a shortcut + tab. For http4k development, we got bored with typing things longhand, so decided to create a bunch of templates and share them with our favourite people (our users!). Even better, anyone can use our shortcuts in their IDE by adding a read-only \"Settings Repository\". Install the http4k live templates (and save your fingers!) by following the instructions at the bottom of this page .","title":"IntelliJ Live Templates"},{"location":"blog/toolbox/#wrapping_up","text":"We hope you'll find the various tools that we've built into the Toolbox useful to either get started with http4k or to make your life even more productive. As ever, let us know how we're doing, and if there are any other tools that you think might be helpful then we'd love to hear about them!","title":"Wrapping up"},{"location":"blog/typesafe_configuration/","text":"Add typesafe 12-factor configuration to http4k apps with Environments \u00b6 november 2018 / @daviddenton \u00b6 Intro \u00b6 This post covers the various concerns around configuring HTTP apps, and introduces the http4k approach for addressing these when deploying applications into cloud-native environments, which leverages the Kotlin type system for maximum safely and code reuse. Concerns when configuring applications \u00b6 One of the tenets of operating applications according to the principles of 12factor , and especially in containerised cloud-native apps, is to inject all app configuration through the use of environmental variables. Additionally, when using more restrictive settings (such as utilising JVM security manager policies or through the use of container images which don't provide an OS baseline) it may not be possible to read files (such as YAML, JSON etc) from disk, which reinforces this approach. There are several particular relevant concerns that we need to address, but overall the effect that we are looking for is that any misconfiguration of the application will result in it failing to startup. For this reason we want to reify all values to check them as soon as possible in the application bootstrap phase. 1. Optionality \u00b6 Kotlin's type system guards us against missing values being injected - for instance the following code will throw a IllegalStateException due to a typo in the parameter name: package blog.typesafe_configuration.pre // export ANTIDISESTABLISHMENTARIANISM=opposition to the disestablishment of the Church of England val definition : String = System . getenv ( \"ANTIDISESTABLISHMENTARIAMISM\" ) However not all configuration values will be required. We can define that there are 3 distinct modes of optionality available for each parameter: Required: These values must be injected for each environment, with no default value defined. Most configurations such as hostnames should always use this form to maximise operational safety. Optional: These values can be supplied, but there is no default value. This category fits well with dynamic properties which could be data-driven (ie. not known at compile-time). Defaulted: These values can be supplied, but a fallback value (or chain of other config values) will be used if they are not. Missing values should produce a reasonable error and stop the app from starting. 2. Type coercion \u00b6 Most applications will require a variety of configuration primitive types, which may or may not map to the Java/Kotlin standard types, including: strings such as service URLs, log levels, or AWS role names numeric values such as ports or retry counts booleans such as debug switch or feature flags duration values for timeouts, backoff times But handling these raw types alone is not enough to guarantee safety - it is best to marshall the values into a suitable operational/domain type that can validate the input and avoid confusion. Kotlin gives us a simple way to do this using require as a guard: package blog.typesafe_configuration.pre class Port ( val value : Int ) { init { require (( 1. . 65535 ). contains ( value )) { \"Out of range Port: ' $ value '\" } } } // export PORT=8000 val port = Port ( System . getenv ( \"PORT\" ). toInt ()) Additionally to the above, it is important to represent those values in a form that cannot be misinterpreted. A good example of this is the passing of temporal values as integers - timeouts defined this way could be easily be parsed into the wrong time unit (seconds instead of milliseconds). Using a higher level primitive such as Duration will help us here: package blog.typesafe_configuration.pre import java.time.Duration data class Timeout ( val value : Duration ) { init { require ( ! value . isNegative ) { \"Cannot have negative timeout\" } } } // export TIMEOUT=PT30S val timeout = Timeout ( Duration . parse ( System . getenv ( \"TIMEOUT\" ))) Obviously, the above is still not very safe - and what's more, a coercion could now fail with one of 3 different exceptions depending on if the value was missing ( IllegalStateException ), unparsable ( DateTimeParseException ) or invalid ( IllegalArgumentException ). The conversion code from String -> Duration must also be repeated (or extracted) for each value that we wish to parse. 3. Multiplicity \u00b6 Configuration parameters may have one or many values and need to be converted safely from the injected string representation (usually comma-separated) and into their internally represented types at application startup: package blog.typesafe_configuration.pre class Host ( val value : String ) // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val hosts = System . getenv ( \"HOSTNAMES\" ). split ( \",\" ). map { Host ( it . trim ()) } Once again, the splitting code will need to be repeated for each config value, or extracted to a library function. 4. Security \u00b6 The configuration of a standard app will generally contain both sensitive and non-sensitive values. Sensitive such as application secrets, DB passwords or API keys should be handled in a way that avoid storing directly in memory in a readable format or long lived fashion, where they may be inadvertently inspected or outputted into a log file. Dangling code situations such as in the code below are common, and are asking for trouble... package blog.typesafe_configuration.pre import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import java.nio.ByteBuffer val s3 = OkHttp () fun readFile ( secretKey : String , bucketKey : String ): ByteBuffer { return s3 ( Request ( GET , \"https://mybucket.s3.amazonaws.com/ $ bucketKey \" )). body . payload } // export AWS_SECRET_KEY=someSuperSecretValueThatOpsReallyDoNotWantYouToKnow val file = readFile ( System . getenv ( \"AWS_SECRET_KEY\" ), \"myfile.txt\" ) 5. Configuration Context & Overriding \u00b6 We also want to avoid defining all values for all possible scenarios - for example in test cases, so the ability to overlay configuration sets on top of each other is useful. Although it is against the rules of 12-factor, it is sometimes convenient to source parameter values from a variety of contexts when running applications in non-cloud environments: System Environment variables Properties files JAR resources Local files Source code defined environmental configuration Implementing this kind of fallback logic manually, you'd end up with code like the below: package blog.typesafe_configuration.pre val name = System . getProperty ( \"USERNAME\" ) ?: System . getenv ( \"USERNAME\" ) ?: \"DEFAULT_USER\" The http4k approach... \u00b6 There are already many options for configurational libraries written in Kotlin, but http4k also provides an option in the http4k-cloudnative add-on module which leverages the power of the Lens system already built into the core library to provide a consistent experience to API users. In case you're new to Lenses, here's a recap... Lenses - a recap \u00b6 In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. A Lens is an stateless object responsible for either the one-way (or Bidirectional) transformation of It defines type parameters representing input IN and output OUT types and implements one (for a Lens ) or both (for a BiDiLens ) of the following interfaces: LensExtractor - takes a value of type IN and extracts a value of type OUT LensInjector - takes a value of type IN and a value of type OUT and returns a modified value of type IN with the value injected into it. package blog.typesafe_configuration.post interface LensExtractor < in IN , out OUT > : ( IN ) -> OUT { override operator fun invoke ( target : IN ): OUT } interface LensInjector < in IN , in OUT > { operator fun < R : OUT > invoke ( value : IN , target : R ): R } interface Lens < IN , OUT > : LensExtractor < IN , OUT > interface BiDiLens < IN , OUT > : Lens < IN , OUT > , LensInjector < IN , OUT > The creation of a Lens consists of 4 main concerns: targeting determines where the Lens expects to extract and inject the values from/to, which can consist of both an overall target and a name within that target. multiplicity handling which defines how many of a particular value we are attempting to handle. the transformation chain of function composition which forms a specification for converting one type to another. This is done in code using the map() method defined on the Lens. the optionality of a Lens denotes the behaviour if/when a value cannot be found in the target. To define a Lens instance through the http4k Lens API, we take an initial target specification, decide it's multiplicity , provide any transformations with map() , and finally reify the specification into a Lens instance by deciding it's optionality. It sounds involved, but it is consistent and the fluent API has been designed to make it simpler. By way of an example, here we define a bi-directional Lens for custom type Page , extracted from a querystring value and defaulting to Page 1. package blog.typesafe_configuration.post import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.lens.BiDiLens import org.http4k.lens.Query import org.http4k.lens.int data class Page ( val value : Int ) { init { require ( value > 1 ) { \"Page number must be positive\" } } } val lens : BiDiLens < Request , Page > = Query . int (). map ( :: Page , Page :: value ). defaulted ( \"pageNumber\" , Page ( 1 )) val pageNumber : Page = lens ( Request ( GET , \"http://abc/search?pageNumber=55\" )) val updatedRequest : Request = lens ( pageNumber , Request ( GET , \"http://abc/search\" )) In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. http4k Environments \u00b6 in http4k, an Environment object is a context which holds configuration values. It effectively behaves like a Map , in that it can be composed with other Environment objects to provide a consolidated view of all of it's component values. package blog.typesafe_configuration.post import org.http4k.config.Environment import java.io.File val systemEnv : Environment = Environment . ENV val jvmFlags : Environment = Environment . JVM_PROPERTIES val jar : Environment = Environment . fromResource ( \"jar.properties\" ) val filesystem : Environment = Environment . from ( File ( \"fs.properties\" )) val codeBased : Environment = Environment . from ( \"key1\" to \"value1\" , \"key2\" to \"value2\" ) val consolidated : Environment = jvmFlags overrides systemEnv overrides codeBased overrides filesystem overrides jar If you're using any of the other Kotlin-based configuration libraries, the above should look pretty familiar. The difference starts to become apparent when attempting to retrieve values from the Environment instance. This is done using EnviromentKey Lenses, which are an extension of the http4k Lens system that specifically targets Environment objects. package blog.typesafe_configuration.post import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.lens.Lens import org.http4k.lens.duration import org.http4k.lens.int import java.time.Duration data class Timeout ( val value : Duration ) data class Port ( val value : Int ) data class Host ( val value : String ) // export TIMEOUT=PT30S // export PORT=8080 // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val env = Environment . ENV val portLens : Lens < Environment , Port > = EnvironmentKey . int (). map ( :: Port ). defaulted ( \"PORT\" , Port ( 9000 )) val timeoutLens : Lens < Environment , Timeout?> = EnvironmentKey . duration (). map ( :: Timeout ). optional ( \"TIMEOUT\" ) val hostsLens : Lens < Environment , List < Host >> = EnvironmentKey . map ( :: Host ). multi . required ( \"HOSTNAMES\" ) val timeout : Timeout? = timeoutLens ( env ) val port : Port = portLens ( env ) val hosts : List < Host > = hostsLens ( env ) Handling failure \u00b6 When using the http4k Environment to define config, missing or values which cannot be deserialised all now cause a LensFailure to be thrown with a descriptive error message. As before, this results in the application failing to start, but as the exception if both consistent and explicit, diagnosing the problem becomes much simpler. Single-shot Secrets \u00b6 In order to avoid the accidental exposure of sensitive information such as passwords into the application runtime, a new type Secret has been introduced, which tries as much as possible to avoid exposing it's internal value as a readable String . The Secret class is designed to only have the string version of it's value read once, and only within a specific use() block, after which the underlying value is internally overwritten and further attempts to read it throw an IllegalStateException . The typical use-case for this block is to set-up a SQL Datasource or to create a Filter which adds authentication to all outbound requests, as in the example below: package blog.typesafe_configuration.post import org.http4k.client.OkHttp import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.lens.Lens import org.http4k.lens.secret // export USER_PASSWORD=12345 val accessToken : Lens < Environment , Secret > = EnvironmentKey . secret (). required ( \"USER_PASSWORD\" ) val secret : Secret = accessToken ( Environment . ENV ) val authFilter : Filter = secret . use { value : String -> ServerFilters . BearerAuth ( value ) } val authedHttp : HttpHandler = authFilter . then ( OkHttp ()) As with other supported primitives, Secret is available by default in all supported Lens Locations.","title":"Typesafe 12-factor config"},{"location":"blog/typesafe_configuration/#add_typesafe_12-factor_configuration_to_http4k_apps_with_environments","text":"","title":"Add typesafe 12-factor configuration to http4k apps with Environments"},{"location":"blog/typesafe_configuration/#november_2018_daviddenton","text":"","title":"november 2018 / @daviddenton"},{"location":"blog/typesafe_configuration/#intro","text":"This post covers the various concerns around configuring HTTP apps, and introduces the http4k approach for addressing these when deploying applications into cloud-native environments, which leverages the Kotlin type system for maximum safely and code reuse.","title":"Intro"},{"location":"blog/typesafe_configuration/#concerns_when_configuring_applications","text":"One of the tenets of operating applications according to the principles of 12factor , and especially in containerised cloud-native apps, is to inject all app configuration through the use of environmental variables. Additionally, when using more restrictive settings (such as utilising JVM security manager policies or through the use of container images which don't provide an OS baseline) it may not be possible to read files (such as YAML, JSON etc) from disk, which reinforces this approach. There are several particular relevant concerns that we need to address, but overall the effect that we are looking for is that any misconfiguration of the application will result in it failing to startup. For this reason we want to reify all values to check them as soon as possible in the application bootstrap phase.","title":"Concerns when configuring applications"},{"location":"blog/typesafe_configuration/#1_optionality","text":"Kotlin's type system guards us against missing values being injected - for instance the following code will throw a IllegalStateException due to a typo in the parameter name: package blog.typesafe_configuration.pre // export ANTIDISESTABLISHMENTARIANISM=opposition to the disestablishment of the Church of England val definition : String = System . getenv ( \"ANTIDISESTABLISHMENTARIAMISM\" ) However not all configuration values will be required. We can define that there are 3 distinct modes of optionality available for each parameter: Required: These values must be injected for each environment, with no default value defined. Most configurations such as hostnames should always use this form to maximise operational safety. Optional: These values can be supplied, but there is no default value. This category fits well with dynamic properties which could be data-driven (ie. not known at compile-time). Defaulted: These values can be supplied, but a fallback value (or chain of other config values) will be used if they are not. Missing values should produce a reasonable error and stop the app from starting.","title":"1. Optionality"},{"location":"blog/typesafe_configuration/#2_type_coercion","text":"Most applications will require a variety of configuration primitive types, which may or may not map to the Java/Kotlin standard types, including: strings such as service URLs, log levels, or AWS role names numeric values such as ports or retry counts booleans such as debug switch or feature flags duration values for timeouts, backoff times But handling these raw types alone is not enough to guarantee safety - it is best to marshall the values into a suitable operational/domain type that can validate the input and avoid confusion. Kotlin gives us a simple way to do this using require as a guard: package blog.typesafe_configuration.pre class Port ( val value : Int ) { init { require (( 1. . 65535 ). contains ( value )) { \"Out of range Port: ' $ value '\" } } } // export PORT=8000 val port = Port ( System . getenv ( \"PORT\" ). toInt ()) Additionally to the above, it is important to represent those values in a form that cannot be misinterpreted. A good example of this is the passing of temporal values as integers - timeouts defined this way could be easily be parsed into the wrong time unit (seconds instead of milliseconds). Using a higher level primitive such as Duration will help us here: package blog.typesafe_configuration.pre import java.time.Duration data class Timeout ( val value : Duration ) { init { require ( ! value . isNegative ) { \"Cannot have negative timeout\" } } } // export TIMEOUT=PT30S val timeout = Timeout ( Duration . parse ( System . getenv ( \"TIMEOUT\" ))) Obviously, the above is still not very safe - and what's more, a coercion could now fail with one of 3 different exceptions depending on if the value was missing ( IllegalStateException ), unparsable ( DateTimeParseException ) or invalid ( IllegalArgumentException ). The conversion code from String -> Duration must also be repeated (or extracted) for each value that we wish to parse.","title":"2. Type coercion"},{"location":"blog/typesafe_configuration/#3_multiplicity","text":"Configuration parameters may have one or many values and need to be converted safely from the injected string representation (usually comma-separated) and into their internally represented types at application startup: package blog.typesafe_configuration.pre class Host ( val value : String ) // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val hosts = System . getenv ( \"HOSTNAMES\" ). split ( \",\" ). map { Host ( it . trim ()) } Once again, the splitting code will need to be repeated for each config value, or extracted to a library function.","title":"3. Multiplicity"},{"location":"blog/typesafe_configuration/#4_security","text":"The configuration of a standard app will generally contain both sensitive and non-sensitive values. Sensitive such as application secrets, DB passwords or API keys should be handled in a way that avoid storing directly in memory in a readable format or long lived fashion, where they may be inadvertently inspected or outputted into a log file. Dangling code situations such as in the code below are common, and are asking for trouble... package blog.typesafe_configuration.pre import org.http4k.client.OkHttp import org.http4k.core.Method.GET import org.http4k.core.Request import java.nio.ByteBuffer val s3 = OkHttp () fun readFile ( secretKey : String , bucketKey : String ): ByteBuffer { return s3 ( Request ( GET , \"https://mybucket.s3.amazonaws.com/ $ bucketKey \" )). body . payload } // export AWS_SECRET_KEY=someSuperSecretValueThatOpsReallyDoNotWantYouToKnow val file = readFile ( System . getenv ( \"AWS_SECRET_KEY\" ), \"myfile.txt\" )","title":"4. Security"},{"location":"blog/typesafe_configuration/#5_configuration_context_overriding","text":"We also want to avoid defining all values for all possible scenarios - for example in test cases, so the ability to overlay configuration sets on top of each other is useful. Although it is against the rules of 12-factor, it is sometimes convenient to source parameter values from a variety of contexts when running applications in non-cloud environments: System Environment variables Properties files JAR resources Local files Source code defined environmental configuration Implementing this kind of fallback logic manually, you'd end up with code like the below: package blog.typesafe_configuration.pre val name = System . getProperty ( \"USERNAME\" ) ?: System . getenv ( \"USERNAME\" ) ?: \"DEFAULT_USER\"","title":"5. Configuration Context & Overriding"},{"location":"blog/typesafe_configuration/#the_http4k_approach","text":"There are already many options for configurational libraries written in Kotlin, but http4k also provides an option in the http4k-cloudnative add-on module which leverages the power of the Lens system already built into the core library to provide a consistent experience to API users. In case you're new to Lenses, here's a recap...","title":"The http4k approach..."},{"location":"blog/typesafe_configuration/#lenses_-_a_recap","text":"In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts. A Lens is an stateless object responsible for either the one-way (or Bidirectional) transformation of It defines type parameters representing input IN and output OUT types and implements one (for a Lens ) or both (for a BiDiLens ) of the following interfaces: LensExtractor - takes a value of type IN and extracts a value of type OUT LensInjector - takes a value of type IN and a value of type OUT and returns a modified value of type IN with the value injected into it. package blog.typesafe_configuration.post interface LensExtractor < in IN , out OUT > : ( IN ) -> OUT { override operator fun invoke ( target : IN ): OUT } interface LensInjector < in IN , in OUT > { operator fun < R : OUT > invoke ( value : IN , target : R ): R } interface Lens < IN , OUT > : LensExtractor < IN , OUT > interface BiDiLens < IN , OUT > : Lens < IN , OUT > , LensInjector < IN , OUT > The creation of a Lens consists of 4 main concerns: targeting determines where the Lens expects to extract and inject the values from/to, which can consist of both an overall target and a name within that target. multiplicity handling which defines how many of a particular value we are attempting to handle. the transformation chain of function composition which forms a specification for converting one type to another. This is done in code using the map() method defined on the Lens. the optionality of a Lens denotes the behaviour if/when a value cannot be found in the target. To define a Lens instance through the http4k Lens API, we take an initial target specification, decide it's multiplicity , provide any transformations with map() , and finally reify the specification into a Lens instance by deciding it's optionality. It sounds involved, but it is consistent and the fluent API has been designed to make it simpler. By way of an example, here we define a bi-directional Lens for custom type Page , extracted from a querystring value and defaulting to Page 1. package blog.typesafe_configuration.post import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.lens.BiDiLens import org.http4k.lens.Query import org.http4k.lens.int data class Page ( val value : Int ) { init { require ( value > 1 ) { \"Page number must be positive\" } } } val lens : BiDiLens < Request , Page > = Query . int (). map ( :: Page , Page :: value ). defaulted ( \"pageNumber\" , Page ( 1 )) val pageNumber : Page = lens ( Request ( GET , \"http://abc/search?pageNumber=55\" )) val updatedRequest : Request = lens ( pageNumber , Request ( GET , \"http://abc/search\" )) In http4k, Lenses are typically used to provide typesafe conversion of typed values into and out of HTTP messages, although this concept has been extended within the http4k ecosystem to support that of a form handling and request contexts.","title":"Lenses - a recap"},{"location":"blog/typesafe_configuration/#http4k_environments","text":"in http4k, an Environment object is a context which holds configuration values. It effectively behaves like a Map , in that it can be composed with other Environment objects to provide a consolidated view of all of it's component values. package blog.typesafe_configuration.post import org.http4k.config.Environment import java.io.File val systemEnv : Environment = Environment . ENV val jvmFlags : Environment = Environment . JVM_PROPERTIES val jar : Environment = Environment . fromResource ( \"jar.properties\" ) val filesystem : Environment = Environment . from ( File ( \"fs.properties\" )) val codeBased : Environment = Environment . from ( \"key1\" to \"value1\" , \"key2\" to \"value2\" ) val consolidated : Environment = jvmFlags overrides systemEnv overrides codeBased overrides filesystem overrides jar If you're using any of the other Kotlin-based configuration libraries, the above should look pretty familiar. The difference starts to become apparent when attempting to retrieve values from the Environment instance. This is done using EnviromentKey Lenses, which are an extension of the http4k Lens system that specifically targets Environment objects. package blog.typesafe_configuration.post import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.lens.Lens import org.http4k.lens.duration import org.http4k.lens.int import java.time.Duration data class Timeout ( val value : Duration ) data class Port ( val value : Int ) data class Host ( val value : String ) // export TIMEOUT=PT30S // export PORT=8080 // export HOSTNAMES=eu-west1.aws.com,eu-west2.aws.com,eu-west3.aws.com val env = Environment . ENV val portLens : Lens < Environment , Port > = EnvironmentKey . int (). map ( :: Port ). defaulted ( \"PORT\" , Port ( 9000 )) val timeoutLens : Lens < Environment , Timeout?> = EnvironmentKey . duration (). map ( :: Timeout ). optional ( \"TIMEOUT\" ) val hostsLens : Lens < Environment , List < Host >> = EnvironmentKey . map ( :: Host ). multi . required ( \"HOSTNAMES\" ) val timeout : Timeout? = timeoutLens ( env ) val port : Port = portLens ( env ) val hosts : List < Host > = hostsLens ( env )","title":"http4k Environments"},{"location":"blog/typesafe_configuration/#handling_failure","text":"When using the http4k Environment to define config, missing or values which cannot be deserialised all now cause a LensFailure to be thrown with a descriptive error message. As before, this results in the application failing to start, but as the exception if both consistent and explicit, diagnosing the problem becomes much simpler.","title":"Handling failure"},{"location":"blog/typesafe_configuration/#single-shot_secrets","text":"In order to avoid the accidental exposure of sensitive information such as passwords into the application runtime, a new type Secret has been introduced, which tries as much as possible to avoid exposing it's internal value as a readable String . The Secret class is designed to only have the string version of it's value read once, and only within a specific use() block, after which the underlying value is internally overwritten and further attempts to read it throw an IllegalStateException . The typical use-case for this block is to set-up a SQL Datasource or to create a Filter which adds authentication to all outbound requests, as in the example below: package blog.typesafe_configuration.post import org.http4k.client.OkHttp import org.http4k.config.Environment import org.http4k.config.EnvironmentKey import org.http4k.config.Secret import org.http4k.core.Filter import org.http4k.core.HttpHandler import org.http4k.core.then import org.http4k.filter.ServerFilters import org.http4k.lens.Lens import org.http4k.lens.secret // export USER_PASSWORD=12345 val accessToken : Lens < Environment , Secret > = EnvironmentKey . secret (). required ( \"USER_PASSWORD\" ) val secret : Secret = accessToken ( Environment . ENV ) val authFilter : Filter = secret . use { value : String -> ServerFilters . BearerAuth ( value ) } val authedHttp : HttpHandler = authFilter . then ( OkHttp ()) As with other supported primitives, Secret is available by default in all supported Lens Locations.","title":"Single-shot Secrets"},{"location":"blog/typesafe_websockets/","text":"Websockets. But typesafe. And testable. Without the Server. \u00b6 december 2017 / @daviddenton \u00b6 Reaction to the last post introducing http4k was pretty good, and one of the most popular questions was: \"But what about Websockets\" ? The answer to that question at the time was an emphatic \"Not yet\" - because they didn't fit the \"Server as a Function\" model, and the team hadn't worked out a way to deliver them in a simple, offline testable* way. Well, a month is a long time, and we've been beavering away, so now we're thrilled to release Websockets for http4k , which are: Simple : using the same style of API as the rest of http4k, allowing the same dynamic path-based routing as is available for standard HttpHandlers . Typesafe : Marshall and unmarshall typed objects from Websocket Messages using the established Lens API. Testable : This is something that is massively important to us - and just like standard HttpHandlers, http4k Websockets are completely testable in a synchronous online or offline environment. No. Server. Required. Details schmeetails... \u00b6 Just as with HttpHandlers, the here are 2 basic function types which make up the core of the Websocket routing API: A WsHandler - represented as a typealias: (Request) -> WsConsumer? . This is responsible for matching an incoming HTTP upgrade request to a websocket. WsConsumer - represented as a typealias: (WebSocket) -> Unit . This function is called on connection and allow the API user to react to events coming from the connected Websocket by attaching listeners. Additionally, WsMessage objects are used for actual communication - ie. a message which is sent or received on a Websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. And just like the http4k HTTP message model, WsMessages are immutable data classes . An example server \u00b6 The example below shows how: Websockets can be dynamically routed Lens-based marshalling of Websocket message objects using Jackson. WsHandler can be combined with an HttpHandler to make a PolyHandler - an application which can serve many protocols. Conversion of the PolyHandler to a supporting Server can be done via the standard asServer() mechanism, or it can be kept offline for ultra-fast in-memory testing: package blog.typesafe_websockets import org.http4k.core.HttpHandler import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson.auto import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse // in json, this looks like: {\"value\": 123, \"currency: \"EUR\" } data class Money ( val value : Int , val currency : String ) fun main () { // we use the Lens API to convert between the WsMessage and the Money instance, and to // dynamically bind the \"name\" from the path val moneyLens = WsMessage . auto < Money > (). toLens () val nameLens = Path . of ( \"name\" ) // the routing API is virtually identical to the standard http4k http routing API. // on connection, the bound WsConsumer is called with the Websocket instance val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . onMessage { val received = moneyLens ( it ) ws . send ( moneyLens ( received )) } ws . onClose { println ( \"closed\" ) } ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) val http : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } // the poly-handler can serve both http and ws protocols. PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start (). block () } Alternatively, you can check out the Websocket enabled http4k demo: IRC clone in 30 lines of Kotlin . Testability \u00b6 As well as API simplicity, the http4k team are very passionate about testing, and it was very important that this could be done in an out-of-container fashion - ie. in memory and with no server being started. As such, it is possible to call testWsClient() on an WsHandler to provide a synchronous API for testing. Messages and other events can be \"sent\" to a connected websocket and responses will be received back in a completely predictable way from the application under test. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package blog.typesafe_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } } Fin \u00b6 Websocket support is now available for the Jetty server backend in http4k v3.2.0 . We plan to roll out support for other server-backends in due course. Have a play a let us know what you think... Footnotes \u00b6 * We had a bit of a search for \"unit testing websockets\", half through curiosity and half because we wanted to swipe other people's ideas for implementing it. But we came up with nothing - it seems like all the existing JVM HTTP libraries rely on running servers for testing websockets. We hope we're wrong - because the alternative makes us a little . If we are, then please let us know! \ud83d\ude1d","title":"Typesafe Websockets"},{"location":"blog/typesafe_websockets/#websockets_but_typesafe_and_testable_without_the_server","text":"","title":"Websockets. But typesafe. And testable. Without the Server."},{"location":"blog/typesafe_websockets/#december_2017_daviddenton","text":"Reaction to the last post introducing http4k was pretty good, and one of the most popular questions was: \"But what about Websockets\" ? The answer to that question at the time was an emphatic \"Not yet\" - because they didn't fit the \"Server as a Function\" model, and the team hadn't worked out a way to deliver them in a simple, offline testable* way. Well, a month is a long time, and we've been beavering away, so now we're thrilled to release Websockets for http4k , which are: Simple : using the same style of API as the rest of http4k, allowing the same dynamic path-based routing as is available for standard HttpHandlers . Typesafe : Marshall and unmarshall typed objects from Websocket Messages using the established Lens API. Testable : This is something that is massively important to us - and just like standard HttpHandlers, http4k Websockets are completely testable in a synchronous online or offline environment. No. Server. Required.","title":"december 2017 / @daviddenton"},{"location":"blog/typesafe_websockets/#details_schmeetails","text":"Just as with HttpHandlers, the here are 2 basic function types which make up the core of the Websocket routing API: A WsHandler - represented as a typealias: (Request) -> WsConsumer? . This is responsible for matching an incoming HTTP upgrade request to a websocket. WsConsumer - represented as a typealias: (WebSocket) -> Unit . This function is called on connection and allow the API user to react to events coming from the connected Websocket by attaching listeners. Additionally, WsMessage objects are used for actual communication - ie. a message which is sent or received on a Websocket. This message can take advantage of the typesafety accorded to other entities in http4k by using the Lens API. And just like the http4k HTTP message model, WsMessages are immutable data classes .","title":"Details schmeetails..."},{"location":"blog/typesafe_websockets/#an_example_server","text":"The example below shows how: Websockets can be dynamically routed Lens-based marshalling of Websocket message objects using Jackson. WsHandler can be combined with an HttpHandler to make a PolyHandler - an application which can serve many protocols. Conversion of the PolyHandler to a supporting Server can be done via the standard asServer() mechanism, or it can be kept offline for ultra-fast in-memory testing: package blog.typesafe_websockets import org.http4k.core.HttpHandler import org.http4k.core.Request import org.http4k.core.Response import org.http4k.core.Status.Companion.OK import org.http4k.format.Jackson.auto import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Jetty import org.http4k.server.PolyHandler import org.http4k.server.asServer import org.http4k.websocket.Websocket import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse // in json, this looks like: {\"value\": 123, \"currency: \"EUR\" } data class Money ( val value : Int , val currency : String ) fun main () { // we use the Lens API to convert between the WsMessage and the Money instance, and to // dynamically bind the \"name\" from the path val moneyLens = WsMessage . auto < Money > (). toLens () val nameLens = Path . of ( \"name\" ) // the routing API is virtually identical to the standard http4k http routing API. // on connection, the bound WsConsumer is called with the Websocket instance val ws : WsHandler = websockets ( \"/hello\" bind websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws : Websocket -> val name = nameLens ( req ) ws . onMessage { val received = moneyLens ( it ) ws . send ( moneyLens ( received )) } ws . onClose { println ( \"closed\" ) } ws . send ( WsMessage ( \"hello $ name \" )) } } ) ) val http : HttpHandler = { _ : Request -> Response ( OK ). body ( \"hiya world\" ) } // the poly-handler can serve both http and ws protocols. PolyHandler ( http , ws ). asServer ( Jetty ( 9000 )). start (). block () } Alternatively, you can check out the Websocket enabled http4k demo: IRC clone in 30 lines of Kotlin .","title":"An example server"},{"location":"blog/typesafe_websockets/#testability","text":"As well as API simplicity, the http4k team are very passionate about testing, and it was very important that this could be done in an out-of-container fashion - ie. in memory and with no server being started. As such, it is possible to call testWsClient() on an WsHandler to provide a synchronous API for testing. Messages and other events can be \"sent\" to a connected websocket and responses will be received back in a completely predictable way from the application under test. In the below example, we have gone one step further - defining a contract test case and then providing 2 implementations of it - one for unit-testing (in memory), one using a server. http4k provides clients with an identical interface for both cases, meaning it's possible reuse the same test logic: package blog.typesafe_websockets import com.natpryce.hamkrest.assertion.assertThat import com.natpryce.hamkrest.equalTo import org.http4k.client.WebsocketClient import org.http4k.core.Method.GET import org.http4k.core.Request import org.http4k.core.Uri import org.http4k.lens.Path import org.http4k.routing.websockets import org.http4k.routing.ws.bind import org.http4k.server.Undertow import org.http4k.server.asServer import org.http4k.testing.testWsClient import org.http4k.websocket.WsClient import org.http4k.websocket.WsHandler import org.http4k.websocket.WsMessage import org.http4k.websocket.WsResponse import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test val namePath = Path . of ( \"name\" ) // here is our websocket app - it uses dynamic path binding and lenses val testApp : WsHandler = websockets ( \"/{name}\" bind { req : Request -> WsResponse { ws -> val name = namePath ( req ) ws . send ( WsMessage ( \"hello $ name \" )) } } ) // this is the abstract contract that defines the behaviour to be tested abstract class WebsocketContract { // subclasses only have to supply a blocking WsClient abstract fun client (): WsClient @Test fun `echoes back connected name` () { assertThat ( client (). received (). take ( 1 ). toList (), equalTo ( listOf ( WsMessage ( \"hello bob\" ))) ) } } // a unit test version of the contract - it connects to the websocket in memory with no network class WebsocketUnitTest : WebsocketContract () { override fun client () = testApp . testWsClient ( Request ( GET , \"/bob\" )) } // a integration test version of the contract - it starts a server and connects to the websocket over the network class WebsocketServerTest : WebsocketContract () { override fun client () = WebsocketClient . blocking ( Uri . of ( \"ws://localhost:8000/bob\" )) private val server = testApp . asServer ( Undertow ( 8000 )) @BeforeEach fun before () { server . start () } @AfterEach fun after () { server . stop () } }","title":"Testability"},{"location":"blog/typesafe_websockets/#fin","text":"Websocket support is now available for the Jetty server backend in http4k v3.2.0 . We plan to roll out support for other server-backends in due course. Have a play a let us know what you think...","title":"Fin"},{"location":"blog/typesafe_websockets/#footnotes","text":"* We had a bit of a search for \"unit testing websockets\", half through curiosity and half because we wanted to swipe other people's ideas for implementing it. But we came up with nothing - it seems like all the existing JVM HTTP libraries rely on running servers for testing websockets. We hope we're wrong - because the alternative makes us a little . If we are, then please let us know! \ud83d\ude1d","title":"Footnotes"},{"location":"changelog/","text":"Changelog This list is not intended to be all-encompassing - it will document major and breaking API changes with their rationale when appropriate: v5.28.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Include Vary header on CORS responses. H/T @ollieabbey http4k-multipart : [Fix Break] Multipart form files were all calling deleteOnExit() instead of being deleted when the Body is closed. Possible memory leak for long running processes. The fix MAY be a change of OS files-system usage if you are not closing your MultiPart form body. #H/T @oharaandrew314 for the report. v5.27.0.0 \u00b6 http4k- * : Upgrade some dependency versions including Kotlin to 2.0.10 http4k-core New HTTP status codes. H/T @torfinnberset http4k-core Added helper method for dealing with forms. H/T @tim-mortimer http4k-core [Fix] Close backing DiskLocation when MultipartForm closed. H/T @oharaandrew314 http4k-testing-kotest Fix haveSetCookie and haveCookie to work when cookie isn't present. H/T @bagguley v5.26.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Fix transformer is lost when adding name suffix to Approver H/T @ilya.aliaksandrovich v5.26.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Response caching extensions. H/T @ollieabbey http4k-config : [New Module!] Extraction of typesafe configuration module from http4k-cloudnative. http4k-cloudnative : [Breaking!] Repackaging of typesafe configuration module classes (org.http4k.cloudnative.env) to http4k-config (org.http4k.config). New imports are required. http4k-contract : Adds ApiKeySecurity that identifies a consumer and makes it available for later use. H/T @dhs3000 v5.25.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Breaking Fix] Incorrect lambda request context variable is passed - we now pass the incoming reqeust object instead of the converted http4k request. If you were using the LAMBDA_REQUEST_KEY, you can just use the request passed into the handler instead. v5.24.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda * : [Fix] AWS adapter throws on invalid URLs. http4k-testing-webdriver : [Fix] Base path replacement logic for same-dir-path and dot-path URLs. H/T jweidler v5.24.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Allow removal of all queries for a URI. H/T @dhs3000 http4k-format-kondor : Upgrade to new version of Kondor. H/T @uberto http4k-testing-strikt [Break] The upgrade to the latest version drops Java <17 support. If you are still using Java 8, you will need to stick with the previous version of this module. v5.23.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : [Approval test break] Addition of \"nullable\" field to every model property. This improves JSON output compatability with various tooling for generating types from the definitions. v5.22.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [Break] Move classes to alternative package to not clash with existing format objects. v5.21.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-dataframe : [New module] Support for KotlinX DataFrame. v5.21.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-multipart * : [Fix #1113] Disk-backed multipart form field data is now cleaned up when the body is closed, including the parent form directory. v5.21.0.0 \u00b6 http4k- * : Upgrade some dependency versions including Kotlin to v2 http4k-testing-chaos : [Breaking] Changed Trigger to be a fun interface instead of a typealias. Should be no-op or a simple fix to the type. http4k-core : [Possible Break] Renamed CachingFilters.Request/Response to CachingFilters.CacheRequest/CacheResponse . If you have imports then they may break and need to be updated. v5.20.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-chaos * : [Unlikely break]: remove Hamkrest dependency so that it does not appear randomly in your projects. If you were accidentally relying on this it will need to be re-added manually. v5.19.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.24 http4k-core : Add support for the timesource typealias () -> Instant where a Clock is used. H/T @kwydler v5.18.2.0 \u00b6 http4k-core : Add convenience methods to read bodies from HttpMessages as JSON/XML/CSV etc.. request.json() v5.18.1.0 \u00b6 http4k-core : Add convenience methods to set common headers to HTTP message. v5.18.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Behaviour break] We now do not set the legacy Expires header in CachingFilters. Modern caches should use the Cache-Control header instead (max-age). v5.17.0.0 \u00b6 http4k- * : Tweaks to make the K2 compiler happy http4k : Added convenience methods to set the body of an HTTP message. The works for both standard body types and with automarshallers. http4k-core : Fix request source in SunHttp. H/T @dkandalov http4k-contract : Added top-level MetadataRetrieval to schema objects. H/T @BBB http4k-format- * : [Unlikely break] rename with() functions on auto-marshallers to match content type, so you can now do req.json(myObj) and get the content type and body set in one go. Likewise for other content types v5.16.2.0 \u00b6 http4k- * : Upgrade some dependency versions. v5.16.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Add support for surrogate-key headers in EtagSupport. H/T @jason-annadani-springer v5.16.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : [Unlikely break] Rename typo in an ApprovalSource instance http4k-testing-approval : Addition of optional suffix to the approval file name, and added ApprovalTransformer for varying the compared content from the InputStream http4k-core : [Fix #1084] Route name without a beginning / works for everything except static resources. H/T @ArthurS1 v5.15.1.0 \u00b6 http4k-core : [Unlikely break] Change to Meta to remove default params http4k-testing-approval : Add ability to add a suffix to the approval file name. v5.15.0.0 \u00b6 http4k-core : [Unlikely break] Change to Meta to remove default params v5.14.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Make Lenses support metadata passed through the LensBuilder construction methods. H/T @BBB, @ivanmoore @jack-bolles http4k-testing-tracerbullet : Account for spans across traces with same spanId. H/T @IvanPavlov1995 v5.14.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov v5.14.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-tracerbullet : Improve identification of actor for incoming traces. http4k-client-helidon : Various fixes H/T @dkandalov v5.14.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Validator optimisation. H/T @dkandalov http4k-testing-webdriver : Adding a space between method name and URI when naming spans H/T @ReinholdsB http4k-testing-webdriver : Multipart forms in the webdriver, including sending files. H/T @gypsydave5 http4k-testing-webdriver : Fix bug in webdriver form submission + a method for relative Uri resolution. H/T @gypsydave5 v5.14.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.23 http4k- * : Static handlers serve an index.html file from a subdirectory. H/T @mbcltd v5.13.9.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Implement Kondor Schema creator. H/T @tamj0rd2 http4k-cloudnative : Read environment properties from yaml resources. H/T @dzappold http4k-webdriver : [Fix] Bug when submitting with inputs of type submit. H/T @gypsydave5 http4k-testing-approval : Allow adding a suffix to an approval test file name. H/T @becky-sequence v5.13.8.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-format-kondor : Expose converterFor method. H/T @tamj0rd2 v5.13.7.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Allow user to provide schema creation implementation. H/T @tamj0rd2 http4k-core : [Fix #1053]: Add BiDiLensSpec defaulted with factory method http4k-core : [Fix #1059]: Update kondor-json to 2.2.2. H/T @asadmanji v5.13.6.1 \u00b6 http4k-core : FollowRedirects also sets port on redirect. v5.13.6.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-serverless-lambda :[Fix #1057] Error when parsing AWS lambda event from S3 bucket http4k-testing-webdriver :[Fix #1050] Http4kWebDriver does not work on Windows due to path issues. H/T @cmh-dev http4k-core :[Fix #1055] Host header should contain host with port. H/T @obecker v5.13.5.0 \u00b6 http4k-client-core : Ensure consistent content-length behaviour across clients http4k-client-apache : Ensure consistent content-length behaviour across clients http4k-client-apache4 : Ensure consistent content-length behaviour across clients http4k-client-fuel : Ensure consistent content-length behaviour across clients http4k-client-jetty : Ensure consistent content-length behaviour across clients v5.13.4.1 \u00b6 http4k- * : Fix broken POM dependencies. v5.13.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract : Support for data4k progressive data models with field metadata via delegate properties v5.13.3.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-cloudnative * : Ability to override separator in Environment . v5.13.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract * : [Fix] Enums do not pick up custom prefixes in model naming. H/T @ashcor for the tip-off! http4k-opentelemetry * : [Fix] Fix to set HTTP_REQUEST_BODY_SIZE attribute in OpenTelemetryTracing. H/T @dkandalov http4k-contract * : Added Canonical model-namer. v5.13.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-client-helidon : [Fix #1037] Improve support for query parameters. H/T @franckrasolo v5.13.0.1 \u00b6 http4k-testing-tracerbullet : [Fix] Mermaid sequence diagram generation was constantly changing by default editorconfig files and people committing with different IDE settings http4k-server-jetty : [Fix #1023] Header values in quotes lose their quotes. H/T @efasel, @dhs3000 v5.13.0.0 \u00b6 http4k-format-jade4j : [Breaking] This module has been renamed due to the library Jade4J becoming Pug4J. Migration should be a no-op apart from switching the imported module, and renaming your templates from .jade to .pug. Please see Pug4j docs for anything else. http4k-format-pug4j : [New module] Replacement for Jade4j v5.12.2.1 \u00b6 http4k-webhooks : Move VerifyWebhookSignature filter to ServerFilters as it's not for HTTP clients! v5.12.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [New module!] Support for the Standard Webhooks format http4k-core [Fix #1022] For a request with matching if-none-match header the response lacks the etag header. H/T @efasel http4k-core [Fix #1030] Maven POM for http4k-format-jackson-xml is invalid: jackson-dataformat-xml is missing a version v5.12.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : Fix lens replacement of path parameter when there is a regular expression in the path segment http4k-format-jackson : Added lens support for deserialising data4k containers directly from HTTP message bodies (via Body.json(::JsonNodeDataContainer)).toLens() v5.12.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.22, and Jetty 12 (see below). http4k-server-jetty - [Breaking] Upgrade to Jetty 12. This is a major rewrite of the Jetty engine and the API surface is incompatible with v11. If you are using vanilla Jetty backend then this is a NoOp replacement, otherwise fallback to using the new Jetty11 module and then plan migration accordingly. Massive H/T to H/T @FredNordin for the implementation upgrade. http4k-server-jetty11 - [New Module!] Drop-in replacement module for custom Jetty11 users. Constructor is now called Jetty11() instead of Jetty() , so migration should be very simple. Other renames as required (using 11 ) to avoid API clashes in the http4k codebase. http4k-aws : [Breaking] Tweaks to the signature of AwsPreSignRequests . Use AwsRequestPreSigner instead. H/T @oharaandrew314 v5.11.1.0 \u00b6 http4k-aws : Pre-sign AWS requests with the new AwsPreSignRequests class. H/T @oharaandrew314 http4k-serverless-lambda : [Fix #1013] Support multi value query parameters in ApiGatewayV2LambdaFunction ( http4k-serverless/lambda) v5.11.0.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [Unlikely Break: Fix #1011] Jackson does not honour serialisation of Enums when they are used as Map keys. The fix MAY break JSON serialisation (which actually is a bug as the expected behaviour is for the Enums to use the predefined mapping). v5.10.7.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : [Fix #1009] Extracting access token from non-standard AccessToken response fails v5.10.6.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core : Make RouterDescription print-friendly v5.10.5.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-serverless-lambda : Add support for custom EventBridgeEvent format v5.10.4.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.21 v5.10.3.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract [Fix #1002]: Ability to use RequestContexts for providing a User Principal with Security. v5.10.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-core- : [Fix] FollowRedirects now remove host header http4k-testing-webdriver- : Ability to inject clock into the Webdriver v5.10.1.0 \u00b6 http4k-testing-webdriver * : Allow the originalUri method of the OAuthRedirectionFilter to be configured when constructing an OAuthProvider H/T @mbcltd http4k-format- * : Add alternative syntax for Automarshalling injection/extraction of bodies into and out of HttpMessages v5.10.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.20 http4k-testing-webdriver * : Host header is populated in Http4kWebDriver H/T @mbcltd v5.9.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k-client-helidon : [Breaking] Upgrade to stable v4 of Helidon, API changes. http4k- * : [Breaking - dev only] http4k is now built with Java 21, although Java 8 is still targeted. v5.8.6.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : New filter to validate zipkin headers. H/T @time4tea v5.8.5.1 \u00b6 http4k- * : Fix maven dependencies marked as optional in various http4k modules v5.8.5.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Jetty. http4k-core : Rename Events.then() with Events.and() for clarity. v5.8.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-lambda : Add support to multiple query/header values with the same key v5.8.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator * : Added HTMX emulation on Http4kWebDriver H/T @mbcltd v5.8.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core * : Added extension function ExecutionService.withRequestTracing() to propagate Zipkin traces across threads v5.8.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : BiDiLenses now implement LensInjectorExtractor v5.8.0.0 \u00b6 http4k-core : BiDiLenses now implement LensInjectorExtractor http4k-contract : [Unlikely break] NoRenderer now returns a 404 instead of an empty JSON document. v5.7.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-aws : Add support for AwsSdkAsyncClient. H/T @oharaandrew314 v5.7.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-testing-playwright : [New Module] Easily browser-test your http4k apps with this Playwright JUnit extension! H/T @dmcg for the inspiration. v5.7.3.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.10 http4k-htmx : Added hyperscript.js webjar to the distribution. v5.7.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-htmx : [New Module] Basic support for htmx development, including Webjar and custom lens types http4k-testing-webdriver : Improve support for radio buttons and radio groups in the http4k-testing-webdriver. H/T @mbcltd v5.7.1.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-contract * : [Fix #964] ContractRoute - inconsistent behavior on route matching. H/T @potfur for the investigation. v5.7.0.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-server-undertow : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite. http4k-server-jetty : [Unlikely break] - Reverse removal of the connectRequest from the SSE interface. This should undo the break caused by the recent rewrite. v5.6.5.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-testing-approval : Whitespace is now trimmed from end of approval file content. Improves compatibility with IntelliJ (as final line endings might be added automatically) http4k-testing-webdriver : [Fix #963] Submitting empty textarea form element in the webdriver causes a validation error v5.6.4.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-server-apache4 : Upgrade compromised commons-codec version. H/T @oharaandrew314 http4k-template-jte : [New module] JTE templating support v5.6.3.0 \u00b6 http4k-security-oauth :Add ability to override response mode in OAuthProvider. v5.6.2.1 \u00b6 http4k-core : [Fix] Extend URI now supports fragment parameters v5.6.2.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-security-oauth : [Fix] In-memory request tracking for FakeOAuthServer now supports full AuthRequest. v5.7.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-serverless-core : Add some filters for Serverless functions. v5.6.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added nonBlank() to set of standard BiDiMappings. H/T @dmcg http4k-core : [Unlikely break: Fix #956] Lenses don't really work for optional fields in HTML form parsing. Form-fields are now filtered for values which are not blank. This means that you may need to change form lenses to be optional and default to an empty string if they are missing. v5.5.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-cloudnative - Add support for enums in EnvironmentKey http4k-testing-tracerbullet : [Breaking] Fixed SequenceDiagrams for Mermaid to be formatted correctly. This may break approval files for any tests. v5.4.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-helidon : API changes from Helidon alpha to M1 http4k-server-helidon : API changes from Helidon alpha to M1 http4k-contract : [Fix #750] JacksonFieldMetadataRetrievalStrategy is incompatible with kotlinx.serialization @Serializable classes. H/T @krissrex http4k-realtime-core : [Fix #951] Add filters to initialise request context for SSE and WS. http4k-realtime-core : [Fix #885] Accept websocket subprotocols. Note that not all servers are currently supported http4k-server-jetty : [Fix #885] Support subprotocols for Websockets v5.4.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-aws : [Fix #656] AWS request signing issue for URLs with special characters. H/T @krissrex http4k-aws : [Fix #947] AWS request signing issue for duplicated headers and header values with multiple spaces. H/T @krissrex http4k-format-kondor-json : [Unlikely Break] Upgrade kondor-json to 2.0.0. H/T @FredNordin v5.3.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.9.0. http4k-core : Surface root error messages in Body lenses when failure occurs on deserialisation. http4k-template-thymeleaf : [Unlikely Break] Use HTML rendering mode and .html suffix by default for Thymeleaf templates. H/T @mikaelstaldal v5.2.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Fix #939] Override all Request and Response mutators for routed messages. H/T @kwydler v5.2.0.0 \u00b6 http4k-contract-jsonschema : [New module] Extracted this so we can reuse in non-OpenAPI scenarios http4k-contract : [Deprecation] Repackaging of JSON schema classes. Should only affect users if they have explicitly used/extended the standard behaviour. v5.1.2.1 \u00b6 http4k-serverless-lambda * : [Fix #936] AWS SQS null deserialization issues v5.1.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth * : Remove dependency on Kotlin-reflect by adding custom adapters. H/T v5.1.1.1 \u00b6 http4k-serverless-lambda * : [Fix #933] AWS SQS deserialization issue when md5OfMessageAttributes is null. H/T @oharaandrew314 v5.1.1.0 \u00b6 http4k-realtime-core * : Readd test client methods for SSE and WS v5.1.0.0 \u00b6 http4k-server-realtime-core * : [Breaking - Fix #931] Change Websocket and SSE interfaces to return WsResponse and SseResponse objects. This makes it easier to set response headers and control if a connection is made from the incoming request as it is no-longer hidden (it is exposed at the top level instead of being hidden in the SSE and Websocket objects). It also means that the interfaces for the protocols follow the same pattern. http4k-server-jetty * : As above http4k-server-undertow * : As above http4k-core * : [Fix #930] Update content-length header after GZipping it. H/T @bjornbugge v5.0.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : [Breaking] Remove all previous deprecations from all modules for v4. To upgrade cleanly, first upgrade to v4.48.0.0 and then re-upgrade to v5.0.0.0 . This will ensure that you only have to deal with Deprecations between the major versions. http4k-templates-dust : [Breaking] Nashorn is finally removed, so we are dropping support for this module. If you are on-pre Java 19 you can continue to use the old module version with no breaking changes. http4k- *: [Breaking] http4k is now built with Java 20. We are still compiling for older Java versions. New major versions will now be incoming with every major JDK release in order to track new and retired JVM features (6 month cycle). http4k-server-jetty : New Server Backend JettyLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard Jetty remains usable with any Java version. http4k-core : New Server Backend SunHttpLoom , based on Loom VirtualThreads. Requires Java 21 to use. Standard SunHttp remains usable with any Java version. http4k-server-helidon : [New Module] Helidon is a Loom-first rewrite of the popular server. Requires Java >= 19 to use. http4k-server-websocket : [New Module] A lightweight Websocket server built on TooTallNate/Java-Websocket . H/T @oharaandrew314 http4k-client-helidon : [New Module] An HTTP client build from the ground up to take advantage of project Loom. Requires Java >= 19 to use. http4k-format-kondor-json : [New Module] Support for KondorJson , the reflection-free JSON library. http4k-testing-tracerbullet : [New Module] TracerBullet allows you to hook into the http4k Events implementation to visually document your applications through testing. See example in reference guide. http4k-contract : Allow RouteMetaDsl to be marked as hidden H/T @oharaandrew314 v4.48.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.22. v4.47.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator Further simplifications of tracing algorithm v4.47.1.0 \u00b6 http4k-core Make it easy to propagate or update trace spans in ZipkinStorage http4k-incubator Further simplifications of tracing algorithm v4.46.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added status lookup by code. H/T @jhult http4k-core : [Unlikely break] Client request tracing now sets and resets the ThreadLocal containing the current Zipkin traces. Possible break if you were relying on Zipkin state in a downstream handler. This change will allow better in-memory testing as traces will be reported correctly inside the context of the filter. http4k-incubator : [Break] Changes to improve how we create Tracing trees, and this the signature of the Tracer to take EventNode which is a tree node. v4.45.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Fix #912 - CatchLensFailure filter now can pass the Request object into the receiver. H/T @mikaelstaldal http4k-server-format-moshi : Add support for Sets http4k-security-oauth : [Breaking] AccessTokens create method took an unnecessary duplicate parameter. To fix, just remove the authorizationCode parameter from your implementations and use the code from the tokenRequest v4.44.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : SameSite cookie is now lax when it comes to casing. v4.44.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] Allow setting of compression level on GZip filter in both Streaming and Memory mode. To fix, simply change from GzipCompressionMode.Memory/Streaming to GzipCompressionMode.Memory()/Streaming() v4.43.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Fix] #901. Improve performance of GZip in streaming mode. v4.43.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-undertow : Upgrade websocket requests based on other common headers. H/T @endofhome http4k-security-oauth : [Breaking] Make full callback URI available as part AuthorizationCodeMissing error. Fixes #895 http4k-core : Static resources now return directives as well as content type on served assets. v4.42.1.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.42.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-kotest : [Possible break] Fix of this Kotest issue in new dependency release might lead to some surprising changes in behaviour of matchers for comparing JSON nodes v4.41.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract-ui-redoc : [New Module] Serve Redoc with the redocUiWebjar function. http4k-contract : [New Feature] Serve Redoc with the redocUiLite function. http4k-contract-ui-swagger : [Fix] #880. swaggerUiWebjar now works properly with a non-root path. Plus performance improvements. v4.41.3.0 \u00b6 http4k-incubator TracerBullet diagrams have more options for reporting errors. v4.41.2.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.20. http4k-incubator TracerBullet diagrams have added colours. v4.41.1.1 \u00b6 http4k-contract-ui-swagger Fix dependency from provided -> api v4.41.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-opentelemetry * :Fix #867. OpenTelemetry tracing uses bad default span naming. H/T @krissrex for the report. http4k-contract-ui-swagger : [New Module] Serve a customized Swagger UI via a bundled WebJar with the new swaggerUiWebjar function. H/T @oharaandrew314 http4k-contract : Deprecate swaggerUI in favor of new swaggerUiLite function, which uses a new config format. H/T @oharaandrew314 v4.41.0.0 \u00b6 http4k-core * : [Unlikely break] Fix creation of UriTemplate when it starts/ends with multiple slashes. This shouldn't cause any problems that we know about, but we are bumping the breaking version number just in case. v4.40.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-apache * : Fix #866 - ApacheClient does not handle SocketException. v4.40.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : TracerBullet now renders results of tests by default. Use RenderingMode to switch off this default behaviour. v4.40.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [Possible clash/break] Upgrade to v2.0 of SnakeYaml (CVE fixes etc) may break dependencies which previously used v1.3X.X. It is safe to pin your SnakeYaml version to 1.3X.X if there is a clash with other libraries in your stack. v4.39.0.0 \u00b6 http4k-contract : [Breaking] Support for HTTP webhooks and callbacks in OpenApi3 models. Note that the Swagger UIs do not support OA 3.1.0 yet so we have limited the OA version number to 3.0.0. v4.38.0.1 \u00b6 http4k-core : [Fix] Header parsing to split correctly. v4.38.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.10. http4k-core : [Unlikely break] Header values now trim leading space (as per RFC) http4k-incubator : Added D2 support for tracing diagrams. http4k-testing-approval : Make tests line-ending-agnostic. H/T @oharaandrew314 http4k-format- * : Various tweaks to modules to standardise behaviour. H/T @oharaandrew314 v4.37.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.8.0. http4k-core : [Fix] #846 - Status.hashCode is inconsistent with Status.equals. http4k-contract : Add new endpoint security type: OpenIdConnectSecurity . H/T @oharaandrew314 http4k-contract : swaggerUi now supports Oauth2 redirects. H/T @oharaandrew314 v4.36.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-multipart : Add lensing of Multipart form fields using JSON and Automarshalling http4k-server-jetty : Add support for serving SSE. H/T @FredNordin http4k-contract : [Breaking Fix] Fix #842 - Map OpenAPI implementation adds all properties as required H/T @BBB v4.35.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Allow access-control-max-age header to be set from cors policy. H/T @moddular http4k-contract : [Fix] Or security renderer was not rendering properly when the component parts are themselves composite securities. v4.35.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : Trace diagram improvements for PUML, Mermaid and Markdown. v4.35.2.0 \u00b6 http4k-incubator : More diagram tweaking. v4.35.1.0 \u00b6 http4k-incubator : Tweak of some diagramming. v4.35.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-failsafe : [New Module!] Failsafe is a lightweight, zero-dependency library for handling failures. H/T @FredNordin http4k-incubator : [Breaking] Rewrite of infrastructure for generating tracing diagrams, including new interfaces and support for rendering to various formats. Initial support for PUML and Mermaid. v4.34.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-undertow : Remove extra dependencies which aren't needed. http4k-contract : fix Path value resolution it starts with same string as the prefix URL segment. H/T @tkint v4.34.3.1 \u00b6 http4k- * : Fix #827 - Requests with unknown HTTP method result in uncaught exceptions v4.34.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support for arrays of enums in OA3. v4.34.2.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.34.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-template-rocker : [New module] Compile-time templating with Rocker! v4.34.0.4 \u00b6 http4k-contract- : Fix errant import which broke multipart Openapi V3 spec. v4.34.0.3 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Re-re-fix. v4.34.0.2 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. Refix. v4.34.0.1 \u00b6 http4k-format- * : Remove Json extension method on MultipartFormField.Companion due to problem in JUnit. v4.34.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.21. http4k-format- * : Added auto() methods to arbitrary lenses (so Query, Header, FormField etc..) http4k-core : [Unlikely break] reverseProxy() now takes the authority into account instead of just the hostname from the request. This should only impact you if you are doing reverse proxy operational on client side and using localhost without a port as a proxy. To fix - simply add the port to your proxying setup and all should be good. http4k-contract * : Fix: Remove duplicate content type header. v4.33.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-ktor * : Fix: Remove duplicate content type header. v4.33.2.1 \u00b6 http4k-contract : Fix OpenApi rendering for enums when there isn't reflection. v4.33.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-resilience4j-jetty : Fix #804 - CircuitBreaker counts error twice, once as an error and once as a success http4k-client-okhttp : Added websocket client. H/T @FredNordin. http4k-format-argo : Fix problem with duplicate keys when creating objects. http4k-security-oauth : Ability to add scopes to the OAuth refresh token. H/T @p10r v4.33.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-jetty : Added websocket client. H/T @FredNordin. http4k-format-moshi : Add facility to use lightweight metadata adapter instead of Kotlin reflect. H/T @oharaandrew314 v4.33.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Handlebars. http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda v4.32.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Move Jakarta Servlet code from Jetty as is now shared. http4k-contract : Add UserCredentialsOAuthSecurity . This allows the OpenApi spec to define a Resource Owner Password Credentials grant. It also includes a shortcut to load the principal into a RequestContextLens . H/T @oharaandrew314 http4k-core : Add StringBiDiMappings.csv to map between string and list, with a configurable delimiter and element mapping. H/T @oharaandrew314 http4k-multipart : [Breaking] Add DiskLocation and the ability to keep uploaded files permanently stored on disk. H/T @jippeholwerda v4.32.3.0 \u00b6 http4k- * : Upgrade some dependency versions including CVE fix for Undertow backend. v4.32.2.0 \u00b6 http4k-core : Add StringBidDiMapping.basicCredentials to easily convert between Credentials and basic auth. H/T oharaandrew314 http4k-core : Add Header.AUTHORIZATION_BASIC lens to easily get and set basic Credentials for a message. H/T oharaandrew314 http4k-contract : BasicAuthSecurity now supports a RequestContextLens to store the principal. H/T oharaandrew314 v4.32.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Added ability to make Automarshallers strict. v4.32.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.20. http4k-testing-webdriver : [Unlikely Break] Upgrade has removed deprecated method. v4.31.0.0 \u00b6 http4k-core : [Unlikely Break] Added ZipkinTraceStorage , defaulting to ThreadLocal implementation. This allows centralised storage of trace information in non-standard threading environments (eg. coroutines). v4.30.10.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : YAML is now a recognised content type. v4.30.9.0 \u00b6 http4k-cloudevents : Add custom lenses to retrieve data from a cloud event and an extension function to set it. v4.30.8.0 \u00b6 http4k-cloudevents : Add Jackson.cloudEventsFormat() so we can use custom formats in cloud events lenses v4.30.7.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #779: SunHttp does not blow up if you add a ll value. v4.30.6.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.30.5.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-websocket : Fix #775 - WebsocketClient.nonBlocking cannot receive messages in binary mode. H/T oharaandrew314 v4.30.4.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.30.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : RefreshingOAuthToken does not blow up when no expiry returned by server. v4.30.2.1 \u00b6 http4k-format-moshi-yaml : [Fix] Re-fix YAML defaults for over greedy boolean values (regression caused by upgrade to SnakeYaml). v4.30.2.0 \u00b6 http4k-security-oauth : Make FakeOAuthServer more configurable, and removed the need for passing in an auth-code generator. v4.30.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Converted AccessToken to be an interface, and internalised a lens which shouldn't have been used by anyone. To fix uses of accessTokenResponseBody , replace with Body.auto().toLens() , importing from OAuthServerMoshi. v4.29.1.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.29.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : [Unlikely Break] Slight changes to CSRF generator interface. Should be easy to fix. v4.28.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth : Internal refactoring v4.28.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-client-okhttp : Handle previously escapable HTTP client timeout case. http4k-contract : Added Swagger UI helper route. H/T @oharaandrew314 v4.28.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including CVE fix for Undertow backend. http4k-contract : [Unlikely break] Remove direct dependency on kotlin-reflect JAR, as it is brought in my http4k-format-jackson anyway. This builds ok but we have bumped the version number just to be sure. H/T @oharaandrew314 for the inspiration. http4k-format-core : Add ContentNegotiator and auto versions to be plugged into http4k-format-* modules. H/T @oharaandrew314 http4k-core : Add cors exposed headers property. H/T @oharaandrew314 v4.27.4.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi * : Upgrade Moshi to introduce a JSON node model, thus converting Moshi to be an AutoMarshallingJson. This should open the door to us eventually allowing Moshi to be used in http4k-contracts (and OpenAPI). Massive H/T to @oharaandrew314 for the work that went into this. v4.27.3.0 \u00b6 http4k-contract : OpenApi3 Operation Ids now replace '-' with '_', as '-' interfere with generation of OpenAPI clients. v4.27.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-graphql : Add GraphQL explorer for http4k-graphql. H/T @arnabkd http4k-realtime-core : Add helper for test Websocket. H/T @oharaandrew314 http4k-resilience4j * : Fix #745: ResilienceFilters.CircuitBreak counts an error twice: once as successful, once as error. v4.27.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added support for Web Linking header standard http4k-multipart : Fix multipart upload failure if charset is included in content type. H/T @wickwirew http4k-server-jetty : Remove usage of deprecated status description API. H/T @@makowalski + @mandyvuong v4.27.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.7.0 v4.26.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Unlikely break] Remove dependency on kotlin stdlib JDK 8 as we don't need it to compile. If this causes a problem, simply re-add api(Kotlin.stdlib.jdk8) to your project dependency list. http4k- * : Fix #744 - Provided dependencies included as runtime in http4k versions > 4.19.1.0. v4.25.16.2 \u00b6 http4k-core : Fix query parameter parsing when value contained = . H/T @overfullstack http4k-security-digest : Fix digest challenge parsing when nonce contained = . H/T @oharaandrew314 v4.25.16.1 \u00b6 http4k-contract : [Revert fix] - File field is described as \"string\" instead of \"file\" in OA3 specification. v4.25.16.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Deprecate eTag filter in favour of ETagSupport. http4k-contract : [Fix] - File field is described as \"string\" instead of \"file\" in OA3 specification. v4.25.15.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #738 - Calculating eTag ate body. http4k-core : Caching filters now replace headers instead of adding them. http4k-server-jetty : Change constructor to use supported shutdown mode. H/T @jshiell v4.25.14.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Refreshing Credentials Provider now does not block if there is more than half of the expiring time left. http4k-core : Fix #735 - use whole message body for etag hash. H/T @aSemy http4k-metrics-micrometer - Enable publishPercentileHistogram for Micrometer request timer H/T @jakubjanecek v4.25.13.0 \u00b6 http4k-server- *: Add support for graceful shutdown (available to most server implementations) H/T @nlochschmidt http4k-core : Simplify hex decoding H/T @dzappold v4.25.12.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml- *: Replace default YAML boolean resolver to be less greedy. v4.25.11.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.21. http4k-core : Fix #728 - No way to set the request timeout when using the JavaHttpClient. H/T @gmulders http4k-oauth-security : Add missing Moshi adapter v4.25.10.1 \u00b6 http4k-core : Fix ServerFilters.BasicAuth handling of passwords containing colons H/T @robd v4.25.10.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Expand out values4k lens option http4k-core : Allow cookie values to be returned unquoted H/T @2x2xplz http4k-format- * : Throw a lens failure if a valid locale was not parsed H/T @oharaandrew314 http4k-opentelemetry : Fix #726 - OpenTelemetry: t.localizedMessage can't be null v4.25.9.0 \u00b6 http4k- * : Upgrade some dependency versions, including Ktor to v2.0.0 http4k-format-jackson-csv * : [New module] H/T @oharaandrew314 for the contribution. http4k-core : New standard mappings for Time primitives. H/T @oharaandrew314 v4.25.8.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.20. http4k-core : Enable overridable behaviour for CatchAll filter. H/T @dcmg http4k-multipart : Add disk cache path to MultipartFormBody.from() parameters. H/T @rny v4.25.7.0 \u00b6 http4k-client-fuel : [New module] An http4k client based on Fuel with both sync and async support. v4.25.6.0 \u00b6 http4k- * : Upgrade some dependency versions, including Jackson to overcome CVE-2020-36518. v4.25.5.2 \u00b6 http4k-contract : Don't output required fields into OpenAPI if there are none. v4.25.5.1 \u00b6 http4k-contract : Small tweak to internal API v4.25.5.0 \u00b6 http4k-contract : Add format OpenApi hints to Arrays and Maps v4.25.4.1 \u00b6 http4k-contract : Remove println from AutoJsonToJsonSchema. Doh! v4.25.4.0 \u00b6 http4k-format- *: Correctly identify integer and number JSON types. This has a knock on effect in OpenApi specifications. v4.25.3.0 \u00b6 http4k-serverless-tencent : Downgrade events library as is insecure. v4.25.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy). v4.25.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Values4k metadata population for OpenApi3 specifications (via Values4kFieldMetadataRetrievalStrategy). v4.25.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-security-oauth [Breaking]: Rename OauthCallbackError to OAuthCallbackError v4.24.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-webdriver * : [Breaking] Upgrade of webdriver to V4 has changed the APIS. The custom By implementation is no longer required so you can use the inbuilt Selenium version instead. The disabledCssSelector By implementation has been removed, although you can simply replicate this using the existing CSS selector model. v4.23.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support OA3 meta fields on properties if they are populated by a custom annotation. http4k-graphql : [Possible breaks] Due to upgrade of underlying graphql lib. v4.22.0.1 \u00b6 http4k-security-oauth : Fix error messages for oauth callback failures v4.22.0.0 \u00b6 http4k-security-oauth [Breaking]: apiBase path is now preserved when building auth and token uris http4k-security-oauth [Breaking]: provide reason when an oauth callback fails http4k-security-oauth [Breaking]: allow id token consumer to fail authentication flow v4.21.1.1 \u00b6 http4k-contract : OpenApi3 - Expose new prefix-overriding in OpenApi definitions. v4.21.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : OpenApi3 - Ability to provide prefixes for all models in a tree. This allows you to have multiple versions of a single model in the specification (at the cost of duplicated schema models). v4.21.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : [Breaking] All metrics now include path when labelled and routed. For consistency, . in path names are now convert to underscores as well. Regexes are also removed from paths on both client and server. H/T @hektorKS http4k-testing-strikt : Fix #709 - Strikt assertion builder for Uri.path H/T @michaelbannister v4.20.2.0 \u00b6 http4k-contract : OpenApi3 - Don't add required field if no fields are required! v4.20.1.0 \u00b6 http4k-contract : Fix #706: Form \"multi\" lens's do not render an items field in contracts. http4k-testing-chaos : ChaoticHttpHandler disables Chaos API when reflection not available. v4.20.0.0 \u00b6 http4k-core : Fix #704: Filters are recreated on every request/ H/T @hektorKS http4k-core : Fix #702: TrafficFilters.ReplayFrom doesn't correctly read from Replay. http4k-server-netty : Fix #703: Netty: null cannot be cast to non-null type java.net.InetSocketAddress http4k-client-apache-async : Remove usage of deprecated API http4k-client-jetty : Remove usage of deprecated API http4k-testing-webdriver : Remove usage of deprecated (internal) API http4k- * : Upgrade some dependency versions. v4.19.5.0 \u00b6 http4k-client-websocket : Apply a timeout when creating a blocking client websocket connection v4.19.4.0 \u00b6 http4k- * : Upgrade some dependency versions. v4.19.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-opentelemetry : Fixes #697: Upgraded OpenTelemetry version to 1.11.0 H/T @jenarros v4.19.2.0 \u00b6 http4k-server-jetty : Replace conscrypt with internal java for ALPN server v4.19.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added ContentDispositionAttachment server filter. H/T @jenarros http4k-core : Fix path conversion for static routing handlers with trailing. H/T @jenarros http4k-contract : Support non-JSON schema types in request definitions. v4.19.0.0 \u00b6 http4k-core : [Potential Break] Fix #693 Cookie implementation uses LocalDateTime val which is implicitly turned into GMT time for cookie. Break is that Cookies now run from Instant instead of LocalDateTime. Thanks to @maedjyuk-ghoti for alerting us to chase down this 5y+ standing bug! http4k-security-oauth : Fixes to the InsecureCookieBasedOAuthPersistence to make it more user-friendly. http4k-server-netty : Keep-alive for Netty when not streaming. H/T @jakubjanecek for the contrib! v4.18.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fixed URLPathSegment encoding/decoding based on RFC 3986. H/T @jenarros for the thoughtful and through contribution! v4.17.9.0 \u00b6 http4k-core : Added mapping for enum() in lenses. v4.17.8.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #687 - OpenApiV3 object serialization. H/T @lawkai v4.17.7.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : Fix for serialising Maps with null values (the key should still be rendered!) http4k-format-moshi-yaml : Remove accidental stack trace dump. v4.17.6.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-moshi-yaml : [New module] YAML marshalling with zero-reflection is now possible due to a combination of Moshi and SnakeYaml http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path. http4k-format-jackson-xml : Add autoBody for ConfigurableJacksonXml. H/T @oharaandrew314 v4.17.5.0 \u00b6 http4k- * : Upgrade some dependency versions, including ForkHandles to 2.0.0.0. http4k-core : Fix to HttpEvent to use correct value in xUriTemplate instead of full path. v4.17.4.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.6.10 http4k-incubator : Playing with TracerBullets... a generic interface for building TraceTrees from lists of MetadataEvents. v4.17.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : Fix #679. Approval tests delete actual when passing. v4.17.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Added Servers to OpenApi renderer. H/T @zsambek and @MarcusDunn for making it happen. v4.17.1.0 \u00b6 http4k-core : Make timeouts configurable for Java8HttpClient . v4.17.0.0 \u00b6 http4k- * : [Careful] Upgrade some dependency versions, including Kotlin to 1.6.0. http4k- * : [Breaking] Removal of all previously deprecated methods and types. To ensure you get the smoothest experience, please upgrade to v4.16.3.0 first, deal with the replacements and then upgrade to 4.17.0.0 v4.16.3.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-approval : Check the content type after the content is checked. v4.16.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Support for top-level enums in schema. http4k-contract : Support for enums in Header/Query/Paths. Finally! v4.16.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Upgrade build process to Kotlin. H/T @franckrasolo v4.16.0.0 \u00b6 http4k-contract : [Breaking] Added API-level tags to the contract rendering. http4k-serverless-lambda : Fix encoding of body to work with gzip filter v4.15.0.0 \u00b6 http4k-contract : [Break] BearerAuthSecurity is now more typesafe when taking a lens. v4.14.1.4 \u00b6 http4k-serverless-lambda : More fixing of deserialisation of SNS events. v4.14.1.3 \u00b6 http4k-serverless-lambda : Fix deserialisation of SNS events. v4.14.1.2 \u00b6 http4k-contract : Fix #667 - Jackson annotations being missed in FieldRetrieval. http4k-undertow : Server now handles HTTP requests gracefully when there is no HTTP handler set. v4.14.1.1 \u00b6 http4k-core : ChaoticHttpHandler is now event better behaved when chaos is not enabled and respects routing templates when applying. http4k-core : Fix #665 - OpenAPI json is incorrect when multi string query lens with defaulted values is used. H/T @suyash192 v4.14.1.0 \u00b6 http4k-core : ChaoticHttpHandler is now better behaved when chaos is not enabled. v4.14.0.0 \u00b6 http4k-core : Tidying up HttpEvents http4k-graphql : [Break] Handle null variables in calls. v4.13.4.0 \u00b6 http4k-core : Added convenience HttpEvents v4.13.3.0 \u00b6 http4k-core : ServerFilter request tracing now reinstates previous trace on exit instead of clearing it. v4.13.2.0 \u00b6 http4k-core : Make MetadataEvent a data class. v4.13.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Rename AsyncHttpClient to AsyncHttpHandler (deprecation). http4k-contract : Fix #664 - Introduce OpenAPIJackson to not serialize nulls by default into OpenAPI specs. If you use your own Jackson instance, you can replicate this behaviour by using .setSerializationInclusion(NON_NULL) on your custom ObjectMapper implementation. v4.13.0.0 \u00b6 http4k-core : Reverse Proxy available in both routing and non-routing version. Use reverseProxy() or reverseProxyRouting() accordingly v4.12.3.1 \u00b6 http4k-core : Reverse Proxy router now falls back to URI host when Host header missing. v4.12.3.0 \u00b6 http4k-security-oauth : Nicer OAuth client filters. v4.12.2.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #657 Use jackson to serialize enum models for OpenApi. v4.12.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix TrafficFilters.RecordTo eating body stream when used on a server. v4.12.0.1 \u00b6 http4k- * : Fix #652 - AWS event format adapters have fields with wrong cases. v4.12.0.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.5.30. http4k-testing-chaos : [Break] Behaviour is now an abstract class instead of a typealias. Super simple to fix. :) v4.11.0.1 \u00b6 http4k-graphql : Fix - Downgrade graphql-java and fix Graphql reference example. 4.11.0.0 contained an incompatible version of graphql-java for generate use. H/T @razvn for spotting and fixing. :) v4.11.0.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-format-jackson : Fix #646 - Boolean field can escape lens check without throwing MissingKotlinParameterException. http4k-aws : Set the query parameter to empty string if it's value is null, instead of \"null\". H/T @raelg for the PR. http4k-contract : [Possible (small) Break] Fix #644 - Lazy init contract without path params: Type mismatch. Contract routes with 0 parameters are now able to be constructed lazily - which has added (for consistency) a secondary to(fn: () -> HttpHandler) function to the construction DSL. This may cause overload ambiguity when routes are defined withtout the request input parameter. To fix, un-ambiguate you bindings! (eg. to { Response(OK) } becomes to { _ -> Response(OK) } ). H/T @dbacinski for the investigation. v4.10.1.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Fix #638 - Revert changes to make Uri incompatible with req -> next.invoke(request.header(\"foo\", \"bar\")); http4k-testing-kotest - Possible Break : DUE TO KOTLIN 1.4.10. Remove a haveBody matcher which uses Matcher directly, because of a bug in Kotest: https://github.com/kotest/kotest/issues/1727 http4k-format-jackson - Possible Break : DUE TO KOTLIN 1.4.10. Inline classes do not deserialise properly. See: https://github.com/FasterXML/jackson-module-kotlin/issues/356 v3.261.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k- * : Remove some example code which was mistakenly added to some main src dirs. No impact on anything other than JAR size. http4k-aws * : Add pluggable Amazon SDK client, allowing you to plug an HttpHandler into the Amazon SDK. v3.260.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Unlikely break : Added some nicer naming and examples for when people are calling http4k via Java code. http4k-core : Fixed SunHttp server backend not setting content length, and hence responses are always chunked. v3.259.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-netty : Fix #141 Http4k-netty performs really badly on all benchmarks. Massive H/T adam-arold! http4k-server-ratpack : Tweak to SO_BACKLOG size (1000). v3.258.0 \u00b6 http4k-testing-kotest : [New module] A set of matchers for use with the kotest library. H/T @nlochschmidt for the PR. http4k- * : Upgrade some dependency versions. v3.257.0 \u00b6 http4k-serverless-* : Making the Serverless APIs consistent between flavours by ensuring that all Serverless functions act by class extension and not reflection based approach. Deprecated old approach. Hopefully this is simpler.. :) v3.256.1 \u00b6 http4k-core : Fix #470. Path.of cannot decode path parameter values containing %/ v3.256.0 \u00b6 http4k-security-oauth : Add ability to handle form encoded responses in OAuth responses. v3.255.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : - Fixed up Maven dependencies so that they are not exporting compileOnly libraries into POMs. http4k-security-oauth : Remove \"user\" from default list of GitHub scopes as it gives you write access to the profile. New default is empty (just public data). http4k-core : Improve defaults of SunHttp server. H/T @nlochschmidt for the PR. http4k-contract : Add description to OpenApi schema fields using Jackson annotations. H/T @env0der for the PR. v3.254.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Added hostDemux() routing for when you want to select an HttpHandler based on the Host header. v3.253.0 \u00b6 http4k-core : Replaced implementation of JavaHttpClient with one from Java standard library. Should you not yet have access to the Java 11 SDK, we renamed the old implementation to Java8HttpClient . Note that some headers that are added by default by the old Java8 implementation will no longer be added. http4k-core, Breaking : Change Body.binary() lens to use an InputStream instead of a raw Body . To fix, just provide the InputStream by calling Body.stream() or similar. http4k-client-websocket, Unlikely break : Allow API users to pass in their own Draft object for custom protocols. If broken, simple fix is to just use named arguments in the construction call to the client. http4k- * : Upgrade some dependency versions. v3.252.0 \u00b6 http4k-server-apache, http4k-client-apache, http4k-client-apache-async, Breaking : Updated to Apache HTTP 5.X.X. H/T to @jshiell. Note that the underlying Apache APIs have changed in the v5 release. For the clients, this should only break if you have customised the underlying HTTP CloseableHttpClient that is passed to the constructor of the http4k client. If you have, we have you covered with.... http4k-server-apache4, http4k-client-apache4, http4k-client-apache4-async : New modules to maintain previous integration with Apache HTTP 4.X.X. Intended to reduce the impact on projects that are not ready to move to v5 yet. In these compatibility modules, renamed ApacheClient -> Apache4Client and ApacheAsyncClient to Apache4AsyncClient - which is the only change that should be required in end user code. http4k-serverless-openwhisk : Fixes to support binary content types and overcome issues with the request/response format of the OW Java runtime. http4k-core : Added some Filters for base64 encoding and decoding responses. http4k- * : Upgrade some dependency versions. v3.251.0 \u00b6 http4k-core : Added support for multiple \"cookie\" headers. H/T @jshiell http4k-serverless-openwhisk : New serverless module! http4k-serverless-*, Breaking : - Repackage some functions to org.http4k.serverless package. Just change the package names to fix. v3.250.0 \u00b6 http4k-core : Add Request.source to provide extra information about the request origin (address/port/scheme). H/T @kam1sh and @jshiell for the contributions. http4k-security-oauth : Add OAuth provider configuration for Facebook. H/T @knyttl for the PR. http4k-server-netty : Implement KeepAlive. H/T @carbotaniuman for the PR. http4k-bom : New Bill-Of-Materials module! http4k- * : Upgrade some dependency versions. v3.249.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-server-netty : Add support for response streaming. H/T @carbotaniuman for the PR. http4k-serverless-gcf : New serverless module! H/T @ssijak for the PR. v3.248.0 \u00b6 http4k-server-ratpack : New backend module! http4k-format-jackson-yaml : [New module] http4k- * : Upgrade some dependency versions. http4k-cloudnative : - Fix #418 - Fix separator propagation when adding values to an existing MapEnvironment. H/T @jshiell http4k-contract : - Add support for securing the API description endpoint. H/T @goodhoko for the PR. http4k-client-websocket : Added auto-reconnection support on blocking WsClient. H/T @alphaho for the PR. http4k-format-* : Rename/deprecate asXYZString(Any) -> asFormatString(Any) in all modules v3.247.0 \u00b6 http4k-server-ktornetty : New backend module! H/T @albertlatacz for the contribution! http4k- * : Upgrade some dependency versions. http4k-security-oauth : Fix #414 BasicAuth server filter to not throw an exception on invalid base64 input. H/T @Sebruck for the fix. v3.246.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-template-pebble : Fix #411 - Non-root pebble templates when using CachingClasspath from a compiled JAR. H/T @alyphen v3.245.1 \u00b6 http4k-server-ktorcio : Fix #410 - KtorCIO does not stop properly. v3.245.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : Factored out Http4kServletAdapter to allow usage of the Servlet API outside of creating a Servlet instance. http4k-*, Breaking (prevent API abuse) : Restricted generic with() method actual http4k types. Usage outside our API should not use this method. http4k-contract : Fix #404 - Rework of some FieldRetrieval classes to remove duplication and to support PropertyNamingStrategies set at the global level v3.244.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-*, Breaking (if you're not using it right!) : Fix #397 - Fixed up Maven dependencies so that they are not bringing in runtime libraries. http4k-core : - Add enum StringBiDiMapping #395 - H/T @goodhoko v3.243.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.72 . http4k-security-oauth : A strategy can now be passed into AuthRequestWithRequestAuthRequestExtractor to determine how to combine AuthRequest and RequestObject H/T @tom v3.242.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-testing-servirtium : Improve error diagnostics. H/T @vchekan for the PR. http4k-*, Unlikely Break : Change Router to return RouterMatch instead of nullable HttpHandler . This allows us to support METHOD_NOT_ALLOWED (405) if we match a path but not a verb instead of just NOT_FOUND (404). This should break custom ro H/T @jshiell for the PR. v3.241.0 \u00b6 http4k-security-oauth, Breaking : client_id along with the corresponding TokenRequest is passed into access and refresh token generators so additional validation can take place H/T @tom v3.240.0 \u00b6 http4k- * : Upgrade Kotlin to 1.3.71 . http4k-testing-servirtium : Switch OkHttp client for Apache. http4k-server-jetty : Made some classes non-internal so they can be easily reused for custom ServerConfig implementations. v3.239.0 \u00b6 http4k-client-websocket, Breaking : Added extra onError handler when creating a non-blocking websocket. http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.70. v3.238.0 \u00b6 http4k-security-oauth : Early work on supporting refresh tokens. H/T @tom v3.237.0 \u00b6 http4k-core : Fix #377. Added replaceHeaders() method. Thanks to @bastman for the idea. http4k-contract : Fix nullability of references in OpenApi3 v3.236.0 \u00b6 http4k-testing-servirtium : Don't pass recording handler into non-test methods as a resolved parameter. v3.235.0 \u00b6 http4k-testing-chaos, Break/Rename : ChaosEngine is now exposed when configuring API. Renamed withChaosEngine() to withChaosApi() , replaced toggle() and update() with enable()/disable() v3.234.0 \u00b6 http4k-testing-chaos, Break : Tweaked API make it simpler to use the ChaosEngine via programmatically (as opposed to REST). http4k-testing-servirtium, Tiny break : Tweaks to InteractionOptions to make working with Servirtium tests a bit nicer. v3.233.0 \u00b6 http4k-testing-servirtium : Upgrade ServirtiumServer to use OkHttp instead of JavaHttpClient (due to streaming restrictions on MiTM). http4k-testing-servirtium, Break : Rename Github to GitHub . v3.232.0 \u00b6 http4k-format-kotlinx-serialization : New JSON module! H/T @joscha-alisch for the PR. :) http4k-testing-servirtium : Work around Kotlin @JvmOverloads problem in ServirtiumServer. http4k- * : Upgrade some dependency versions. v3.231.0 \u00b6 http4k-testing-servirtium : Making API a bit more Java-compatible friendly. Ability to vary the Server implementation. http4k-server-jetty : Fix #362 - Websocket disconnect early causes lateinit reference race condition. H/T @fintara for the report/fix. v3.230.0 \u00b6 http4k-aws : Improved efficiency of building AWS credentials (replace String.format). http4k-testing-servirtium : Making API a bit more Java-compatible friendly. http4k- * : Upgrade some dependency versions. v3.229.0 \u00b6 http4k-security-oauth : Allowing for custom authenticate methods when fetching access tokens H/T @tom v3.228.0 \u00b6 http4k-testing-servirtium, Breaking : API is still in beta, so moving to a more composed approach which will increase reuse and allow for running Servirtium infra without a dependency on http4k or Junit. Added loading from GitHub. :) http4k-security-oauth, Breaking : Audience on request object is now a list to support multiple audiences. H/T @tom http4k-security-oauth : Nonce is now also passed through on RequestJwts, so it can be added to request jwts. H/T @tom v3.227.0 \u00b6 http4k-core : Implmement #340. Support SameSite cookies. H/T @danielwellman for the contribution. http4k-format-jackson : Made JacksonJsonPropertyAnnotated Kotlin 1.4 safe (call to superclass might return null). H/T @pyos for spotting this. v3.226.0 \u00b6 http4k-testing-servirtium : Moved Servirtium code to new module - was previously [http4k-incubator]. v3.225.0 \u00b6 http4k-incubator : Rewrote Servirtium code to support manipulations. v3.224.0 \u00b6 http4k-security-oauth : Fix issue where AuthRequestWithRequestAuthRequestExtractor doesn't take into account scopes not being nullable correctly. H/T @tom v3.223.0 \u00b6 http4k-security-oauth : Adding expiry to RequestObject . H/T @tom http4k-security-oauth : Fixing issue where unknown fields cause extracting RequestObject from a jwt, fails due to unknown fields. H/T @tom v3.222.0 \u00b6 http4k-security-oauth, Breaking : Error responses in the authorise endpoint now take into account values from the request parameter, this will require a validator for that jwt be implemented. H/T @tom http4k-security-oauth, Breaking : State is now its own type, and not just a string, so it can be validated. H/T @tom http4k-security-oauth, Breaking : redirectUri on AuthRequest is now nullable as it might come on a request jwt, this is validated to be always be present downstream. H/T @tom http4k-security-oauth : Allow parsing of request jwt. H/T @tom http4k-security-oauth : Adding RequestObject to AuthRequest . H/T @tom http4k-security-oauth : Adding AuthRequestWithRequestAuthRequestExtractor that will extract the request from the jwt, assuming the validator is implemented which can be used instead of just using AuthRequestFromQueryParameters if support for parsing a request jwt is required. H/T @tom v3.221.0 \u00b6 http4k-*, Unlikely break from Java only : Make all custom http4k exceptions extend RuntimeException. This helps with Java compatibility so things like LensFailure inside Java Lambdas don't require catching (as they are caught/dealt with by other bits of http4k automatically) v3.220.0 \u00b6 http4k-moshi, Behaviour break : Fix #353 Don't fail by default on unknown properties. This is the expected default behaviour for all JSON implementations. H/T cnusp for the report. v3.219.0 \u00b6 http4k-incubator : Next iteration of Servirtium JUnit extensions. Improved API to support multiple storage engines. v3.218.0 \u00b6 http4k-incubator : Next iteration of Servirtium JUnit extensions. Correct indexing of interactions. http4k-security-oauth : Authorisation rendering will now taking into account 'response_mode' of either query or fragment in responses and no longer just use the default fo the 'response_type'. H/T @tom http4k-security-oauth, Breaking : Error responses in the authorise endpoint will actually redirect back to ' redirect_uri' assuming the validator correctly validates both the 'client_id' and 'redirect_uri' to be valid. H/T @tom v3.217.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-incubator : Next iteration of Servirtium JUnit extensions. Only check content which is in the contract when replaying. v3.216.0 \u00b6 http4k-core, Breaking : Removed clashing Events then() from deprecated (meaning it cannot be used as there is also another then() in that package). Use the one in org.http4k.events instead. http4k-security-oauth : Adding nonce to AuthorizationCodeDetails H/T @tom v3.215.0 \u00b6 http4k-core : GZip client filters now send correct accept-encoding header. @jshiell http4k-core : New AcceptGZip client filter allows handling of remote GZip without compressing client requests. @jshiell v3.214.0 \u00b6 http4k-core : Fix #344 H/T Streaming GZip encoder loses data. @jshiell v3.213.0 \u00b6 http4k-security-oauth : Fixing wrong AuthRequestExtractor passed to AuthRequestTrackingFilter. H/T @tom v3.212.0 \u00b6 http4k-security-oauth : allowing additional properties to be stored on auth request, if using additional extractors H/T @tom v3.211.0 \u00b6 http4k-core : Fixes for #338 - Gzip filters send content-encoding of gzip even when body is empty. H/T @jshiell http4k-security-oauth, Break : OIDC callback urls using the ResponseType 'code id_token' will now have the parameters returned as a fragment not a query as per 3.3.2.5 of the OpenID Connect Core 1.0 spec H/T @tom http4k-security-oauth, Break : Initial support of nonce in OIDC requests H/T @tom v3.210.0 \u00b6 http4k-core : Support for GZipping response streams. H/T @jshiell http4k-security-oauth : Adding expires_in to token endpoint response. H/T @tom v3.209.0 \u00b6 http4k- * : Added Status to auto-marshalling JSON mappings. http4k-security-oauth : Adding token_type to token endpoint response, and strip out nulls in response. H/T @tom v3.208.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-core : PR #333. Copy zipkin traces across threads. H/T @jshiell for the PR. http4k-testing-approval : Close Readers when reading from them. http4k-incubator : Next iteration of Servirtium JUnit extensions for recording and replaying. v3.207.0 \u00b6 http4k- * : Upgrade some dependency versions http4k-incubator : Added first cut of Servirtium classes for recording and replaying traffic. Needs validating in the wild http4k-format-jackson : Fix #320. http4k-format-jackson incompatible with jackson-module-kotlin 2.10.1 v3.206.0 \u00b6 http4k- * : Upgrade some dependency versions. http4k-contract : Fix #323. Doc generation does not work with multipart lenses. http4k-format-jackson : Fix #313. Jackson serialization is not working properly with polymorphic types stored in a collection. H/T @alphaho for the PR :) http4k-core, Break : Renamed value on ParamMeta to description . v3.205.0 \u00b6 http4k- * : Upgrade some dependency versions, including Kotlin to 1.3.61 http4k-security-oauth : allowing setting scopes on AccessToken creation so they are set on the response. H/T @tom v3.204.0 \u00b6 http4k-core, http4k-aws : - increase efficiency of Hex implementation for trace ids and HMAC. H/T @time4tea http4k-cloudnative : Reimplemented Environment to be more efficient. H/T @time4tea for noticing this. v3.203.0 \u00b6 http4k-security-oauth : On generating tokens allowing for the client id to be based on the result of validation rather than just the form parameters of the request. To support client assertions. H/T @tom v3.202.0 \u00b6 http4k-security-oauth : Adding new errors to support issues with client assertions. H/T @tom v3.201.0 \u00b6 http4k-security-oauth : Allowing a scope to be set on AccessToken. Allowing for more low level validation of Authorise and Token Requests, by implementing org.http4k.security.oauth.server.AuthoriseRequestValidator and org.http4k.security.oauth.server.accesstoken.AccessTokenRequestAuthentication respectively. H/T @tom v3.200.0 \u00b6 http4k-contract : Support multiple request bodies in OpenApi v3 v3.199.1 \u00b6 http4k-format-jackson : Fix #313 Part 2 - Revert default behaviour for collections of polymorphic types, but is now overridable by using autoBody() instead of auto() . Reopened #313. v3.199.0 \u00b6 http4k-format-jackson, Breaking : Fix #313 - ConfigurableJackson.autoBody implementation would not work with collections of polymorphic types. This fix has the effect of blowing up auto-json behaviour when classes are defined inside functions (causing nasty java.lang.reflect.GenericSignatureFormatError: Signature Parse error exceptions). To remedy, just move inlined classes outside of the functions that they are defined in. H/T @alphaho for the PR. http4k- * : Update some dependency versions v3.198.0 \u00b6 http4k-core, Breaking : Reworking of ContentType to support multiple directives. directive field is now directives , so just add the extra 's' to fix :) http4k-security-oauth : Moar options on OAuthProviderConfig . H/T @tom v3.197.0 \u00b6 http4k- * : Update some dependency versions, including Kotlin to 1.3.60 . http4k-core : Make Query value optional when setting on a Request . http4k-core, Breaking : Fix #316. Optional Query lens handling is more accurate. See issue for details of change in behaviour. v3.196.0 \u00b6 http4k- * : Update some dependency versions. http4k-format-jackson, http4k-format-gson : Add support for auto marshalling Throwable in a sensible way. http4k-cloudnative : Renamed badly named UpstreamRequestFailed to RemoteRequestFailed . Improved error handling. v3.195.1 \u00b6 http4k-cloudnative : Fix adding value to overridden environment when using set() . H/T @jippeholwerda for the PR v3.195.0 \u00b6 http4k-security-oauth : Tweak to handle Content-Type comparisons (with and without directive). H/T @jippeholwerda for the PR http4k-multipart] - [Breaking : Added support for setting custom headers in Multipart form fields and files. This has removed the String as the default field type (it is now MultipartFormField . Calls to create lenses using MultipartFormField will now require MultipartFormField.string() instead. v3.194.0 \u00b6 http4k-contract : Useful tweaks to the contracts API v3.193.1 \u00b6 [http4k-cloudnative] Fix #304 - map get() does not respect fallback values in overridden environment. v3.193.0 \u00b6 http4k-contract : Marking endpoints as deprecated in OpenApi3 v3.192.0 \u00b6 http4k-template-jade4j : [New module] H/T @RichyHBM for the contribution! :) v3.191.0 \u00b6 http4k-contract : Better support for overriding of raw map definition id in JSON schema generation v3.190.0 \u00b6 http4k-core : Added method to (immutably) modify status on Response . H/T @brandon-atkinson for the suggestion http4k-core : Added composite object support to lens system, allowing creation of simple lenses which draw from several different values (of the same location only - e.g Query/EnvironmentKey) http4k-contract : Support for overriding the entity definition id in JSON schema generation http4k- * : Update some dependency versions. v3.189.0 \u00b6 http4k-server-netty : Fix reported port in Netty . H/T @fantayeneh for the PR :) http4k-security-oauth : Add validateScopes() to ClientValidator . H/T @tom v3.188.0 \u00b6 http4k-contract : Support multiple-response models in OpenApi2 and 3. Note that this currently is unsupported in the OpenApi UI due to a bug (which doesn't display the schema for the response correctly). However, the JSON schema is generated correctly in these cases. http4k- * : Update some dependency versions. v3.187.0 \u00b6 http4k- * : Update some dependency versions, and changes to various APIs involved (Jackson and Resilience4J) http4k-core : - Add YearMonth support to standard JSON mappings http4k-format-jackson, http4k-format-gson, Possible break : - Moved reified NODE.asA() method from JsonLibAutoMarshallingJson down onto the instances of the Json ( ConfigurableJackson / ConfigurableGson ). This is so that we can handle generified classes such as lists and maps correctly. (As per the problems fixed in 3.181.0) v3.186.0 \u00b6 http4k-core : - Rollback a couple of places which were using Java9+ APIs (for no good reason). v3.185.0 \u00b6 http4k-contract : Improvements to rendering enums as their own objects in JSON Schema. v3.184.0 \u00b6 http4k-contract : Add Cookies options to contract DSL v3.183.0 \u00b6 http4k-serverless-lambda : Add ability to access Lambda context. H/T @ivoanjo for the PR. http4k-contract : Fix rendering of OrSecurity when there are more than 2 parts. v3.182.0 \u00b6 http4k-core : Rename EventsFilter to EventFilter because sanity. http4k-format-jackson, http4k-format-gson : Reintroduce autoBody() method v3.181.0 \u00b6 http4k-core : Added base events implementations for StructuredLogging. http4k-core, Repackage : Events classes are now in org.http4k.events . http4k-core, Breaking : EventCategory is no longer a field of Event . To fix, just remove override from your Event classes. http4k-format-jackson, http4k-format-gson : Fixed problem when attempting to deserialise generic Lists. v3.180.0 \u00b6 http4k- * : Update various dependencies. http4k-testing-hamcrest : Improve messages of Hamkrest matchers. H/T @albertlatacz http4k-cloudnative : Fix #291 - Readiness check result when there are > 2 checks may not report the correct result. H/T @alfi http4k-security-oauth, Possibly breaking : Making client_secret optional in AuthorizationCodeAccessTokenRequest to support non client_secret flows. H/T @tom v3.179.1 \u00b6 http4k-client-okhttp : Include status description in Response object. v3.179.0 \u00b6 http4k-contract : Added OpenApiExtension interface, which allows the definition of extensions that will modify the OpenApi specification JSON. H/T @rgladwell for the inspiration. http4k-contract : Support composite security models using or() and and() . Once again, H/T @rgladwell :) v3.178.0 \u00b6 http4k-security-oauth, Possibly breaking : Request is passed as a parameter to the ClientValidator. Just pass it in! :) H/T @tom http4k-contract, Behaviour change : When specified, individual route security now replaces global security (this is as the security model in the OpenApi spec is specified) as opposed to both being applied. v3.177.0 \u00b6 http4k-security-oauth, Possibly breaking : More support for OIDC, adding state to AuthorizationCodeDetails, and passing it into createForAccessToken on IdTokens. H/T @tom v3.176.0 \u00b6 http4k-security-oauth : More support for OIDC. H/T @tom v3.175.0 \u00b6 http4k- * : Update various dependencies, including Kotlin to 1.3.50. http4k-security-oauth : Some support for OIDC. H/T @tom v3.174.0 \u00b6 http4k- * : Update various dependencies, including Jackson for a CVE. v3.173.0 \u00b6 http4k-core : Fix #273 - parentSpanId trace incorrectly populated when no previous traces http4k-contract, Unlikely Break : Remodelled how Security is rendered, so it's possible that this may break slightly for customer implementations http4k-contract : Added support for Implicit OAuth flow, with suport for custom googleCloudEndpoints Security. H/T @rgladwell v3.172.0 \u00b6 http4k-core : Added uni-directional serialization/deserialization options to JSON lib auto-conversion configuration. v3.171.0 \u00b6 http4k-core, Break (mitigation) : Replaced default resource loader location for singlePageApp() to /public instead of root - this is for safety of NOT serving the root of the classpath by default. v3.170.0 \u00b6 http4k-core : Add a warning when static() is used with no package path, thus exposing the contents of the classpath remotely. v3.169.0 \u00b6 http4k- * : Update various dependencies. v3.168.0 \u00b6 http4k-contract : Collect LensFailure causes into a single place when validating. v3.167.0 \u00b6 http4k-contract, Possibly Break : Open out ErrorResponseRenderer interface to take LensFailure instead of the individual failures when rendering badResponse() . To fix, simply wrap the list of failures into a LensFailure. v3.166.1 \u00b6 http4k-core : Tweak singlePageApp() routing handler, to correctly apply filters when fallback page is used. v3.166.0 \u00b6 http4k-core : Added singlePageApp() routing handler, which matches both static content or falls back to the root path index file v3.165.0 \u00b6 http4k-contract : Fix invalid OpenApi2 when root and base path match. H/T @rgladwell http4k-contract : ContractRoute is now an HttpHandler , so no need to wrap contract routes in a contract {} to test them. H/T @rgladwell for the inspiration. http4k-contract : Support Host/baseUri values in OpenApi2. H/T @rgladwell http4k-contract : Optionally add description route to route list H/T @rgladwell v3.164.0 \u00b6 http4k- * : Update various dependencies, including Kotlin to 1.3.41. http4k-testing-approval : Upgrade of HTML library from above may have an effect on output of HTML approval tests. http4k-contract : Support for more Jackson annotations in JSON Schema rendering. H/T @tom for the PR contributing this. v3.163.0 \u00b6 http4k-testing-chaos : Add detail to Chaos OpenApi interface. v3.162.0 \u00b6 http4k-testing-chaos : Add detail to Chaos OpenApi interface. v3.161.0 \u00b6 http4k-cloudnative : Added Forbidden request exception to HandleUpstreamRequestFailed. v3.160.1 \u00b6 http4k-testing-chaos : Countdown chaos trigger fixed. v3.160.0 \u00b6 http4k-testing-chaos : Slight fix to avoid consuming stream body when setting chaos. v3.159.0 \u00b6 http4k- * : Update various dependencies. http4k-client-okhttp : Updated OkHttp to v4.0.0 (Kotlin edition). http4k-contract : Tweak to JSON Schema rendering to handle recursive objects better. v3.158.1 \u00b6 http4k-server-netty : Fix #260 - cannot set multiple response headers with same name http4k-server-undertow : Fix #260 - cannot set multiple response headers with same name v3.158.0 \u00b6 http4k-contract : POSSIBLE BEHAVIOUR CHANGE DUE TO BUG: Fix #259 - Contract blocks do not produce 400s if an external CatchAll is provided. This may have an effect on how errors are generated (a 400 is produced instead of the previous 500 from the CatchAll). v3.157.1 \u00b6 http4k-security-oauth : Fix broken deprecation annotation. v3.157.0 \u00b6 http4k-security-oauth : Default to JSON format response in Access Token response http4k-security-oauth : Renamed a couple of classes (AccessTokenContainer -> AccessToken), and removed isValid method from AuthorizationCodes because it doesn't make sense for this to be on the OAuthServer. v3.156.0 \u00b6 http4k- * : Update Kotlin to 1.3.40 http4k-contract : Support OAuthSecurity renderer. v3.155.2 \u00b6 http4k- * : Update various dependencies. http4k- * : Dokka improvements. Does not mitigate #196 as we run the main build on OpenJdk11. H/T @ivoanjo v3.155.1 \u00b6 DO NOT USE - broken v3.155.0 \u00b6 DO NOT USE - broken v3.154.1 \u00b6 http4k-multipart : Made the multipart header parser case-insensitive. H/T @tenniscp25 v3.154.0 \u00b6 http4k-contract : Add SchemaModelNamer to allow for custom JSON Schema model names. v3.153.0 \u00b6 http4k-contract : OperationIds are generated without illegal characters {} . v3.152.0 \u00b6 http4k-contract : Support non-string keys for \"text convertible\" values in maps for Auto-schema generation. v3.151.0 \u00b6 http4k-contract : Fixed Auto-schema generation to detect and remove duplicate items from list schemas. v3.150.0 \u00b6 http4k-security-oauth : Make authentication mechanism for grant types configurable. v3.149.0 \u00b6 http4k-security-oauth : Initial support for client_credentials grant type. v3.148.0 \u00b6 http4k-contract : Jackson property searching in OpenApi3 now searches superclasses. v3.147.0 \u00b6 http4k-contract : Support custom JsonProperty annotation for OpenAPi3 generation http4k-cloudnative : New exception type for unuathorised. H/T @tom v3.146.0 \u00b6 http4k-contract : Fix #228 - Support Map-based fields in OpenApi 3 Auto-schema generation as additionalProperties . H/T @noahbetzen-wk for the idea. v3.145.0 \u00b6 http4k-contract : Reimplement Auto-schema generation using reflection. Added test cases to use the OpenApi generator to create valid code-based OpenApi clients using the OpenApi generator. http4k-format-jackson : Removed reflective JSON schema creator, since it was not actually OA3 compliant. v3.144.0 \u00b6 http4k- * : Update various dependencies. http4k-contract : Improvements to better adhere to OA3 spec. http4k-security-oauth : Allow injecting OpenID's request parameter into the authorization request. http4k-security-oauth : Expose request to AuthRequestTracking. v3.143.1 \u00b6 http4k-core : Replace RequestContexts with reference to Store . H/T @amcghie http4k-contract : Added some missing deprecations. http4k-contract : Fix #243 - Nulls not allowed in OpenApi V3 JSON models. v3.143.0 \u00b6 http4k-contract : Fix #239 - OpenApi v3 schemas for raw lists blow up when rendering. http4k- * : Update various dependencies. v3.142.0 \u00b6 http4k-contract : Both OpenApi v2 and v3 are now supported, including automatic schema generation. Some classes for OpenApi2 have moved to a new package - Deprecations should provide most alternatives. See module docs for details. For OpenApi v3, optionally include http4k-format-jackson to get JSON schema models based on JVM objects. http4k-format-jackson : Added reflective JSON schema creator, to be used for generating named models from JVM objects. v3.141.0 \u00b6 http4k-core : - Fix #233 - MemoryBody blows up with \"java.nio.ReadOnlyBufferException\" http4k-core : - Tighten up security on Basic and Bearer auth server filters. H/T @andymoody http4k-security-oauth : - Add filter to check bearer token is valid access token. H/T @andymoody v3.140.0 \u00b6 http4k- * : Update dependencies (including Kotlin bump to 1.3.31) http4k-security-oauth : Handle user rejecting/failing authentication. H/T @andymoody v3.139.0 \u00b6 http4k-security-oauth : Allow access token generation to explicitly reject an authorization code already used. H/T @andymoody v3.138.1 \u00b6 http4k-security-oauth : Amend error responses from access token generation. H/T @andymoody v3.138.0 \u00b6 http4k-contracts : Tweaks to Security model for http4k-contracts . (Renamed) ApiKeySecurity is now a proper class, and added BasicAuthSecurity . You can now also override the security model on a per-route basis. http4k-contract : Added ability to set the Security on each individual contract route. This overrides any Security set on a contract-level basis. v3.137.1 \u00b6 http4k-serverless : Allow invocation of serverless functions locally. H/T @Charlyzzz http4k-core : Fix #226 - ResourceLoadingHandler not close stream v3.137.0 \u00b6 http4k-security-oauth : Rename AuthRequestPersistence to AuthRequestTracking v3.136.0 \u00b6 http4k-security-oauth : Allow the http request to be referenced when generating OAuth authorization codes. H/T @andymoody v3.135.0 \u00b6 http4k-core : Change mime.types location so it doesn't conflic with other libraries. H/T @benusher and @dgliosca http4k-testing-chaos : Added SnipRequestBody behaviour. http4k-core : (Small) Breaking Fixed location of some extension files to be relevant to the particular package that they are referencing. This will require reimporting the new location into your source if you were using the imports. v3.134.0 \u00b6 http4k-testing-approval : Made content-type aware approval tests check the content type after the content. This is friendlier for failing tests, as it is more important that the content is correct than the content-type (and often errors don't have content type set so you get an erroneous error message which masks the fact that the content was wrong). v3.133.0 \u00b6 http4k-cloudnative : HandleUpstreamRequestFailed client filter now takes a predicate (Response) -> Boolean instead of a boolean. This allows for more fine grained custom control of which Responses are acceptable. http4k- * : Upgrade deps, including Kotlin to 1.3.30 . http4k-contract : Fix #221 - Contract path fixed segments cannot contain slash characters. v3.132.0 \u00b6 http4k-format-jackson : Convert Jackson to use readValue instead of convertValue . This fixes some problems with type conversions. v3.131.0 \u00b6 http4k-core : (Possible) Break: Made lense implementations Query, Header etc clear previous values by default instead of appending. This leads to a more consistent behaviour. In order to be able to set multiple values on an object using a lense, use the multi form instead - eg. Header.required(\"foo\") -> Header.multi.required(\"foo\") . We envisage the impact of this change is limited as it's only Queries that generally can have multiple possible values, and in the vast majority of cases a replace rather than append is expected. v3.130.0 \u00b6 http4k-contract : Generify contract handling code to allow for custom HttpMessageMeta v3.129.0 \u00b6 (Slight) Break: Collapsed UpstreamRequestFailed exceptions to contain the status, and thus removing non-special cases like BadRequest and BadGateway . This makes them much easier to use in practice as users have access to the the status. To migrate, simply replace previous classes with UpstreamRequestFailed(Status.XYZ, message) . http4k-contract : Open up ContractRoute API to facilitate extension when defining a custom ContractRenderer . http4k- * : Upgrade deps. v3.128.0 \u00b6 http4k-core : Added base64 to the supported mappings for Query/Headers etc... http4k-testing-approval : Approver does not write actual output if there is none to write and there is no approved content v3.127.0 \u00b6 http4k-testing-approval : Improved Approver interface to more closely match the traditional assert approach - this results in a more discoverable/obvious API. http4k-testing-hamkrest : Added ability to create a Hamkrest matcher directly from the Approver instance to be combined with other relevant matchers. v3.126.0 \u00b6 http4k-testing-approval : Add support for XML and HTML approval tests. v3.125.0 \u00b6 Added http4k-testing-approval module, which is compatible with JUnit5 tests and integrates with the OkeyDoke approval testing files and IntelliJ plugin. H/T to @jshiell for the inspiration Gist containing the base Junit5 Extension. v3.124.0 \u00b6 http4k-security-oauth : Make authentication response available when creating AuthorizationCode. v3.123.0 \u00b6 http4k-security-oauth : Introduce OAuthServer to http4k-security-oauth to assist in the creation of authorization servers. v3.122.0 \u00b6 Generified GenerateXmlDataClasses filter, and added default implementations for http4k-format-jackson-xml and http4k-format-xml modules. (Rename) Break: GenerateXmlDataClasses filter in http4k-format-xml is now GsonGenerateXmlDataClasses Removed superfluous CatchLensFailure filter from http4k-contracts module. This is not required as lens failures are already handled by the main contract handler. v3.121.0 \u00b6 Moved Jackson XML support to new module http4k-format-jackson-xml . Note that this is for auto-marshalling of data-classes only and does not expose an XML DOM model. v3.120.0 \u00b6 Deprecated Body.view() lens construction in favour of a Body.viewModel() call which removes the implicitly called toLens() . This allows further mapping from one ViewModel type to another, and brings the view lens construction into line with the rest of the extension functions on Body . Add auto-marshalling XML support to http4k-format-jackson module. Upgrade deps. v3.119.0 \u00b6 Add UpstreamRequestFailed exceptions and HandleUpstreamRequestFailed filters to http4k-cloudnative . These allow apps to neatly deal with upstream failure in a sensible way. v3.118.0 \u00b6 Tweak contract() DSL to add remaining options for configuration. v3.117.0 \u00b6 Renamed ChaosControls (deprecated) to ChaosEngine . v3.116.0 \u00b6 Added new templating module http4k-templates-freemarker . H/T @amcghie for the PR implementing this http4k-contract has a new DSL for construction of the contract which replaces the old one (now deprecated). This is consistent with the meta DSL used to construct individual contract routes and avoids repetition of the old API. We attempted to implement the standard replace-with deprecation, but IntelliJ didn't like it (too complex maybe), so we've hard coded the warning instead which code which should work. Added PreFlightExtraction to contract module, which adds the ability to disable body-checking for contract routes. This will allow refining of routes or entire contracts to be more efficient. Upgrade deps. v3.115.1 \u00b6 Fix #217 - Cannot override the definitionId of a top-level array in OpenAPI Upgrade deps v3.115.0 \u00b6 Chaos now do not blat x-uri-template when used with a RoutingHttpHandler Simplified usage of Once chaos trigger. (Slight break) Consistentified (!) construction of Chaos Behaviours, Stages and Triggers. Replaced singletons with function calls. Eg. Always -> Always() v3.114.0 \u00b6 (Possible Break): Fix #215 - LensFailure does not always include target object. Only change to the API is that IN generic in Lenses is now bounded by IN : Any . This fix is a actually internally consistent as we could not always include the target otherwise (which is an Any? ). Trim leading and trailing whitespace from extracted EnvironmentKey values. Secret value is now only usable once via the use() function. Upgrade to various deps. Removed deprecations. v3.113.0 \u00b6 Added some common types for Environmental setup, and equivalent BiDiLens mappings Handle null response in Java Http client. H/T @FredNordin v3.112.2 \u00b6 Fix #212 - allow null values in HTTP contract definitions. This does mean we lose the type definition for that field, but we don't blow up silently (which was the previous behaviour). H/T @xhanin v3.112.1 \u00b6 Re-add Path.nonEmptyString() which was accidentally removed. v3.112.0 \u00b6 Add support for prohibiting String unmarshalling in JSON auto-marshalling configuration. HTTP Contracts now use the underlying ContractRenderer to produce the BadRequest and NotFound responses. Made OpenAPI open so that these responses can be customised. v3.111.0 \u00b6 Add support for JSON views in Jackson module. H/T @xhanin for the donkey work. v3.110.0 \u00b6 Breaking: slight rearrangement of RouteMeta receiving/returning methods to provide consistency when defining route contracts. v3.109.0 \u00b6 Moved the set of predefined String BiDiMapping instances to their own class. Bulked out the auto-mapping configuration options. v3.108.0 \u00b6 Upgrade to various deps. Extracted out new BiDiMapping type, which encapsulates string <-> type conversions and removes a boatload of duplications. These conversions are now used consistently across all the various places (Lenses, auto-mapping). Improved configurability of AutoMarshallingJson instances. v3.107.0 \u00b6 Upgrade to various deps. Fix #208 - Xml auto deserialisation incorrectly converting strings to numbers v3.106.1 \u00b6 Fix #207 - repeating prefixes in static routes are not handled correctly. H/T @ruXlab for the PR to fix. v3.106.0 \u00b6 Add http4k-server-ktorcio server backend. Note that whilst this module does allow http4k apps to plug into the Ktor-CIO engine, it does not provide fully front-to-back coroutine support. v3.105.0 \u00b6 Preventing FallbackCacheControl from duplicating existing headers. H/T @leandronunes85 Breaking: Make Body.length nullable instead of throwing exception when value is not available. H/T @zvozin v3.104.0 \u00b6 Upgrade to various deps. Add session token support to AWS filter, and \"credentials provider\" to allow for rotating AWS sessions. H/T @dhobbs. Breaking: Moved WsClient from org.http4k.testing to org.http4k.websocket . v3.103.2 \u00b6 Fix access-control-allow-origin returned when server supports multiple origins H/T @johnnorris v3.103.1 \u00b6 (Properly) Fix #198 - Rewrote OpenApi contract to ensure it stays fixed. H/T @reik-wargaming for the help in tracking this down. v3.103.0 \u00b6 \"Fix\" #198 - Breaking change made in http4k-contracts to clarify/deconfuse API. Hid body parameter in contract route meta DSL - it is now receiving() . Upgraded some dependencies, including Gradle to v5.0. Breaking: Resilience4j dependency upgrade causes a break when providing custom config. Simply insert the Config type generic to fix: e.g. RetryConfig.custom() -> RetryConfig.custom() v3.102.1 \u00b6 Fix #197 - Swagger spec for form fields had incorrect description. v3.102.0 \u00b6 Introduce interface for Environment v3.101.0 \u00b6 Upgrades to dependencies Improved Client-side HTTP status descriptions Lenses now support Durations out of the box Environments now support multi-value keys (comma separated) v3.100.0 \u00b6 Make Undertow API friendlier Fix to JsonReadinessCheckResultRenderer to actually implement the correct interface v3.99.0 \u00b6 Enhancement of http4k-cloudnative - now supports extra-health check routes, and provide way to load app configuration via Properties files. v3.98.0 \u00b6 Add filter allowing Gzipping based on an allowed set of content types. H/T @jshiell Change HttpHandler extending HttpClients to use object invoke() mechanism, as the individual clients have no visible API surface of their own. Introduced DualSyncAsyncHttpHandler interface. v3.97.0 \u00b6 Webdriver checkbox handling improved. H/T @gypsydave5 upgrade to various versions v3.96.0 \u00b6 upgrade to Kotlin 1.3.0 v3.95.1 \u00b6 Tweak to K8S port variables. v3.95.0 \u00b6 (Unlikely break): Change Http4kServer interface to return Unit from stop() . This affects all server implementations. Added DSL function for working with JSON objects (scopes JSON as this ). fun Json.invoke(Json.() -> T) New module http4k-cloudnative contains classes to help run http4k services inside cloud-native environments, including K8S. Upgrade some dependencies Deprecation: Moved Header.Common fields to main Header object. Extension properties should go there now. v3.94.1 \u00b6 Use UTC when checking cookie expiry v3.94.0 \u00b6 Deprecate String.toBody() Fix checkbox behaviour in webdriver ~v3.39.4~ v3.93.4 \u00b6 Use Jetty latest release version (rather than RC one) v3.39.3 \u00b6 Fix #189 - Uri toString now omits leading slash if the authority of a Uri is blank. This could be a potential break, but is actually more consistent as a Uri can currently be relative or absolute. v3.39.2 \u00b6 Extend SetBaseUriFrom to support query parameters v3.39.1 \u00b6 Added SetBaseUriFrom filter v3.39.0 \u00b6 (Possible breaking change): Json is now only generified by a single type parameter instead of 2. For most usages, this type would have been identical anyway, but the upgrade of Argo has finally allowed the removal of this dead generic. Simply replace Json with Json