Created
August 24, 2016 14:07
-
-
Save jayhutfles/b09e8d2d71aab08ac516029efd2c487d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Akka HTTP and Streams notes: | |
// Working with akka.http.scaladsl.Http: | |
Http().bind( | |
interface: String, | |
port: Int | |
): Source[Http.IncomingConnection, Future[ServerBinding]] | |
//- creates an akka.stream.scaladsl.Source[Http.IncomingConnection, Future[ServerBinding]] | |
//- DOESN'T ACTUALLY START HANDLING REQUESTS. You have to compose the Source in a Flow to a Sink and RUN IT. | |
/** NOTES: | |
* | |
* Http.IncomingConnection has the following methods which run the flows immediately upon calling: | |
* def handleWith[Mat](handler: Flow[HttpRequest, HttpResponse, Mat]): Mat | |
* def handleWithSyncHandler(handler: HttpRequest => HttpResponse): Unit | |
* def handleWithAsyncHandler(handler: HttpRequest => Future[HttpResponse], parallelism: Int): Unit | |
* | |
* Http.IncomingConnection is also constructed with a flow: Flow[HttpResponse, HttpRequest, NotUsed] | |
* this is the flow that feeds responses back to the associated requests | |
* flow.joinMat(handler) will join them together so things flow from end-to-end | |
*/ | |
Http().bindAndHandle( | |
handler: Flow[HttpRequest, HttpResponse, Any], | |
interface: String, | |
port: Int | |
): Future[ServerBinding] | |
//- calls bind() to get a Source[Http.IncomingConnection, Future[ServerBidning]] | |
//- composes binding with handler and IncomingConnection's .flow value to create RunnableGraph | |
//- then runs it | |
//- STARTS HANDLING REQUESTS | |
Http().bindAndHandleSync( | |
handler: HttpRequest ⇒ HttpResponse, | |
interface: String, | |
port: Int | |
): Future[ServerBinding] | |
//- creates binding to interface/port | |
//- creates flow using handler function like this: | |
Flow[HttpRequest].map(handler) | |
//- calls bindAndHandle with that flow | |
//- WHICH STARTS HANDLING REQUESTS | |
Http().bindAndHandleAsync( | |
handler: HttpRequest ⇒ Future[HttpResponse], | |
interface: String, | |
port: Int, | |
parallelism: Int | |
): Future[ServerBinding] | |
//- creates binding | |
//- creates flow using handler function like this: | |
Flow[HttpRequest].mapAsync(parallelism)(handler) | |
//- calls bindAndHandle with that flow | |
//- WHICH STARTS HANDLING REQUESTS | |
/** So... How does it work that these take akka.http.scaladsl.server.Route as arguments? | |
* | |
* HOW ARE ROUTES IMPLICITLY CONVERTED TO FLOWS? */ | |
// TL;DR: a magic combination of these | |
import akka.http.scaladsl.server.Directives._ | |
implicit val materializer = ActorMaterializer() | |
// LONG VERSION: | |
// Per akka-http\src\main\scala\akka\http\scaladsl\server\package.scala: | |
type Route = RequestContext ⇒ Future[RouteResult] | |
// But the Route companion object has this gem: | |
/** | |
* Turns a `Route` into a server flow. | |
* | |
* This conversion is also implicitly available through [[RouteResult#route2HandlerFlow]]. | |
*/ | |
def handlerFlow(route: Route)(implicits...): Flow[HttpRequest, HttpResponse, NotUsed] = | |
Flow[HttpRequest].mapAsync(1)(asyncHandler(route)) | |
// So, tracking where the transitive imports end up lead me here: | |
import akka.http.scaladsl.server.Directives._ // which includes | |
akka.http.scaladsl.server.CodingDirectives // which calls | |
import akka.http.scaladsl.server.MiscDirectives // whose companion object includes | |
import RouteResult._ | |
// But that still didn't explain all of the steps, because if | |
type Route = RequestContext => Future[RouteResult] | |
// then Flow[HttpRequest].mapAsync should take a | |
HttpRequest => Future[HttpResponse], | |
// not a | |
RequestContext => Future[RouteResult] | |
// which means that the Route companion object's asyncHandler method does it. Sure enough, it's right there: | |
request ⇒ | |
sealedRoute(new RequestContextImpl(request, routingLog.requestLog(request), routingSettings, effectiveParserSettings)).fast | |
.map { | |
case RouteResult.Complete(response) ⇒ response | |
case RouteResult.Rejected(rejected) ⇒ throw new IllegalStateException(s"Unhandled rejections '$rejected', unsealed RejectionHandler?!") | |
} | |
// that fun stuff says given an HttpRequest: | |
//- it lifts the request into a RequestContext | |
//- runs the RequestContext through the route (remember, it's a RequestContext => Future[RouteResult] ?) | |
//- and then pattern matches to extract the completed or rejected HttpResponse. | |
// CONCLUSIONS: | |
// - The Route DSL is really just a bunch of convenience for Flow.mapAsync calls. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment