Note: the most up-to-date examples are located in the finatra/examples project.

See examples/hello-world for a simple example HTTP Server.

HTTP Server Definition

To start, add a dependency on the finatra-http library. We also recommend using Logback as your SLF4J implementation. E.g.,

with sbt:

"com.twitter" %% "finatra-http" % "18.9.0",
"ch.qos.logback" % "logback-classic" % versions.logback,

For more information on logging with Finatra see: Introduction to Logging With Finatra.

Create a new class which extends c.t.finatra.http.HttpServer:

import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter

object ExampleServerMain extends ExampleServer

class ExampleServer extends HttpServer {

  override def configureHttp(router: HttpRouter): Unit = {
    ???
  }
}

A more complete example includes adding Modules, a Controller, and Filters.

import DoEverythingModule
import ExampleController
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.filters.{CommonFilters, LoggingMDCFilter, TraceIdMDCFilter}
import com.twitter.finatra.http.routing.HttpRouter

object ExampleServerMain extends ExampleServer

class ExampleServer extends HttpServer {

  override val modules = Seq(
    DoEverythingModule)

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .filter[LoggingMDCFilter[Request, Response]]
      .filter[TraceIdMDCFilter[Request, Response]]
      .filter[CommonFilters]
      .add[ExampleController]
  }
}

Simplistically, a server can be thought of as a collection of controllers composed with filters. Additionally, a server can define modules for providing instances to the object graph and how to map exceptions to HTTP responses.

Creating an HTTPS Server

Finatra provides a default for the defaultHttpPort of ”:8888” which means that Finatra will always attempt to start a non-ssl HTTP server on port 8888 if no other configuration is done. The framework allows for users to specify starting an HTTPS server, either additionally or instead.

An HTTPS server can be started by passing in a value for the -https.port flag or overriding the defaultHttpsPort with a non-empty value. To configure the underlying Finagle c.t.finagle.Http.Server transport correctly, override the configureHttpsServer method in your HttpServer definition. E.g.,

import com.twitter.finagle.Http
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter

object ExampleHttpsServerMain extends ExampleHttpsServer

class ExampleHttpsServer extends HttpServer {

  override val defaultHttpsPort: String = ":443"

  // HTTP server configuration
  override def configureHttpServer(server: Http.Server): Http.Server = {
    server
      .withResponseClassifier(???)
      .withMaxInitialLineSize(???)
  }

  // HTTPS server configuration
  override def configureHttpsServer(server: Http.Server): Http.Server = {
    server
      .withResponseClassifier(???)
      .withMaxInitialLineSize(???)
      .withTransport.tls(???)
  }

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .add[ExampleController]
  }
}

For convenience, a Tls trait is provided which encapsulates standard TLS configuration for an HTTPS server. Thus you can also do:

import com.twitter.finagle.Http
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter

object ExampleHttpsServerMain extends ExampleHttpsServer

class ExampleHttpsServer
  extends HttpServer
  with Tls {

  override val defaultHttpsPort: String = ":443"

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .add[ExampleController]
  }
}

Disabling the Default HTTP Server

As mentioned, the above configuration will still attempt to start a non-ssl HTTP server. To disable the non-ssl HTTP server, override the defaultHttpPort value to an empty String (and do not pass a value for the -http.port flag), e.g.,

import com.twitter.finagle.Http
import com.twitter.finatra.http.HttpServer
import com.twitter.finatra.http.routing.HttpRouter

object ExampleHttpsServerMain extends ExampleHttpsServer

class ExampleHttpsServer
  extends HttpServer
  with Tls {

  override val defaultHttpPort: String = "" // disable the default HTTP port
  override val defaultHttpsPort: String = ":443"

  override def configureHttp(router: HttpRouter): Unit = {
    router
      .add[ExampleController]
  }
}

Naming Convention

The Finatra convention is to create a Scala object with a name ending in “Main” that extends your server class. The server class can be used in testing as this allows your server to be instantiated multiple times in tests without worrying about static state persisting across test runs in the same JVM. The static object, e.g., ExampleServerMain, which contains the static main method for the server would then be used as the application entry point for running the server in all other cases.

Override Default Behavior

Flags

Some deployment environments may make it difficult to set Flag values with command line arguments. If this is the case, Finatra’s HttpServer‘s core flags can be set from code.

For example, instead of setting the -http.port flag, you can override the following method in your server.

class ExampleServer extends HttpServer {

  override val defaultHttpPort: String = ":8080"

  override def configureHttp(router: HttpRouter): Unit = {
    ...
  }
}

For a list of what flags can be set programmatically, please see the BaseHttpServer class.

Framework Modules

You can override some of the modules provided by default in HttpServer.

An example use-case would be to provide a custom Jackson module implementation in place of the default FinatraJacksonModule.

To do so you would override the protected def jacksonModule in your server.

class ExampleServer extends HttpServer {

  override def jacksonModule = MyCustomJacksonModule

  override def configureHttp(router: HttpRouter): Unit = {
    ...
  }
}

If your module is defined as a class, you would pass an instance of the class, e.g.,

override def jacksonModule = new MyCustomJacksonModule

Finagle Server Configuration

If you want to further configure the underlying Finagle server you can override configureHttpServer (or configureHttpsServer) in your server and to additional configuration on, or override the default configuration of the underlying Finagle server.

For example:

class ExampleServer extends HttpServer {

  override def configureHttp(router: HttpRouter): Unit = {
    ...
  }

  override def configureHttpServer(server: Http.Server): Http.Server = {
    server
      .withMaxRequestSize(...)
      .withAdmissionControl.concurrencyLimit(
        maxConcurrentRequests = ...,
        maxWaiters = ...
  }
}

For more information on Finagle server configuration see the documentation here; specifically the server documentation here.

Testing

For information on testing an HTTP server see the HTTP Server Feature Tests section.