Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-httpclient

Named HTTP clients for Pudu: a client factory, delegating handlers, pooled keep-alive connections, handler lifetimes, logging, and resilience

0.1.1Apache-2.01

InstallClose

Handlers.pudu

Pudu74 lines3.4 KB

GitHub ↗
1/** @Examples.Handlers.Program — delegating handlers, logging, metrics, and event listeners */2module Handlers34import Std.Concurrent.Cancel as Cancel5import Std.Concurrent as Concurrent6import Std.Http.Server.Reply as Reply7import Std.Http.Server.Route as Route8import Std.Http.Server as Server9import Std.Io as Io10import PuduLangHttpClient.Client as Client11import PuduLangHttpClient.Events as Events12import PuduLangHttpClient.Factory.Builder as Builder13import PuduLangHttpClient.Factory as Factory14import PuduLangHttpClient.Handler as Handler15import PuduLangHttpClient.Handlers.Logging as Logging16import PuduLangHttpClient.Handlers.Metrics as Metrics17import PuduLangHttpClient.Handlers.Propagation as Propagation18import PuduLangHttpClient as HttpClient19import PuduLangHttpClient.Request as Request20import PuduLangHttpClient.Response as Response21import PuduLangLog.Configuration as Configuration22import PuduLangLog as Log23import PuduLangLog.Sinks.Console as Console2425/// A handler stamping every request with an identifier and timing its response.26fn stamping(request: Request.Request, token: Cancel.Token, next: Handler.Send) -> HttpClient.Outcome[Response.Response] {27  let began = clock()28  let response = next(request.setHeader("x-request-id", "req-" + show(began)), token) ?29  Ok(response.withHeader("x-client-elapsed", show(clock() - began)))30}3132/// Starts a local server echoing the request identifier and answers its address.33fn serve() -> (Server.Running, Str) {34  let running = match Server.start("127.0.0.1", 0) {35    case Ok(found) => found36    case Err(problem) => panic(show(problem))37  }38  let server = Server.server(&Route.routing(&[Route.get("/whoami", |request: Route.Request| Reply.text(200, match Route.header(&request, "x-correlation-id") {39              case Some(found) => found40              case None => "nobody"41            }))]))42  let _serving = Concurrent.start(fn() -> () { let _ran = Server.run(&server, &running, 0) })43  (running, "http://127.0.0.1:" + show(Server.portOf(&running)) + "/")44}4546/// Sends requests through a pipeline of handlers, answering 0 when every layer did its part.47fn main() -> Int {48  let (running, base) = serve()49  let logger = Configuration.create().minimumLevel(Log.Information).writeTo(Console.sink(Console.defaults())).createLogger()50  let meter = Metrics.meter()51  let incoming = || [("x-correlation-id", "order-1234")]52  let options = Factory.Options{listeners: Events.listeners().onRequestCompleted(fn(event: Events.RequestCompleted) -> () {53        let _said = Io.writeLine("completed " + event.client + " " + event.uri + " with " + show(event.status))54      }) }55  let factory = match Factory.buildWith(&options, [56      Builder.defaults().observedBy(Logging.observer(logger)).withHandler(Metrics.handler(&meter)),57      Builder.named("api").withBaseAddress(base).withHandler(stamping).withHandler(Propagation.handler(&["x-correlation-id"], incoming))58    ]) {59    case Ok(built) => built60    case Err(invalid) => panic(Factory.explain(&invalid))61  }62  let client = Factory.createClient(&factory, "api")63  let answered = Client.getText(&client, "whoami")64  let response = Client.get(&client, "whoami")65  let stamped = match response {66    case Ok(found) => Response.header(&found, "x-client-elapsed") != None67    case Err(_) => false68  }69  let snapshot = Metrics.snapshot(&meter)70  Factory.dispose(&factory)71  let _stopped = Server.stop(&running)72  if answered == Ok("order-1234") && stamped && snapshot.successful == 2 { 0 } else { 1 }73}74