{"id":25065485,"url":"https://github.com/pchchv/mrps","last_synced_at":"2026-02-15T20:34:08.249Z","repository":{"id":275480624,"uuid":"926200038","full_name":"pchchv/mrps","owner":"pchchv","description":"Mini reverse proxy server","archived":false,"fork":false,"pushed_at":"2025-02-09T08:39:21.000Z","size":119,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-19T03:51:10.686Z","etag":null,"topics":["api","axum","axum-server","cors","cors-proxy","https","minininja","minininjas","proxy","proxy-server","reqwest","reverse-proxy","reverse-proxy-server","rust","rust-crate","self-contained","server","single-binary","webserver","zero-configuration"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/mrps","language":"Rust","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/pchchv.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-02-02T19:32:45.000Z","updated_at":"2025-02-09T08:38:53.000Z","dependencies_parsed_at":"2025-02-06T08:39:10.144Z","dependency_job_id":"1db64ce9-5b7a-4394-9330-dfa435d20ca6","html_url":"https://github.com/pchchv/mrps","commit_stats":null,"previous_names":["pchchv/rps","pchchv/mrps"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/pchchv/mrps","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fmrps","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fmrps/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fmrps/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fmrps/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pchchv","download_url":"https://codeload.github.com/pchchv/mrps/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pchchv%2Fmrps/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29488621,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T19:29:10.908Z","status":"ssl_error","status_checked_at":"2026-02-15T19:29:10.419Z","response_time":118,"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":["api","axum","axum-server","cors","cors-proxy","https","minininja","minininjas","proxy","proxy-server","reqwest","reverse-proxy","reverse-proxy-server","rust","rust-crate","self-contained","server","single-binary","webserver","zero-configuration"],"created_at":"2025-02-06T19:27:24.783Z","updated_at":"2026-02-15T20:34:08.234Z","avatar_url":"https://github.com/pchchv.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n[![Version](https://img.shields.io/crates/v/mrps)](https://crates.io/crates/mrps)\n[![License: Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-red)](https://github.com/pchchv/mrps/blob/main/LICENSE)\n[![Downloads](https://img.shields.io/crates/d/mrps)](https://crates.io/crates/mrps)\n\n# MRPS — *mini [reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy) server.*\n\n## Features\n- [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS)\n- HTTPS\n- Safe rust\n- Static file server\n- Optional configuration file can be written in\n[JSON](https://www.json.org/json-en.html) or\n[TOML](https://toml.io/en/)\n- Additional [minijinja](https://github.com/mitsuhiko/minijinja) templates with custom functions\n- No panics after startup (every panic is a bug)\n- Good debugging experience (server displays requests and error messages in human-readable templates)\n\n## [MiniJinja](https://github.com/mitsuhiko/minijinja) templates\n- [Reverse proxy](https://en.wikipedia.org/wiki/Reverse_proxy)\n- Execute commands in the template\n- Send HTTP requests in the template\n- Parse and format to [JSON](https://www.json.org/json-en.html), [TOML](https://toml.io/en/) and [FormData](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)\n- Read, write and remove files from the filesystem\n- Modify the response headers and status in the template\n\n## Docs\n### config\nCommand line arguments take priority over the configuration file if are both present.  \n\nCommand line argument paths are relative to the current working directory.\n\n`config` paths are relative to your directory.\n\nWhen making changes to `config`, the server must be restarted.\n\n#### port: integer?\nOptional integer port number on which the server will run, default: 3000\n\n#### all: bool\nWhether to display hidden files.\n\nIf confirmed via the command line or the `config` file, they will be\ndisplayed.\n\n#### ignore: [string]?\nA list of files to ignore.\n\n[glob](https://github.com/devongovett/glob-match) expressions are used.\n\nIf the -i option is passed on the command line, it will be added to the list.\n\nRoutes must be considered in relation to the assets folder, not the working directory.\n\n#### cors: [string]?\nOptional array of strings representing allowed origins for [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) requests.\n\nAn empty array allows all origins.\n\nIf this variable is not defined,[CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) will be disabled.\n\n#### cert: string?\nOptional string with the public key file path for the https server.\n\nOnly if the `cert` and `key` are available will the server run over https.\n\n#### key: string?\nOptional string containing the path to the private key file for the https server.\n\nOnly if the `cert` and `key` are available will the server run over https.\n\n#### assets: string?\nOptional string with the static files folder path.\n\n#### templates: string?\nOptional string with the path to the [minijinja](https://github.com/mitsuhiko/minijinja) templates folder.\n\n#### data: string?\nOptional string with the path where templates can `read`, `write` and `remove` files. If not passed, these functions will be unavailable to templates.\n\n#### routes: [{method, path, template}]\nOptional array of objects that define routes:\n- `method` string: one of the http methods:\n  - GET\n  - POST\n  - DELETE\n  - PUT\n  - PATCH\n  - HEAD\n  - OPTIONS\n  - TRACE\n  - CONNECT\n- `path` string: path associated with the route, `:var` is acceptable for setting path variables (i.e: /api/user/:id).\n- `template` string: path to the template associated with this route in the `templates` folder.\n\n### Template variables\n\n#### method: string\n`method` associated with this `route`.\nIt is useful when the same template is used in many `routes`.\n\n#### url: string\nIt is the junction of the `path` and the `route` `query`.\n\n```http://localhost:3000/api/users?name=john#me =\u003e /api/users?name=john```\n\n#### route: string\nIt is the `route` as declared in the `config` file.\n\n```/api/user/:id```\n\n#### path: string\nAssociated `path` passed by the client in the request.\n\n```http://localhost:3000/api/users?name=john =\u003e /api/users```\n\n#### query: string?\nAssociated `query` string passed by the client in the request.\n\n```http://localhost:3000/api/users?name=john =\u003e name=john```\n\n#### params: {name: value}\nAssociated object of the `path` `params` associated with the client request on a given `route`.\n\n- `name` string: name of the parameter as declared in the `route`.\n- `value` string: value of the parameter passed in the `path`.\n\n```/api/user/:id =\u003e http://localhost:3000/api/user/25 =\u003e {\"id\": \"25\"}```\n\n#### vars: {name: value}\nAssociated object of the `query` params associated with the client request.\n\n- `name` string: The name of the parameter passed in the `query`\n- `value` string: The value of the parameter passed in the `query`\n\n```http://localhost:3000/api/users?name=john =\u003e {\"name\": \"john\"}```\n\n#### headers: {name: value}\nAssociated object of the headers passed by the client in the request.\n\nNote that all header keys are in **lowercase**.\n\n- `name` string: name of the header passed in the request\n- `value` string: value of the header passed in the request\n\n```Content-Type: text/plain =\u003e {\"content-type\": \"text/plain\"}```\n\n#### body: binary\nBody passed by the client in the request.\n\n### Template return state\nVariables that, if defined, modify the behavior of the server response.\n\nIt only works if they are **declared outside the blocks** to be returned in the template's global state.\n\n#### modify {status, headers: {name: value}}\nThe response body is always the result of the template, and this variable allows you to modify the status code and headers.\n\n- `status` (integer?): new response status code, if not passed, will use 200 by default\n- `headers` ({name: value}?): headers that should be changed in the response\n\nAn example of a redirect.\n```jinja\n{% set modify = {\"status\": 303, \"headers\": {\"Location\": \"/new/location\"}} %}\n```\n\n#### proxy {url, method, headers: {name, value}, body}\nUses a proxy instead of the template result.\n\n- `url` (string): proxy URL, is required\n- `method` (string?): method used for the proxy request (by default, the method passed in the original request)\n- `headers` ({name: value}?): headers that should be changed in the proxy request (by default, do not change any header)\n- `body` (binary?): body of the proxy request (by default, the original body)\n\nA simple proxy that retains the request method, headers, body and path and just directs it to another host.\n```jinja\n{% set proxy = {\"url\": \"https://another.host.ip\"~url} %}\n```\n\n### Custom functions\n\n#### command (cmd) -\u003e {code, stdout, stdin}\nExecutes a command passed in the template.\n\nThis function does not raise errors, in case of failure it returns the `code` `999999`, and the error message.\n\n- `cmd` string: command to be executed by the system\n- `code` integer: response code, in general zero indicates OK, and a number greater than zero the error code\n- `stdout` binary: standard output of the executed command\n- `stderr` binary: error message returned\n\nList files in the current directory on UNIX systems.\n```jinja\n{% set res = command(\"ls -l\") %}\n{% set output = res.stdout | parse(\"text\") %}\n```\n\n#### read (file) -\u003e data\nReads the contents of a file, if it does not exist returns `None`.\n\nThis function does not raise errors, any read error will return `None`.\n\nIt will only be available if the `config` file contains the `data` property with the folder that contains the files that can be read and modified.\n\n- `file` string: path of the file to read\n- `data` binary?: contents of the file or `None` in case of errors\n\n```jinja\n{% set content = read(\"some/file.json\") | parse(\"json\") %}\n```\n\n#### read (dir: string) -\u003e [{...info}]\nThis function also works with a directory, which in this case will return an array with information about the files contained in it.\n\n- `dir` string: if the path passed is a directory\n\n**info**\n- `accessed` string: last access date (%Y-%m-%d %H:%M:%S)\n- `created` string: creation date (%Y-%m-%d %H:%M:%S)\n- `modified` string: modification date (%Y-%m-%d %H:%M:%S)\n- `is_dir` bool: 'true' if it is a directory\n- `is_file` bool: 'true' if it is a file\n- `is_symlink` bool: 'true' if it is a symbolic link\n- `name` string: entry name\n- `len` u64: size in bytes\n\n```jinja\n{% set content = read(\"some/dir\") %}\n{% for entry in content %}\n  {{entry.name}}\n{% endfor %}\n```\n\n#### write (file, data) -\u003e error\nWrites to a file. Create folders for the file if necessary. Always overwrites the contents if they exist.\n\nIf an error occurs, it returns the error text, otherwise `None`. Thus, it does not cause errors.\n\nWill only be available if the `config` file contains a `data` property specifying a folder containing files that can be read and modified.\n\n- `file` string: file path\n- `data` binary: raw data to be written\n- `error` string?: error message or `None`\n\n```jinja\n{% set data = \"Hello world!\" %}\n{{write(\"some/file.txt\", data | bytes)}}\n```\n\n#### remove (entry) -\u003e error\nRemoves a file or directory recursively.\n\nIf an error occurred, the error text will be returned, otherwise `None`. Thus, it does not cause errors.\n\nWill only be available if the `config` file contains a `data` property `data` property specifying a folder containing files that can be read and modified.\n\n- `entry` string: path of the file or directory to be removed\n- `error` string?: error message or `None`\n\n```jinja\n{{remove(\"some/dir\")}}\n```\n\n```jinja\n{{remove(\"some/file.txt\")}}\n```\n\n#### {method} (url, body) -\u003e {status, headers, body}\nSends a synchronous request to an external resource.\n\nThis function does not raise errors, any error in the request will be returned `status` with code `400` and a `body` containing an error message.\n\n- `url` string: URL of the request\n- `body` binary: body of the request\n- `status` integer: HTTP status code of the response\n- `headers` {`name` string: `value` string}: response headers\n- `body` binary: response body\n- `method`:\n  - `get` (url) -\u003e {status, headers, body}\n  - `delete` (url) -\u003e {status, headers, body}\n  - `head` (url) -\u003e {status, headers, body}\n  - `options` (url) -\u003e {status, headers, body}\n  - `post` (url, body) -\u003e {status, headers, body}\n  - `put` (url, body) -\u003e {status, headers, body}\n  - `patch` (url, body) -\u003e {status, headers, body}\n\n```jinja\n{% set response = get(\"https://some/api\") %}\n{% set data = response.body | parse(\"json\") %}\n```\n\n```jinja\n{% set body = \"some data\" %}\n{% set response = post(\"https://some/api\", body | bytes) %}\n{% set message = response.body | parse(\"text\") %}\n```\n\n#### log (message) -\u003e ()\nPrints a message from the template on the terminal.\n\n- `message` string: content of the message\n\n```jinja\n{{ log(\"hi!\") }}\n```\n\n### Custom filters\n\n#### parse (data, encoding) -\u003e result\nConverts the raw data returned by some function into a template variable using the passed encoding.\n\nThis function outputs an `error` message if an unsupported encoding is used or if decoding fails.\n\nIn case of an error, it returns a request with the `status` code `500`.\n\n- `data` binary: raw data returned from some function\n- `encoding` string: encoding to be used when reading the data:\n  - form: [FormData](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)  \n  - json: [JSON](https://www.json.org/json-en.html)\n  - toml: [TOML](https://toml.io/en/)\n  - text: just transforms the data into text\n- `result`: value supported by the template with associated data\n\n```jinja\n{% set data = read(\"some/file.txt\") | parse(\"text\") %}\n```\n\n```jinja\n{% set response = get(\"https://some/api\") %}\n{% set data = response.body | parse(\"json\") %}\n```\n\n#### format (data, encoding) -\u003e text\nConverts a template variable to a formatted string.\n\nThis function raises an `error` if an unsupported encoding is used or if the encoding fails.\n\nIn case of an error, it returns a request with the `status` code `500`.\n\n- `data`: any template variable\n- `encoding` string: type of encoding to be adopted when formatting the text:\n  - form: [FormData](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST)  \n  - json: [JSON](https://www.json.org/json-en.html)\n  - toml: [TOML](https://toml.io/en/)\n  - debug: uses rust pretty print formatter\n- `text` string: text after encoding\n\n```jinja\n{% set data = {\"name\": \"John\", \"age\": 30} %}\n{% set text = data | format(\"form\") %}\n{{text}}\n```\n\n```name=John\u0026age=30```\n\n#### bytes (data) -\u003e raw\nConverts text to binary format.\n\n- `data` string: any text\n- `raw` binary: text converted to binary\n\n```jinja\n{% set error = write('hello.txt', 'Hello World!' | bytes) %}\n```\n\n```jinja\n{% set response = post('http://ip/some/api', 'Hello World!' | bytes) %}\n```\n\n## Dependencies\n- [clap](https://github.com/clap-rs/clap)\n- [hurl](https://github.com/Orange-OpenSource/hurl)\n- [axum](https://github.com/tokio-rs/axum)\n- [serde](https://github.com/serde-rs/serde)\n- [reqwest](https://github.com/seanmonstar/reqwest)\n- [minijinja](https://github.com/mitsuhiko/minijinja)\n- [glob-match](https://github.com/devongovett/glob-match)\n\nA huge thank you to everyone who contributed to these projects.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchchv%2Fmrps","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpchchv%2Fmrps","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpchchv%2Fmrps/lists"}