{"id":37122482,"url":"https://github.com/pierods/dynaroutes","last_synced_at":"2026-01-14T14:09:28.492Z","repository":{"id":57526851,"uuid":"96530437","full_name":"pierods/dynaroutes","owner":"pierods","description":"dynaroutes is a reverse proxy with scripted Go, inspired by Zuul","archived":false,"fork":false,"pushed_at":"2017-11-05T22:33:12.000Z","size":155,"stargazers_count":6,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-06-20T15:43:19.358Z","etag":null,"topics":["ab-testing","dynamic","go","golang","reverse-proxy","router","scripted"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pierods.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":"2017-07-07T11:02:02.000Z","updated_at":"2021-07-20T11:36:19.000Z","dependencies_parsed_at":"2022-09-07T02:51:52.317Z","dependency_job_id":null,"html_url":"https://github.com/pierods/dynaroutes","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/pierods/dynaroutes","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pierods%2Fdynaroutes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pierods%2Fdynaroutes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pierods%2Fdynaroutes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pierods%2Fdynaroutes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pierods","download_url":"https://codeload.github.com/pierods/dynaroutes/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pierods%2Fdynaroutes/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28422408,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T13:30:50.153Z","status":"ssl_error","status_checked_at":"2026-01-14T13:29:08.907Z","response_time":107,"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":["ab-testing","dynamic","go","golang","reverse-proxy","router","scripted"],"created_at":"2026-01-14T14:09:27.789Z","updated_at":"2026-01-14T14:09:28.468Z","avatar_url":"https://github.com/pierods.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ![logo](assets/logo.png) \n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![](https://godoc.org/github.com/pierods/dynaroutes?status.svg)](http://godoc.org/github.com/pierods/dynaroutes)\n[![Go Report Card](https://goreportcard.com/badge/github.com/pierods/dynaroutes)](https://goreportcard.com/report/github.com/pierods/dynaroutes)\n[![Build Status](https://travis-ci.org/pierods/dynaroutes.svg?branch=master)](https://travis-ci.org/pierods/dynaroutes)\n\ndynaroutes is a reverse proxy for dynamic routing with scripted Go, inspired by [Zuul](https://github.com/Netflix/zuul). It features:\n\n+ dynamic filters - written in Go and compiled on the fly. \n+ directory-based or consul-based filter loading/unloading/editing\n\n## Use cases\ndynaroutes is useful for:\n+ A/B testing at full speed - only compiled code is used\n+ Quick scaffolding of microservice architectures\n+ On-the-fly routing changes, hot reload of filters\n+ load balancing\n+ surgical routing\n+ statistics gathering\n+ authentication\n+ logging, monitoring etc.\n\n\n## What is a filter\n\n![router](assets/dynaroutes.png)\n\nA filter is a small program that is applied either at the moment when a request comes into the reverse proxy (\"pre filters\") or after \na proxy request has returned (\"post filters\") - but before the final response is sent out.\nMany pre and post filters can be added to dynaroutes. Every filter has got an order, and they are apllied in that order.\n\n### pre filters\n\nPre filters have this interface:\n\n```Go\n    type PreFilter interface {\n        Name() string\n        Order() int\n        Description() string\n        //Filter returns a Route in case it decides to route the request, nil otherwise.\n        Filter(request *http.Request) (*Route, error)\n    }\n```\nPre filters are able to determine where a request will be proxied to. They have the complete, original request to make that decision:\n\n```Go\n// Route is returned by pre filters to set the reverse proxy request parameters\n    type Route struct {\n        Scheme  string\n        Host    string\n        URI     string\n        Port    int\n        Method  string\n        Timeout time.Duration\n}\n```\nPre filters are applied (Filter() is called) until a filter returns a route. Then the router stops applying filters and forwards the request \nto the desired endpoint, as described by the returned Route. A Route can set the Scheme/host/URI/Port/Method/Timeout of a request.\n\n### post filters\n\nAfter the proxy request has returned, post filters are called. A post filter has this interface:\n\n```Go\n    //PostFilter is applied before a response is sent back to the client. Many post filter can be defined, and they are applied in the order defined by Order(), however as soon as a non-nil []byte is returned, the router stops applying post filters.\n    type PostFilter interface {\n        Name() string\n        Order() int\n        Description() string\n        //Filter can return nil, or a []byte, in which case that's what will be sent back to the client\n        Filter(request, proxyRequest *http.Request, proxyResponse *http.Response) ([]byte, error)\n    }\n\n```\nEvery post filter has at its disposal the original request, the proxied request (the proxied address can be seen for example) and the proxied \nresponse.\nIn case a post filter decides it wants to modify the response returned by the router, it can return a []byte with the desired response. \nThe router will stop applying filters and send that response to the client instead.\n\n### where are filters found\n\ndynaroutes can be instantiated in file mode or consul mode.\nIf you have acces to a directory on the host machine, then do this:\n```bash\n    dynarouter -port 8080 \\\n        -interface xxx.xxx.xxx.xxx \\\n        -readtimeout 10 \\\n        -writetimeout 10 \\\n        -filterdir  \"/path/to/directory\" \n        -consolehost xxx.xxx.xxx.xxx\n        -consoleport 30000\n```\n\nthen write your filters and drop them in that directory. You can then either edit them in place, or re-drop them or delete them and any change\nwill be picked up by dynaroutes. Filters must have names ending in .go, like \"myprefilter.go\".\n\nIf you prefer to use consul, and consul is running on machine consulhost:consulport, then do this:\n```bash\n    dynarouter -port 8080 \\\n        -interface xxx.xxx.xxx.xxx \\\n        -readtimeout 10 \\\n        -writetimeout 10 \\\n        -consul  true\n        -consulport consulport\n        -consulhost  consulhost\n        -consulprefix  \"com.github.pierods.dynaroutes.filters\"\n        -consolehost xxx.xxx.xxx.xxx\n        -consoleport 30000\n```\ndynaroutes will watch the consulprefix key and pick up filters from there. Sub-keys must have names ending in .go, like \"myprefilter.go\"\n\n### accessing the console\nPoint your browser to consolehost:consoleport (default localhost:30000):\n\n![console](assets/console.png)\n\n\nYou can also observe the code of the filters:\n![word map](assets/consolecode.png)\n\n### how do filters look\n\nHere is a sample pre filter:\n```Go\npackage main\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\n\t\"github.com/pierods/dynaroutes\"\n)\n\ntype EchoRoutePreFilter struct{}\n\nfunc (srf *EchoRoutePreFilter) Filter(request *http.Request) (*dynaroutes.Route, error) {\n\n\tnewCtx := context.WithValue(request.Context(), \"filterkey\", \"this value will be available to other pre filters and post filters\")\n\t*request = *request.WithContext(newCtx)\n\treturn \u0026dynaroutes.Route{\n\t\tScheme: \"http\",\n\t\tHost:   \"localhost\",\n\t\tURI:    request.URL.String(),\n\t\tPort:   50000,\n\t\tMethod: request.Method,\n\t}, nil\n}\nfunc (srf *EchoRoutePreFilter) Order() int {\n\treturn 100\n}\n\nfunc (srf *EchoRoutePreFilter) Description() string {\n\treturn \"A simple route filter - forwards to localhost:50000\"\n}\n\nfunc (srf *EchoRoutePreFilter) Name() string {\n\treturn \"EchoRoutePreFilter\"\n}\n\nvar PreFilter dynaroutes.PreFilter = \u0026EchoRoutePreFilter{}\n\nfunc main() {}\n\n\n```\nA pre filter:\n\n+ must be in package main\n+ must have an exported var called PreFilter, implementing dynarouter.Prefilter\n+ can have a func main() {} stub, to make it compilable on the local machine\n\n\nHere is a sample post filter:\n\n```Go\npackage main\n\nimport (\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\n\t\"github.com/pierods/dynaroutes\"\n)\n\ntype SimplePostFilter struct{}\n\nfunc (spf *SimplePostFilter) Filter(request, proxyRequest *http.Request, proxyResponse *http.Response) ([]byte, error) {\n\n\tlog.Print(\"post filter: original request:\", request.URL.String())\n\tlog.Println(\" was forwarded to:\", proxyRequest.URL.String())\n\tlog.Println(request.Context().Value(\"filterkey\"))\n\trespB, err := ioutil.ReadAll(proxyResponse.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn append(respB, []byte(\" - filtered\")...), nil\n}\n\nfunc (spf *SimplePostFilter) Order() int {\n\treturn 10\n}\n\nfunc (spf *SimplePostFilter) Description() string {\n\treturn \"A simple post filter - logs basic info\"\n}\n\nfunc (spf *SimplePostFilter) Name() string {\n\treturn \"SimplePostFilter\"\n}\n\nvar PostFilter dynaroutes.PostFilter = \u0026SimplePostFilter{}\n\nfunc main() {}\n\n```\n\nA post filter:\n\n+ must be in package main\n+ must have an exported var called PostFilter, implementing dynarouter.Postfilter\n+ can have a func main() {} stub, to make it compilable on the local machine\n\n\n## Installation\nCompile and build dynaroutes: get and install [pluginator](github.com/pierods/pluginator)\n\n```bash\n\n    go get github.com/hashicorp/consul/api\n    go get github.com/fsnotify/fsnotify\n    go get github.com/pierods/pluginator\n    \n    cd pluginator\n    go install\n```\n\nget and install dynaroutes:\n```bash\n   \n    go get github.com/pierods/embedder\n    go get github.com/pierods/dynaroutes\n    \n    cd dynaroutes\n    go generate\n    cd dynarouter\n    go install\n   \n```\nMake sure there is a go tooolchain on the host machine (the machine that will run the router) - basically, untar a go distro and put its bin folder in the PATH. \nInstructions [here](https://golang.org/doc/install?download=go1.8.3.linux-amd64.tar.gz).\n\n## Notes\n### why no route filters\n\nIn Zuul, there are pre, route and post filters. Problem is, route filters do the actual request proxying themselves. \nSee for example [SimpleHostRoutingFilter.groovy](https://github.com/Netflix/zuul/blob/1.x/zuul-simple-webapp/src/main/groovy/filters/route/SimpleHostRoutingFilter.groovy):\n```java\n    ...\n    switch (verb) {\n                case 'POST':\n                    httpRequest = new HttpPost(uri + getQueryString())\n                    InputStreamEntity entity = new InputStreamEntity(requestEntity)\n                    httpRequest.setEntity(entity)\n                    break\n                case 'PUT':\n                    httpRequest = new HttpPut(uri + getQueryString())\n                    InputStreamEntity entity = new InputStreamEntity(requestEntity, request.getContentLength())\n                    httpRequest.setEntity(entity)\n                    break;\n                default:\n                    httpRequest = new BasicHttpRequest(verb, uri + getQueryString())\n    }\n    ...\n```\nSimpleHostRoutingFilter.groovy has 435 lines of scripted code - not ideal from a maintenance standpoint. Also, it is not clear to me why every \nroute filter should reimplement a router, and what is the advantage of doing so. dynaroutes does the routing after pre filters have run.\n\nOne benefit of having route filters is that there can be many of them, and multiple routing can be done. While this can be still done without writing \nmultiple reverse proxies (a route filter could simply be handed the response of the last call and return another route or not) it is still debatable\nwhether a dynamic reverse proxy should also act as an API gateway, and instead route to a special purpose API gateway instead. Should such demand arise,\nthis functionality could be implemented.\n\n### why no lua/javascript filters\nThere exist scripting engines for Go with target languages [javascript](https://github.com/robertkrimen/otto) and [lua](https://github.com/Shopify/go-lua). \nHowever, since Go can be compiled and loaded on the fly, and filters are not full-fledged programs, but bits of functionality, there is no clear\nadvantage in having filters written in another language. \nThere are clear disadvantages though:\n+ one must learn and use another language\n+ there is a performance loss in using a scripting engine\n+ scripting engines occupy memory and take CPU cycles\n+ scripting engines tend to fall behind on features of their target language\n+ scripting engines lose expressivity when compared to their host language, since the host language APIs must be exposed through a different language\n\n### limitations\ndynaroutes only works with Go \u003e= 1.8, on linux. Plugins can be written under any OS though.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpierods%2Fdynaroutes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpierods%2Fdynaroutes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpierods%2Fdynaroutes/lists"}