Skip to content

mathieuancelin/heimdallr

Repository files navigation

Heimdallr

A library to build modern http reverse proxies. By default supports http/http2, TLS terminaison, circuit breaker, retry, weighted round robin load balancing, api key access, etc .... Provides a pluggable module system to add new features easily.

Experimental project to try new things on http reverse proxies. Do not use in production ... yet, or not ;)

Get it

you can fetch the last Heimdallr build from bintray an run it

wget -q --show-progress https://dl.bintray.com/mathieuancelin/heimdallr/heimdallr.jar/snapshot/heimdallr.jar
java -jar heimdallr.jar --proxy-config=/path/to/heimdallr.conf

or you can use the latest Docker image

docker run -p "8080:8080"  mathieuancelin-docker-heimdallr-docker.bintray.io/heimdallr:latest

Use it from code

Heimdallr is designed to be embedded and enhanced through pluggable modules

import io.heimdallr.Proxy

object MyOwnProxy {

  def main(args: Array[String]): Unit = {
    Proxy.defaultFromConfigPath("./heimdallr.conf") match {
      case Left(e) => println(s"error while parsing config. $e")
      case Right(proxy) => proxy.stopOnShutdown()
    }
  }
} 
import io.heimdallr.Proxy
import java.io.File

object MyOwnProxy {

  def main(args: Array[String]): Unit = {
    Proxy.defaultFromConfigPath("https://foo.bar/heimdallr.conf") match {
      case Left(e) => println(s"error while parsing config. $e")
      case Right(proxy) => proxy.stopOnShutdown()
    }
  }
}
import io.heimdallr.Proxy
import java.io.File

object MyOwnProxy {

  def main(args: Array[String]): Unit = {
    Proxy.defaultFromConfigFile(new File("./heimdallr.conf")) match {
      case Left(e) => println(s"error while parsing config. $e")
      case Right(proxy) => proxy.stopOnShutdown()
    }
  }
}
import io.heimdallr.Proxy
import io.heimdallr.models._

object MyOwnProxy {

  def main(args: Array[String]): Unit = {
    val proxy = Proxy.defaultWithConfig(ProxyConfig(
      http = HttpConfig(
        httpPort = 8080,
        httpsPort = 8443,
        listenOn = "0.0.0.0",
      ),
      api = ApiConfig(
        httpPort = 9080,
        httpsPort = 9443,
        listenOn = "127.0.0.1",
        certPath = Some("./cert/foo.bar-cert.pem"),
        keyPath = Some("./cert.foo.bar-key.pem"),
        certPass = Some("foo")
      ),
      services = Seq(
        Service(
          id = "load-balancing-test",
          domain = "test.foo.bar",
          targets = Seq(
            Target("http://127.0.0.1:8081"),
            Target("http://127.0.0.1:8082"),
            Target("http://127.0.0.1:8083")
          ),
          additionalHeaders = Map(
            "Authorization" -> "basic 1234"
          ),
          publicPatterns = Set("/*")
        )
      )
    )).stopOnShutdown()
  }
}

Modules

Heimdallr provides extension points to add new feature on top of basic http proxies

Build it

sh ./scripts/build.sh server

or to build everything

sh ./scripts/build.sh all

Helpers

for http2

sbt 
~reStart --- -javaagent:/Users/mathieuancelin/.ivy2/cache/org.mortbay.jetty.alpn/jetty-alpn-agent/jars/jetty-alpn-agent-2.0.6.jar

curl2 -k -H 'Host: test.foo.bar' https://127.0.0.1:8443 --include
curl2 -k --http2-H 'Host: test.foo.bar' https://127.0.0.1:8443 --include
curl2 -k -v -H 'Host: test.foo.bar' https://127.0.0.1:8443 --include
wrk -t1 -c1 -d20s -H "Host: test.foo.bar" http://127.0.0.1:8080/ >> /dev/null
wrk -t2 -c200 -d60s -H "Host: test.foo.bar" --latency http://127.0.0.1:8080/
docker kill $(docker ps -q)
docker run -d -p "8081:80" emilevauge/whoami
docker run -d -p "8082:80" emilevauge/whoami
docker run -d -p "8083:80" emilevauge/whoami

Waiting for

Features

  • built-in kafka support as commands input
  • built-in kafka support as logs output
  • implements more statsd measure points
  • dynamic TLS
  • add extra typesafe attributes to services and apikey for modules ???
  • write some usage docs in readme
  • statsd support
  • handle wildcard matching hosts
  • otoroshi config poll module
  • find a name for the project
  • admin API complete on another port. Add service to serve this api
  • api based on diff commands
  • scan config file for changes and reload
  • docker dev
  • session.sh
  • build.sh
  • travis.yml
  • upload on bintray
  • API to change state of a Proxy instance
  • API to get one service per command
  • read API rest style to get services
  • read API rest style to get one service
  • disable api from config
  • config for state file (period for writes, enabled or not, etc ...)
  • demo mode (--demo) or use config file ./proxy.conf
  • remote config file
  • remote state (with polling support from conf)
  • ProxyConfig object for Api integration
  • withConfig(ProxyConfig)
  • withConfigFrom(path)
  • additional headers
  • metrics JMX or statsd with metrics (maybe statsd integration already provided)
  • apikeys on the service (no quotas), clientId, clientSecret, enabled, name
  • pass if public or apikey
  • api integration + main class
  • save state to file periodically
  • circuit breaker config in service
  • config for ssl
  • matching root
  • target root
  • public / private stuff
  • support for WS
  • support JWT auth
  • start https only if certificate provided
  • smaller https password
  • pass --proxy.config=???
  • pass --proxy.config=???
  • shutdown hook in main
  • handle service access preconditions with pluggable modules
  • handle service access with pluggable modules (apikey + throttling, global throtthling, ip filtering)
  • handle headers out manipulation with pluggable modules
  • handle errors rendering with pluggable modules
  • handle target set choice with pluggable modules