Embassy
纯 Swift 的超轻量级异步 HTTP 服务器。
请阅读:iOS UI 测试的内嵌 Web 服务器。
另见:我们基于 Embassy 的轻量级 Web 框架 Ambassador
功能
- Swift 4 & 5
- iOS / tvOS / MacOS / Linux
- 超轻量级,仅 1.5 K 行
- 无第三方依赖
- 基于异步事件循环的 HTTP 服务器,使得长轮询、延迟和带宽限制成为可能
- 基于 SWSGI 的 HTTP 应用,超级灵活
- IPv6 兼容,也支持 IPv4(双栈)
- 自动测试覆盖
示例
下面是一个简单的示例,展示了 Embassy 的工作原理。
let loop = try! SelectorEventLoop(selector: try! KqueueSelector())
let server = DefaultHTTPServer(eventLoop: loop, port: 8080) {
(
environ: [String: Any],
startResponse: ((String, [(String, String)]) -> Void),
sendBody: ((Data) -> Void)
) in
// Start HTTP response
startResponse("200 OK", [])
let pathInfo = environ["PATH_INFO"]! as! String
sendBody(Data("the path you're visiting is \(pathInfo.debugDescription)".utf8))
// send EOF
sendBody(Data())
}
// Start HTTP server to listen on the port
try! server.start()
// Run event loop
loop.runForever()
然后您可以在浏览器中访问 http://[::1]:8080/foo-bar
并查看
the path you're visiting is "/foo-bar"
默认情况下,出于安全考虑,服务器将仅绑定到localhost接口(::1)。如果您想通过外部网络访问您的服务器,则需要将绑定接口更改为所有地址。
let server = DefaultHTTPServer(eventLoop: loop, interface: "::", port: 8080)
异步事件循环
要使用异步事件循环,您可以从environ
字典中通过键embassy.event_loop
获取它并将其转换为EventLoop
。例如,您可以创建一个SWSGI应用,延迟如下sendBody
调用。
let app = { (
environ: [String: Any],
startResponse: ((String, [(String, String)]) -> Void),
sendBody: @escaping ((Data) -> Void)
) in
startResponse("200 OK", [])
let loop = environ["embassy.event_loop"] as! EventLoop
loop.call(withDelay: 1) {
sendBody(Data("hello ".utf8))
}
loop.call(withDelay: 2) {
sendBody(Data("baby ".utf8))
}
loop.call(withDelay: 3) {
sendBody(Data("fin".utf8))
sendBody(Data())
}
}
请注意,传递给SWSGI的函数应当仅在运行EventLoop
的同一线程中调用,因为它们都不是线程安全的,所以您不应该使用GCD来延迟任何调用。相反,有一些来自EventLoop
的方法可以使用,它们都是线程安全的。
call(callback: (Void) -> Void)
尽可能快地在事件循环中调用给定的回调。
call(withDelay: TimeInterval, callback: (Void) -> Void)
计划在指定的时间(以秒为单位)后,在事件循环中调用给定的回调。
call(atTime: Date, callback: (Void) -> Void)
在指定时间(atTime
)在事件循环中安排给定的回调进行调用。如果给定的时间是过去或者为零,这个方法将像带有仅回调参数的call
一样工作。
什么是SWSGI(Swift Web服务器网关接口)?
SWSGI是为了向Python的WSGI(Web服务器网关接口)致敬。它是一个网关接口,使Web应用能够与HTTP客户端进行通信,而无需了解HTTP服务器的实现细节。
它被定义为
public typealias SWSGI = (
[String: Any],
@escaping ((String, [(String, String)]) -> Void),
@escaping ((Data) -> Void)
) -> Void
environ
它是一个字典,包含有关请求的所有必要信息。它基本上遵循WSGI标准,除了wsgi.*
键将被替换为swsgi.*
。例如
[
"SERVER_NAME": "[::1]",
"SERVER_PROTOCOL" : "HTTP/1.1",
"SERVER_PORT" : "53479",
"REQUEST_METHOD": "GET",
"SCRIPT_NAME" : "",
"PATH_INFO" : "/",
"HTTP_HOST": "[::1]:8889",
"HTTP_USER_AGENT" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36",
"HTTP_ACCEPT_LANGUAGE" : "en-US,en;q=0.8,zh-TW;q=0.6,zh;q=0.4,zh-CN;q=0.2",
"HTTP_CONNECTION" : "keep-alive",
"HTTP_ACCEPT" : "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"HTTP_ACCEPT_ENCODING" : "gzip, deflate, sdch",
"swsgi.version" : "0.1",
"swsgi.input" : (Function),
"swsgi.error" : "",
"swsgi.multiprocess" : false,
"swsgi.multithread" : false,
"swsgi.url_scheme" : "http",
"swsgi.run_once" : false
]
要从请求体中读取数据,可以使用swsgi.input
,例如
let input = environ["swsgi.input"] as! SWSGIInput
input { data in
// handle the body data here
}
当EOF到达时,空数据将被传递到输入数据处理器。请注意,如果swsgi.input
未设置或设置为nil,则不会读取请求体。您可以将swsgi.input
用作带宽控制,在您不想从客户端接收任何数据时将其设置为nil。
一些额外的Embassy服务器特定键包括
embassy.connection
- 请求的HTTPConnection
对象embassy.event_loop
-EventLoop
对象embassy.version
- Embassy的版本,作为字符串,例如3.0.0
startResponse
用于向客户端发送HTTP响应头部的函数,第一个参数是带有消息的状态码,例如“200 OK”。第二个参数是头信息,作为键值对列表。
要响应HTTP头部,可以进行以下操作
startResponse("200 OK", [("Set-Cookie", "foo=bar")])
startResponse
只能对每个请求调用一次,额外的调用将被简单地忽略。
sendBody
向客户端发送正文数据的函数。在调用sendBody
之前,您必须先调用startResponse
。如果您先不调用startResponse
,所有对sendBody
的调用都将被忽略。要发送数据,在此处您可以执行
sendBody(Data("hello".utf8))
要结束响应数据流,只需用空数据调用sendBody
。
sendBody(Data())
安装
CocoaPods
要使用CocoaPod安装,将Embassy添加到您的Podfile中
pod 'Embassy', '~> 4.1'
Carthage
要使用Carthage安装,将Embassy添加到您的Cartfile中
github "envoy/Embassy" ~> 4.1
包管理器
在Package.swift
中添加此Embassy仓库,如下所示
import PackageDescription
let package = Package(
name: "EmbassyExample",
dependencies: [
.package(url: "https://github.com/envoy/Embassy.git",
from: "4.1.1"),
]
)
您可以在此示例项目中看到。