3个版本

0.1.2 2024年8月5日
0.1.1 2023年10月20日
0.1.0 2023年10月19日

#109 in HTTP服务器

Download history 3/week @ 2024-07-06 5/week @ 2024-07-27 119/week @ 2024-08-03 10/week @ 2024-08-10

每月下载量 134次

MIT 许可证

52KB
386

Mini RPS

用Rust编写的微型反向代理服务器

特性 ❤️

  • 非常快速的单个二进制文件,无依赖项
  • 静态文件服务器
  • 反向代理路由器
  • HTTPS
  • CORS
  • 使用minijinja模板消费任何API数据并创建自定义响应
  • 使用hurl进行广泛测试

动机 💡

在我看来,构建服务器并适应动态市场需求最好的方法是应用Linux哲学。

每个服务器或微服务执行一个特定且定义良好的功能。

因此,我决定编写一个微服务,我可以快速配置它来提供文件,并编排其他服务。

安装 💻

cargo install minirps

或者您可以使用每个版本中提供的预编译二进制文件(目前仅限于通用Linux)。

用法 🎮

minirps -h

简单的静态文件服务器

minirps path/to/static/folder

提供隐藏文件

minirps -a path/to/static/folder

忽略根目录中的Markdown文件

minirps -i "/*.md" path/to/static/folder

忽略任何Markdown文件

minirps -i "/**/*.md" path/to/static/folder

在4000端口而不是3000端口上运行

minirps -p 4000 path/to/static/folder

使用https而不是http

minirps -p 4000 path/to/static/folder -c path/to/cert.pem -k path/to/key.pem

允许所有来源的CORS

minirps -o -p 4000 path/to/static/folder -c path/to/cert.pem -k path/to/key.pem

使用config.toml文件启动服务器

这里通过命令行传递的可能配置的极限已达到。

为了创建更复杂和有趣的示例,我们需要一个config.toml文件

minirps -f path/to/config.toml

忽略根目录中的任何Markdown文件和以secret_开头的文件

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
ignore = [
  "/**/*.md",
  "/secret_*"
]

允许我的网站进行CORS

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = [
  "https://www.my-website.com"
]

允许来自我的网站的不同来源进行CORS

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = [
  "http://www.my-website.com",
  "https://www.my-website.com",
  "http://www.my-other-website.com",
  "https://www.my-other-website.com"
]

允许所有来源的CORS

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = []

将一个反向代理添加到运行在https://127.0.0.1:8000的API服务器

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = []

# GET https://127.0.0.1:4000/api/users => GET https://127.0.0.1:8000/users
[[routes]]
method = "GET"
path = "/api/users"

[[routes.requests]]
method = "GET"
url = "https://127.0.0.1:8000/users"

# PUT https://127.0.0.1:4000/api/users/21 => PUT https://127.0.0.1:8000/users/21
[[routes]]
method = "PUT"
path = "/api/users/:id"

[[routes.requests]]
method = "PUT"
url = "https://127.0.0.1:8000/users/{{params.id}}"
body = "{{body}}"

发送纯文本响应而不是API响应

config.toml

assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = []

# GET https://127.0.0.1:4000/api/users => GET https://127.0.0.1:8000/users
[[routes]]
method = "GET"
path = "/api/users"

[[routes.requests]]
name = "users"
method = "GET"
url = "https://127.0.0.1:8000/users"

[routes.response]
body = """
{% for user in data.users.json %}
  {{user.name}}
{% endfor %}"""
headers = { Content-Type = "text/plain" }

# PUT https://127.0.0.1:4000/api/users/21 => PUT https://127.0.0.1:8000/users/21
[[routes]]
method = "PUT"
path = "/api/users/:id"

[[routes.requests]]
name = "result"
method = "PUT"
url = "https://127.0.0.1:8000/users/{{params.id}}"
body = "{{body}}"

[routes.response]
body = "{% if data.result.status == 200 %}SUCCESS!{% else %}ERROR!{% endif %}"
headers = { Content-Type = "text/plain" }

发送HTML模板响应而不是API响应

config.toml

templates = "path/to/templates/folder"
assets = "path/to/static/folder"
port = 4000
cert = "path/to/cert.pem"
key = "path/to/key.pem"
cors = []

# GET https://127.0.0.1:4000/api/users => GET https://127.0.0.1:8000/users
[[routes]]
method = "GET"
path = "/api/users"

[[routes.requests]]
name = "users"
method = "GET"
url = "https://127.0.0.1:8000/users"

[routes.response]
body = "{% include 'users.html' %}"
headers = { Content-Type = "text/html" }

# PUT https://127.0.0.1:4000/api/users/21 => PUT https://127.0.0.1:8000/users/21
[[routes]]
method = "PUT"
path = "/api/users/:id"

[[routes.requests]]
name = "result"
method = "PUT"
url = "https://127.0.0.1:8000/users/{{params.id}}"
body = "{{body}}"

[routes.response]
body = "{% include 'edit.html' %}"
headers = { Content-Type = "text/html" }

示例 🧪

静态服务器与CORS

在这个例子中,创建了一个静态服务器,并添加了一个CORS请求以供展示。

静态服务器

minirps assets

CORS服务器

minirps assets/tests -o -p 4000 -c assets/certs/cert.txt -k assets/certs/key.txt

starwars

在这个例子中使用了minijinja模板来从swapi的《星球大战》API中获取数据。

minirps -f examples/starwars.toml

测试

在这个例子中,构建了一个静态服务器和一些路由,用于测试使用hurl自动反向代理和模板的使用。

minirps -f examples/test.toml
hurl --test examples/test.hurl

文档 📖

config.toml

如果同时存在,则命令行参数优先于配置文件。

命令行参数路径相对于当前工作目录。

config.toml路径相对于您的目录。

目前,对config.toml的任何更改都必须重新启动服务器才能应用。

port: 整数?

运行服务器上的可选整数端口号,默认:3000

all: 布尔值

是否显示隐藏文件。

如果通过命令行或config.toml确认,则将显示。

ignore: [string]?

使用glob表达式忽略的文件列表。

如果命令行上传递了-i选项,它将被追加到列表中。

路由必须与资产文件夹相关,而不是工作目录。

有关glob表达式的完整参考和可能的错误,请查看此

cors: [string]?

表示允许的CORS请求来源的字符串数组的可选值。

空数组允许所有来源。

如果未定义此变量,则禁用CORS

cert: 字符串?

可选字符串,包含https服务器的公钥文件路径。

只有当certkey可用时,服务器才会通过https运行。

key: 字符串?

可选字符串,包含https服务器的私钥文件路径。

只有当certkey可用时,服务器才会通过https运行。

assets: 字符串?

可选字符串,包含静态文件文件夹的路径。

templates: 字符串?

可选字符串,包含minijinja模板文件夹的路径。

routes: [{method: string, path: string, requests: [{...}]?, response: {...}?}]

定义反向代理路由的对象数组。

  • method是一个包含http方法之一的字符串
    • GET
    • POST
    • DELETE
    • PUT
    • PATCH
    • HEAD
    • OPTIONS
    • TRACE
    • CONNECT
  • path是一个与路由关联的路径的字符串,:var可用于设置路径变量(例如:/api/user/:id)。

routes.requests: [{name: string?, method: string, headers: {header: string}?, url: string, body: string?}]?

Requests是一个可选对象数组,代表需要生成响应而必须发出的请求。

  • name是一个可选字符串,如果存在,则用于存储与要发出的请求相关联的响应数据,以便在minijinja模板中使用。
  • method是一个必需的字符串,包含http方法(或minijinja模板),如路由定义中所述。
  • headers 是一个对象,其键是要设置的请求头,值是包含该头值或用于生成它的 minijinja 模板。
  • headers 是一个对象,其键是要配置的请求头,值是包含该头值或用于生成它的 minijinja 模板。
  • url 是与请求相关联的必需的 minijinja 模板或原始字符串。
  • body 是与请求相关联的可选的 minijinja 模板或原始字符串。

routes.response {status: string?, headers: {header: string}?, body: string?}?

响应以请求数组中最后一个请求的状态、头和响应体开始,或者如果没有,则为空的 200 响应,这里属性是反向代理发送给客户端的响应的修饰符。

  • status 是一个可选的字符串或 minijinja 模板,表示一个整数以修改响应的状态码。
  • headers 是一个可选的对象,其键是要修改的响应头,值是表示与该头关联的值的字符串或 minijinja 模板。
  • body 是一个可选的字符串或 minijinja 模板,用于替换原始响应体。

可用的 minijinja 模板变量

path: string

客户端在请求中传递的关联路径。

例如: /api/user/:id => /api/user/25

query: string?

客户端在请求中传递的关联查询字符串或 none

例如: https://127.0.0.1:3000/api/users?name=john => name=john

headers: {header: string}

客户端在请求中传递的关联头对象。

注意,所有头键都是 小写

例如:Content-Type: text/plain => {"content-type": "text/plain"}

params: {param: string}

给定路由上与客户端请求关联的路径参数的关联对象。

例如: /api/user/:id => https://127.0.0.1:3000/api/user/25 => {"id": "25"}

vars: {param: string}

与客户端请求关联的查询参数的关联对象。

例如: https://127.0.0.1:3000/api/users?name=john => {"name": "john"}

body: string

客户端在请求中传递的体。

json

客户端在请求中传递的体转换为 json。

如果失败,包含作为 json 字符串的体。

data: {name: {status: integer, headers: {header: string}, body: string, json}}

数据对象是存储所有反向代理请求数组的所有结果的地点。只有与它相关联名称的结果才会被存储,并将对下一个请求或响应模板可用。

  • name:对象键是传递到requests数组中的name
  • status:与请求关联的响应状态。
  • headers:与请求关联的响应头(在这个对象中,头名称始终为小写
  • body:与请求关联的响应体字符串。
  • json:将响应体转换为json(如果失败则为json字符串)并与请求关联。

发布 📦

目前,仅在发布中分发Linux通用版本的二进制文件。

sudo apt install pkg-config libssl-dev musl-tools
rustup update
rustup target add x86_64-unknown-linux-musl
cargo update
cargo build --release --target x86_64-unknown-linux-musl

微服务 💯

我使用的微服务列表以及与minirps共享你的哲学。

  • serialscale:一个用rust编写的IOT服务器,用于通过串行端口读取秤的重量数据。
  • rawprinter:一个用rust编写的IOT服务器,用于通过USB连接到原始打印机。

贡献 🤝

这是一个非常简单的项目。任何贡献、任何反馈都将受到极大的欢迎。

支持 ⭐

如果这个项目对你有帮助,请在github上给它一个star,这是一种增加证据并吸引更多贡献者的方式。

致谢 🙏

如果没有这些相关项目,这项工作将无法实现

对为这些项目做出贡献的所有人表示衷心的感谢。

依赖

~15–30MB
~508K SLoC