Embassy 4.1.4

Embassy 4.1.4

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布时间最新发布2022年10月
SPM支持 SPM

Fabien LegoupillotGreg LeeBecky ChristensenRobin Goos 维护。



Embassy 4.1.4

Embassy

Build Status Carthage compatible SwiftPM compatible CocoaPods Swift Version Plaform GitHub license

纯 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"),
    ]
)

您可以在此示例项目中看到。