{"id":47444101,"url":"https://github.com/JuliaWeb/Mux.jl","last_synced_at":"2026-04-06T13:00:57.365Z","repository":{"id":28120517,"uuid":"31619500","full_name":"JuliaWeb/Mux.jl","owner":"JuliaWeb","description":"Middleware for Julia","archived":false,"fork":false,"pushed_at":"2024-06-28T14:51:37.000Z","size":192,"stargazers_count":280,"open_issues_count":17,"forks_count":67,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-09-27T01:47:07.483Z","etag":null,"topics":["middleware","webserver"],"latest_commit_sha":null,"homepage":null,"language":"Julia","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JuliaWeb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2015-03-03T20:25:28.000Z","updated_at":"2025-06-26T13:58:35.000Z","dependencies_parsed_at":"2024-01-23T04:30:05.524Z","dependency_job_id":"3125e8b7-f99c-40c8-ab06-28c496dd7a3b","html_url":"https://github.com/JuliaWeb/Mux.jl","commit_stats":{"total_commits":151,"total_committers":37,"mean_commits":4.081081081081081,"dds":0.7350993377483444,"last_synced_commit":"816fde79b119815e390147d73d1a36d2b2dea22e"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"purl":"pkg:github/JuliaWeb/Mux.jl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaWeb%2FMux.jl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaWeb%2FMux.jl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaWeb%2FMux.jl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaWeb%2FMux.jl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JuliaWeb","download_url":"https://codeload.github.com/JuliaWeb/Mux.jl/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JuliaWeb%2FMux.jl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31473271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T08:36:52.050Z","status":"ssl_error","status_checked_at":"2026-04-06T08:36:51.267Z","response_time":112,"last_error":"SSL_read: 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":["middleware","webserver"],"created_at":"2026-03-23T06:00:59.902Z","updated_at":"2026-04-06T13:00:57.345Z","avatar_url":"https://github.com/JuliaWeb.png","language":"Julia","funding_links":[],"categories":["Web Security"],"sub_categories":["HTTP and Web Frameworks"],"readme":"# Mux.jl\n\n[![Build Status](https://api.travis-ci.com/JuliaWeb/Mux.jl.svg?branch=master)](https://travis-ci.com/JuliaWeb/Mux.jl)\n[![Build status](https://ci.appveyor.com/api/projects/status/iuyp5jrre7s905ay/branch/master?svg=true)](https://ci.appveyor.com/project/shashi/mux-jl/branch/master)\n[![codecov.io](https://codecov.io/github/JuliaWeb/Mux.jl/coverage.svg?branch=master)](https://codecov.io/github/JuliaWeb/Mux.jl?branch=master)\n\n```jl\nPkg.add(\"Mux\")\n```\n\nMux.jl gives your Julia web services some closure. Mux allows you to\ndefine servers in terms of highly modular and composable components\ncalled middleware, with the aim of making both simple and complex\nservers as simple as possible to throw together.\n\nFor example:\n\n```jl\nusing Mux\n\n@app test = (\n  Mux.defaults,\n  page(respond(\"\u003ch1\u003eHello World!\u003c/h1\u003e\")),\n  page(\"/about\",\n       probability(0.1, respond(\"\u003ch1\u003eBoo!\u003c/h1\u003e\")),\n       respond(\"\u003ch1\u003eAbout Me\u003c/h1\u003e\")),\n  page(\"/user/:user\", req -\u003e \"\u003ch1\u003eHello, $(req[:params][:user])!\u003c/h1\u003e\"),\n  Mux.notfound())\n\nserve(test)\n```\n\nYou can run this demo by entering the successive forms into the Julia\nREPL. The code displays a \"hello, world\" at `localhost:8000`, with an\nabout page at `/about` and another hello at `/user/[your name]`.\n\nThe `@app` macro allows the server to be redefined on the fly, and you\ncan test this by editing the `hello` text and re-evaluating. (don't\nre-evalute `serve(test)`)\n\nNote that `serve(test)` launches an asynchronous `Task` that will not prevent Julia from terminating.\nThis is good at the REPL, but not always what you want.\nIf you want Julia to wait for the task to finish, use `wait(serve(test))`.\n\n## Technical Overview\n\nMux.jl is at heart a control flow library, with a [very small core](https://github.com/one-more-minute/Mux.jl/blob/master/src/Mux.jl#L7-L16). It's not important to understand that code exactly as long as you understand what it achieves.\n\nThere are three concepts core to Mux.jl: Middleware (which should be familiar\nfrom the web libraries of other languages), stacking, and branching.\n\n### Apps and Middleware\n\nAn *app* or *endpoint* is simply a function of a request which produces\na response:\n\n```jl\nfunction myapp(req)\n  return \"\u003ch1\u003eHello, $(req[:params][:user])!\u003c/h1\u003e\"\nend\n```\n\nIn principle this should say \"hi\" to our lovely user. But we have a\nproblem – where does the user's name come from? Ideally, our app\nfunction doesn't need to know – it's simply handled at some point up the\nchain (just the same as we don't parse the raw HTTP data, for example).\n\nOne way to solve this is via *middleware*. Say we get `:user` from a cookie:\n\n```jl\nfunction username(app, req)\n  req[:params][:user] = req[:cookies][:user]\n  return app(req) # We could also alter the response, but don't want to here\nend\n```\n\nMiddleware simply takes our request and modifies it appropriately, so\nthat data needed later on is available. This example is pretty trivial,\nbut we could equally have middleware which handles authentication and\nencryption, processes cookies or file uploads, provides default headers,\nand more.\n\nWe can then call our new version of the app like this:\n\n```jl\nusername(myapp, req)\n```\n\nIn fact, we can generate a whole new version of the app which has username\nsupport built in:\n\n```jl\nfunction app2(req)\n  return username(myapp, req)\nend\n```\n\nBut if we have a lot of middleware, we're going to end up with a lot of `appX` functions.\nFor that reason we can use the `mux` function instead, which creates the new app for us:\n\n```jl\nmux(username, myapp)\n```\n\nThis returns a *new* function which is equivalent to `app2` above. We\njust didn't have to write it by hand.\n\n### Stacking\n\nNow suppose you have lots of middleware – one to parse the HTTP request into\na dict of properties, one to check user authentication, one to catch errors,\netc. `mux` handles this too – just pass it multiple arguments:\n\n```jl\nmux(todict, auth, catch_errors, app)\n```\n\nAgain, `mux` returns a whole new app (a `request -\u003e response` function)\nfor us, this time wrapped with the three middlewares we provided.\n`todict` will be the first to make changes to the incoming request, and\nthe last to alter the outgoing response.\n\nAnother neat thing we can do is to compose middleware into more middleware:\n\n```jl\nmymidware = stack(todict, auth, catch_errors)\nmux(mymidware, app)\n```\n\nThis is effectively equivalent to the `mux` call above, but creating a\nnew middleware function from independent parts means we're able to\nfactor out our service to make things more readable. For example, Mux\nprovides the `Mux.default` middleware which is actually just a stack of\nuseful tools.\n\n`stack` is self-flattening, i.e.\n\n```jl\nstack(a, b, c, d) == stack(a, stack(b, c), d) == stack(stack(a, b, c), d)\n```\n\netc.\n\n### Branching\n\nMux.jl goes further with middleware, and expresses routing and decisions\nas middleware themselves. The key to this is the `branch` function,\nwhich takes\n\n1. a predicate to apply to the incoming request, and\n2. an endpoint to run on the request if the predicate returns true.\n\nFor example:\n\n```jl\nmux(branch(_ -\u003e rand() \u003c 0.1, respond(\"Hello\")),\n    respond(\"Hi\"))\n```\n\nIn this example, we ignore the request and simply return true 10% of the time.\nYou can test this in the repl by calling\n\n```jl\nmux(branch(_ -\u003e rand() \u003c 0.1, respond(\"Hello\")),\n    respond(\"Hi\"))(nothing)\n```\n\n(since the request is ignored anyway, it doesn't matter if we set it to `nothing`).\n\nWe can also define a function to wrap the branch\n\n```jl\nprobability(x, app) = branch(_ -\u003e rand() \u003c x, app)\n```\n\n### Utilities\n\nDespite the fact that endpoints and middleware are so important in Mux,\nit's common to not write them by hand. For example, `respond(\"hi\")`\ncreates a function `_ -\u003e \"hi\"` which can be used as an endpoint.\nEqually, functions like `status(404)` will create middleware which\napplies the given status to the response. Mux.jl's \"not found\" endpoint\nis therefore defined as\n\n```jl\nnotfound(s = \"Not found\") = mux(status(404), respond(s))\n```\n\nwhich is a much more declarative approach.\n\nFor example:\n\n* `respond(x)` – creates an endpoint that responds with `x`, regardless of the request.\n* `route(\"/path/here\", app)` – branches to `app` if the request location matches `\"/path/here\"`.\n* `page(\"/path/here\", app)` – branches to `app` if the request location *exactly* matches `\"/path/here\"`\n\n## Serving static files from a package\n\nPlease use [AssetRegistry.jl](https://github.com/JuliaGizmos/AssetRegistry.jl) to\nregister an assets directory.\n\n**DEPRECATED**: The `Mux.pkgfiles` middleware (included in `Mux.defaults`) serves static\nfiles under the `assets` directory in any Julia package at `/pkg/\u003cPACKAGE\u003e/`.\n\n## Integrate with WebSocket\n\nYou can easily integrate a general HTTP server and a WebSocket server with Mux.\nTo do so, define two apps, one for regular HTTP requests, and another that will handle WebSocket connections.\n\nHere is a complete example:\n\n```julia\nusing Mux\nusing Mux.WebSockets: send\n\n# HTTP Server\n@app h = (\n    Mux.defaults,\n    page(\"/\", respond(\"\u003ch1\u003eHello World!\u003c/h1\u003e\")),\n    Mux.notfound());\n\nfunction websocket_example(x)\n    sock = x[:socket]\n    for str in sock\n        println(\"client -\u003e server: \", str)\n        send(sock, \"I'm hard of hearing, did you say '$str'?\")\n    end\nend\n\n# WebSocket server\n@app w = (\n    Mux.wdefaults,\n    route(\"/ws_io\", websocket_example),\n    Mux.wclose,\n    Mux.notfound());\n\n# Serve both servers on the same port.\nserve(h, w, 2333)\n```\n\nAnd finally, run a client, optionally in another process:\n\n```julia\nusing Mux.WebSockets\n\nWebSockets.open(\"ws://localhost:2333/ws_io\") do ws\n    send(ws, \"Hello World!\")\n    data = receive(ws)\n    println(stderr, \"server -\u003e client: \", data)\nend;\n```\n\nNow, if you run both programs, you'll see two `Hello World` messages, as the\nserver sends the same message back to the client.\n\n## Using Mux in Production\n\nWhile Mux should be perfectly useable in a Production environment, it is not\nrecommended to use the `Mux.defaults` stack for a Production application. The\n`basiccatch` middleware it includes will dump potentially sensitive stacktraces\nto the client on error, which is probably not what you want to be serving to\nyour clients! An alternative `Mux.prod_defaults` stack is available for\nProduction applications, which is just `Mux.defaults` with a `stderrcatch`\nmiddleware instead (which dumps errors to stderr).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJuliaWeb%2FMux.jl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJuliaWeb%2FMux.jl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJuliaWeb%2FMux.jl/lists"}