{"id":42965384,"url":"https://github.com/overthink/circlet","last_synced_at":"2026-01-30T23:56:19.759Z","repository":{"id":57726476,"uuid":"56898796","full_name":"overthink/circlet","owner":"overthink","description":"Scala HTTP server abstraction in the style of Ring","archived":false,"fork":false,"pushed_at":"2016-08-22T05:12:46.000Z","size":163,"stargazers_count":6,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2023-07-01T13:05:10.546Z","etag":null,"topics":["continuation-passing-style","handlers","middleware","rack","ring","scala","web-framework","wsgi"],"latest_commit_sha":null,"homepage":null,"language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/overthink.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-04-23T03:39:26.000Z","updated_at":"2019-11-20T08:14:06.000Z","dependencies_parsed_at":"2022-09-26T21:50:49.299Z","dependency_job_id":null,"html_url":"https://github.com/overthink/circlet","commit_stats":null,"previous_names":[],"tags_count":2,"template":null,"template_full_name":null,"purl":"pkg:github/overthink/circlet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overthink%2Fcirclet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overthink%2Fcirclet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overthink%2Fcirclet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overthink%2Fcirclet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/overthink","download_url":"https://codeload.github.com/overthink/circlet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/overthink%2Fcirclet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28923736,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T22:32:35.345Z","status":"ssl_error","status_checked_at":"2026-01-30T22:32:31.927Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["continuation-passing-style","handlers","middleware","rack","ring","scala","web-framework","wsgi"],"created_at":"2026-01-30T23:56:19.134Z","updated_at":"2026-01-30T23:56:19.749Z","avatar_url":"https://github.com/overthink.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circlet\n\n[![Build Status](https://travis-ci.org/overthink/circlet.svg?branch=master)](https://travis-ci.org/overthink/circlet)\n\n```scala\nimport com.markfeeney.circlet.Circlet.handler\nimport com.markfeeney.circlet.{JettyAdapter, Response}\n\nval app = handler { req =\u003e Response(body = \"Hello world!\") }\nJettyAdapter.run(app)\n```\n\nCirclet is a simple Scala web application library.  Circlet allows you to\nwrite your application as a plain function of type `Request =\u003e\nOption[Response]` (also called a `Handler`).\n\nComposable middleware functions of type `Handler =\u003e Handler` are used to add\nreusable functionality to handlers.  Circlet includes some useful middleware\nlike parameter parsing (including multipart, i.e. file upload), and cookie\nhandling. For routing and higher-level functionality, see\n[Usher](https://github.com/overthink/usher).\n\nCirclet attempts to abstract away the underlying web server, but it's somewhat\ntheoretical, since only Jetty is currently supported.\n\nCirclet also supports websockets, [see below](#websockets).\n\nCirclet aims to be in roughly the same space as other web server interfaces\nlike [WSGI](https://wsgi.readthedocs.io/en/latest/),\n[Rack](http://rack.github.io/), [WAI](https://github.com/yesodweb/wai), and\n[Ring](https://github.com/ring-clojure/ring).  In fact, Ring is the inspiration for\nCirclet, and large parts of Circlet are ported directly from it.\n\n## Try it\n\nLatest release:\n\n```scala\nlibraryDependencies += \"com.markfeeney\" % \"circlet_2.11\" % \"0.2.0\"\n```\n\nThere is also an [example circlet project](https://github.com/overthink/circlet-example) \nshowing how to get a running Circlet app.\n\n## Project Goals\n\nIn priority order:\n\n1. Small: few classes, few dependencies, no special tooling\n1. Composable\n1. Maintainable code\n1. Type safe enough\n1. Fast enough\n\n## Design\n\nHandlers and middleware must be implemented in continuation-passing \nstyle ([CPS](https://en.wikipedia.org/wiki/Continuation-passing_style)).\n\n```scala\ntype Cont = Option[Response] =\u003e Sent.type\ntype Handler = Request =\u003e Cont =\u003e Sent.type\ntype Middleware = Handler =\u003e Handler\n```\n\ni.e. you need to write this:\n\n```scala\n// respond is the continuation fn\nval cpsHelloWorld: Handler = request =\u003e respond =\u003e {\n  val resp = Response(body = \"hello world\")\n  respond(resp)\n}\n```\n\ninstead of something like this:\n\n```scala\n// doesn't compile\nval helloWorld: Handler = request =\u003e {\n  Response(body = \"hello world\")\n}\n```\n\nWorrying about the continuation can be a pain in the neck sometimes, so\n[helpers](src/main/scala/com/markfeeney/circlet/Circlet.scala#L23) are\navailable for cases where you don't need the extra features CPS affords. e.g.\n\n```scala\nval helloWorld = handler { req =\u003e Response(body = \"hello world\") }\n```\n\nI've gone the CPS route so handlers can allocate \"request scoped\" resources\nand properly clean them up when request processing is complete.  As an\nexample, Circlet's multipart parameter middleware uses this to [clean up temp\nfiles](src/main/scala/com/markfeeney/circlet/middleware/MultipartParams.scala#L163-L167)\ncreated when handling file uploads.\n\nHere's another (contrived) example: streaming a response from the database.\n\n```scala\nval streamingHandler: Handler = req =\u003e respond =\u003e {\n  val conn = // get a db connection\n  try {\n    val resp = Response(body = SeqBody(makeLazySeq(req, conn)))\n    respond(resp)\n  } finally {\n    conn.close() // response has been sent, close connection\n  }\n}\n```\n\nCirclet's CPS approach is borrowed from\n[WAI](https://hackage.haskell.org/package/wai-3.2.1/docs/Network-Wai.html)\n(thanks [stebulus](https://github.com/stebulus)).\n\n## \u003ca name=\"websockets\"\u003e\u003c/a\u003eWebsockets\n\nAs of 0.2.0, Criclet supports websockets.  These are somewhat bolted on,\nunfortunately: they're Jetty-specific and do not take part in middleware.  Regular handlers still\nbehave as normal, but a separate mapping of paths to websocket implementations\nis provided to the server at creation time.  Here is a simple example that\nreturns whatever it is sent, converted to uppercase.\n\n```scala\nimport com.markfeeney.circlet.Circlet.handler\nimport com.markfeeney.circlet.{JettyAdapter, JettyOptions, Response}\nimport com.markfeeney.circlet.JettyWebSocket\n\nval upper = JettyWebSocket(\n  onText = (session, msg) =\u003e session.getRemote.sendString(msg.toUpperCase)\n)\nval opts = JettyOptions(webSockets = Map(\"/upper/\" -\u003e upper))\nval app = handler { req =\u003e Response(body = \"websocket example, connect to ws://upper/\") }\nJettyAdapter.run(app, opts)\n```\n\n## TODO\n\n* Better understand websocket threading model\n* Async handlers? (not yet convinced that I care)\n* Make websockets use [JSR 356](https://jcp.org/en/jsr/detail?id=356) rather than Jetty-specific classes\n* Support other web servers besides Jetty?\n\n## Known issues\n\n* A CPS handler could call the continuation function multiple times.  I'm not\n  sure how to prevent this, (or if I should) so don't do it.  Unless you need\n  to.\n\n## Other options\n\nThere are a lot of Scala web libraries and frameworks out there.  Here are\nsome that I feel are close in spirit to circlet.\n\n* [http4s](http://http4s.org/) - very similar to circlet, focus on async, heavy use of scalaz\n* [finagle](https://twitter.github.io/finagle/) - Twitter's big system for building high-concurrency servers\n\n## License                                                                                                                                                                            \n                                                                                                                                                                                      \nCopyright \u0026copy; 2016 Mark Feeney\n                                                                                                                                                     \nReleased under the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverthink%2Fcirclet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foverthink%2Fcirclet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foverthink%2Fcirclet/lists"}