com.soundcloud.twinagle.ServerBuilder.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of twinagle-runtime_2.13 Show documentation
Show all versions of twinagle-runtime_2.13 Show documentation
An implementation of the Twirp protocol on top of Finagle
The newest version!
package com.soundcloud.twinagle
import com.twitter.finagle.http.{Request, Response}
import com.twitter.finagle.{Filter, Service}
/** ServerBuilder can be used to customize the Twinagle HTTP server.
*
* In the simple case, you can use `MyService.server()` to create an HTTP server,
* but if your application needs to satisfy multiple Twirp services or customize
* the HTTP path prefix, you can use `ServerBuilder` to create an HTTP server instead.
*/
class ServerBuilder private (
extension: EndpointMetadata => Filter.TypeAgnostic,
endpoints: Seq[ProtoRpcBuilder],
prefix: String,
messageFilter: MessageFilter
) {
if (prefix.nonEmpty) {
require(prefix.startsWith("/"), "prefix must start with slash")
require(!prefix.endsWith("/"), "prefix must not end with slash")
}
/** register a service with ServerBuilder.
* All service traits generated by twinagle from Protobuf service definitions
* can be used with `register`, since they implement the `AsProtoservice` typeclass.
*/
def register[T: AsProtoService](svc: T): ServerBuilder = {
val protoService = implicitly[AsProtoService[T]].asProtoService(svc)
new ServerBuilder(extension, endpoints ++ protoService.rpcs, prefix, messageFilter)
}
/** withPrefix configures the HTTP path prefix to use for this server (default: `/twirp`).
* Paths must be absolute and may not end with `/`.
* Use an empty string to expose endpoints at the root of the HTTP path.
*/
def withPrefix(prefix: String): ServerBuilder = {
new ServerBuilder(extension, endpoints, prefix, messageFilter)
}
/** withMessageFilter configures the message filter. Such filters can be used
* to observe and modify request and response payloads
* expressed as ScalaPB's GeneratedMessage.
*/
def withMessageFilter(filter: MessageFilter): ServerBuilder = {
new ServerBuilder(extension, endpoints, prefix, filter)
}
/** create an HTTP server that implements the Twirp wire protocol by
* dispatching to the registered services.
*/
def build: Service[Request, Response] =
new Server(endpoints.map(instrumentAndBuild), prefix)
private def instrumentAndBuild(builder: ProtoRpcBuilder): ProtoRpc = {
val rpc = builder.build(messageFilter)
rpc.copy(
svc = extension(rpc.metadata).toFilter andThen
new TracingFilter[Request, Response](rpc.metadata) andThen
rpc.svc
)
}
}
object ServerBuilder {
def apply(
extension: EndpointMetadata => Filter.TypeAgnostic = _ => Filter.TypeAgnostic.Identity,
endpoints: Seq[ProtoRpcBuilder] = Seq.empty,
prefix: String = "/twirp",
messageFilter: MessageFilter = MessageFilter.Identity
): ServerBuilder = new ServerBuilder(extension, endpoints, prefix, messageFilter)
}
trait AsProtoService[T] {
def asProtoService(t: T): ProtoService
}