Giter VIP home page Giter VIP logo

http4k / http4k Goto Github PK

View Code? Open in Web Editor NEW
2.5K 26.0 241.0 26.27 MB

The Functional toolkit for Kotlin HTTP applications. http4k provides a simple and uniform way to serve, consume, and test HTTP services.

Home Page: https://http4k.org

License: Apache License 2.0

Kotlin 97.65% Shell 0.11% HTML 0.41% JavaScript 0.09% CSS 0.17% Java 1.34% Python 0.06% Handlebars 0.06% Pug 0.03% TypeScript 0.08% Dockerfile 0.01%
kotlin http-client typesafe testability tdd http-server http http4k immutability

http4k's Introduction


http4k logo

build download GitHub license codebeat awesome kotlin Kotlin Slack back us! sponsor us!


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 the from 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

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

  • 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 underying 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 - eg. 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:
      • 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
      • 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
  • Cloud Native:
    • Tooling to support operating http4k applications in orchestrated cloud environments such as Kubernetes and CloudFoundry. 12-factor configuration, dual-port servers and health checks such as liveness and readiness checking.
  • Approval 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.
  • 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

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.17.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

Contributors

This project exists thanks to all the people who contribute.

Backers & Sponsors

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.

http4k's People

Contributors

albertlatacz avatar alphaho avatar asemy avatar daviddenton avatar dkandalov avatar dmcg avatar dzappold avatar frednordin avatar github-actions[bot] avatar gypsydave5 avatar hakky54 avatar ivoanjo avatar jenarros avatar jhult avatar jippeholwerda avatar jmfayard avatar jshiell avatar krissrex avatar mbcltd avatar nlochschmidt avatar oharaandrew314 avatar p10r avatar quii avatar razvn avatar rgladwell avatar s4nchez avatar tamj0rd2 avatar time4tea avatar tom avatar zvozin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

http4k's Issues

Support for naming models in Swagger

I'm using the contracts library to generate Swagger definitions for my API, and then using swagger-code-gen to generate client libraries in different languages. Currently, the Swagger model definitions from Http4k look like:

"definitions" : {
  "object-1918755743" : {
    "type" : "object",
    "properties" : {
      "reason" : {
        "type" : "string"
      }
    }
   },
  "object1265469862" : {
    "type" : "object",
    "properties" : {
      "id" : {
        "type" : "integer"
      },
      "title" : {
        "type" : "string"
      }
    }
  }
}

Instead, I'd like to give these models a name, so they're named correctly in our generated Python library as well in our Swagger UI docs.

Netty fails to send all of large response bodies

When a response body is over a certain size, the Netty server implementation does not send all of the response bytes.

For this reason, we are obviously currently not recommending using this Netty server implementation, as this is a blocker.

Coroutine support

Are there plans to support coroutines in http4k?

Specifically, would be great to see an enhanced HttpAsyncClient interface and implementations that offer a suspend function to be used in place of the current invoke with callback. I've prototyped this and seems to work well.

On the http server side, it would be nice to see route support that would allow async responses to be provided. Currently routes are called on the netty worker thread - and if those threads block on IO or otherwise take a long time to complete, then worker threads are unavailable to service other requests. Ideally there would be a coroutine friendly way of specifying routes - allowing the worker thread to go back to its business of dispatching work while leaving the drudgery of creating the response up to coroutines and the threads in their associated contexts. Haven't prototyped this yet - but worming my way through http4k code and have an idea of where this might work.

Filters does not apply to "Route not found" responses

When RoutingHttpHandlers has filters added to it, they will be invoked only when a route is found.

The example below shows that the behaviour is different if the RoutingHttpHandler is casted to a normal HttpHandler.

import org.http4k.core.Filter
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.core.then
import org.http4k.routing.bind
import org.http4k.routing.routes

fun main(args: Array<String>) {
    val loggingFilter = Filter { next -> { request: Request -> next(request).apply { println("Logged status ${this.status}") } } }
    val app = routes("/" bind Method.GET to { Response(Status.OK).apply { println("handled request") } })

    val notFoundRequest = Request(Method.GET, "/not-found")

    println("Filter to routing handler")
    loggingFilter.then(app)(notFoundRequest) // will not log

    println("Filter + generic handler")
    loggingFilter.then(app as HttpHandler)(notFoundRequest) // will log
}

The behaviour for 404 should be the same for both HttpHandler and RoutingHttpHandler.

Does http4k have any benchmarks?

I've written about Kotlin HTTP libraries on my blog and someone mentioned http4k.
I looked at the Techempower benchmarks but http4k was not present. Do you plan to write a benchmark for it or do you happen to have one now?
I'd like to see how http4k performs because the lib itself looks promising.

Don't restrict Undertow server to localhost

The use of "localhost" here means the server won't accept requests from other machines:

val server = Undertow.builder()
.addHttpListener(port, "localhost")
.setHandler(BlockingHandler(HttpUndertowHandler(handler))).build()

This causes the http4k-undertow test in the TechEmpower Framework Benchmarks to fail, as the load-generating client is on a different machine than the application server.

Using "0.0.0.0" or "::" instead of "localhost" should fix that particular issue.

Publish 2.37.0

Documentation refers to version 2.37.0, but that doesn't appear to be available on Maven Central

RequestTracing Filters are setting traces too early

The ServerFilter.RequestTracing should copy the existing (or new) traces onto the request and NOT assign a new spanId/parentSpanId combo. Currently the modification is done in the Server and not done during the ClientFilters.RequestTracing, which means that outgoing requests all receive the same span/parentSpan combo. Change the update to happen in the ClientFilter instead which will assign a new combo to each outgoing request.

Swagger API json file: duplicate key in "definitions"

I found a duplicate key in object definitions, found on the generated Swagger API documentation:

        "object-1161328054": {
            "type": "object",
            "properties": {}
        },
        "object158819726": {
            "type": "object",
            "properties": {
                "id": {
                    "type": "string"
                },
                "attrs": {
                    "$ref": "#/definitions/object-1161328054"
                },
                "created": {
                    "type": "string"
                },
                "updated": {
                    "type": "string"
                }
            }
        },
        "object-1161328054": {
            "type": "object",
            "properties": {}
        },

Cookie date format is taking Locale into account

    java.lang.AssertionError: expected: a value that is equal to "my-cookie=\"my-value\"; Max-Age=37; Expires=Sat, 11 Mar 2017 12:15:21 GMT; Domain=google.com; Path=/; secure; HttpOnly"
    but was: "my-cookie=\"my-value\"; Max-Age=37; Expires=sam., 11 mars 2017 12:15:21 GMT; Domain=google.com; Path=/; secure; HttpOnly"```

Unexpected behaviour combining `path bind staticPath` and `path bind GET to staticPath` routes

E.g. the following test should have no 404 results given that all files exist:

class ATest {
   private val resourceLoader = Directory(“/some/local/directory/“)

   @Test
   fun `a`() {
       val routes = routes(
           “/path1” bind static(resourceLoader),
           “/path2" bind static(resourceLoader)
       )
       val response1 = routes(Request(GET, “/path1/index.html”))
       val response2 = routes(Request(GET, “/path2/index.html”))
       assertThat(“${response1.status.code} ${response2.status.code}“, equalTo(“200 200”))
   }

   @Test
   fun `b`() {
       val routes = routes(
           “/path1" bind GET to static(resourceLoader),
           “/path2” bind static(resourceLoader)
       )
       val response1 = routes(Request(GET, “/path1/index.html”))
       val response2 = routes(Request(GET, “/path2/index.html”))
       assertThat(“${response1.status.code} ${response2.status.code}“, equalTo(“200 404”))
   }

   @Test
   fun `c`() {
       val routes = routes(
           “/path1" bind static(resourceLoader),
           “/path2” bind GET to static(resourceLoader)
       )
       val response1 = routes(Request(GET, “/path1/index.html”))
       val response2 = routes(Request(GET, “/path2/index.html”))
       assertThat(“${response1.status.code} ${response2.status.code}“, equalTo(“404 200”))
   }

   @Test
   fun `d`() {
       val routes = routes(
           “/path1" bind GET to static(resourceLoader),
           “/path2” bind GET to static(resourceLoader)
       )
       val response1 = routes(Request(GET, “/path1/index.html”))
       val response2 = routes(Request(GET, “/path2/index.html”))
       assertThat(“${response1.status.code} ${response2.status.code}“, equalTo(“404 404”))
   }
}

Use single function to start a server

Currently one needs to do:

app.asServer(Jetty(800)).start().block()

It shouldn't require all that. A start(block:Boolean = true) should make the API simpler and less error prone.

Request.uri doesn't have full context

Hi!

I'm a total newbie with both Kotlin and http4k, but loving how it looks. Kudos!

I was poking around at the TodoBackend implementation, and noticed that you're hard-coding the base url for the hypermedia links in the generated API. Digging in, it looks like that's necessary because Request.uri doesn't have things like host and port populated, at least when running against the Servlet backend. Am I right that this is the motivation for the hard coding?

I think the root cause for this missing context in the request is because the conversion from a Servlet request to an internal http4k request is using getRequestUri, and per the servlet API docs that will not include the full URL. They say "To reconstruct an URL with a scheme and host, use HttpUtils#getRequestURL".

Would you be interested in a PR to fix this? And am I on the right track for solving it, or is there a nicer way?

Upgrade to Kotlin 1.2?

Hi,

Are there any plans to upgrade to Kotlin v1.2?

Some of the new features introduced with v1.2 can be found on Kotlin's blog.

-Kevin

Moshi support fails when serializing collections

Example JUnit 5 Test that fails:

class MoshiTest {

    @Test
    fun `will throw an exception`() {
        val moshi = com.squareup.moshi.Moshi.Builder().build()
        // this works and outputs ["foo"] as expected
        println(moshi.adapter(List::class.java).failOnUnknown().toJson(listOf("foo")))

        // this fails
        println(org.http4k.format.Moshi.asJsonString(listOf("foo")))
    }

}

The error message is java.lang.IllegalArgumentException: Platform class java.util.Collections$SingletonList annotated [] requires explicit JsonAdapter to be registered

The same exception is thrown when trying to use a Lens for a collection class

The same if you pass multiple list elements, except it will be ArrayList that it complains about. The root cause is that http4k's Moshi wrapper always passes an object's actual class to Moshi instead of the interface class (List in this case). Moshi, however, will not serialize implementations, it deliberately only serializes against the interface in order to avoid tieing us to one specific implementation on one specific JVM version.

This probably affects other collections like maps etc as well.

Expected: http4k's Moshi adapter allows me to pass the desired interface, e.g. List::class, explicitly to properly serialize the collection.

Context: http4k 3.6.0, Moshi 1.5.0

Prometheus Monitoring

Is there any plans to develop a Prometheus module
that would expose relevant statistics automatically?

Would love a Java example

I think the docs or the code would benefit greatly from a pure java (JDK8+) example.
I like the framework a lot but curious to see how it feels in a pure java codebase.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.