Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/envoy/Ambassador
Super lightweight web framework in Swift based on SWSGI
https://github.com/envoy/Ambassador
asynchronous ios swift webserver
Last synced: 10 days ago
JSON representation
Super lightweight web framework in Swift based on SWSGI
- Host: GitHub
- URL: https://github.com/envoy/Ambassador
- Owner: envoy
- License: mit
- Created: 2016-06-10T20:46:44.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2022-12-05T08:13:27.000Z (almost 2 years ago)
- Last Synced: 2024-10-14T22:21:28.577Z (25 days ago)
- Topics: asynchronous, ios, swift, webserver
- Language: Swift
- Homepage:
- Size: 91.8 KB
- Stars: 185
- Watchers: 14
- Forks: 46
- Open Issues: 28
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- fucking-awesome-swift - Ambassador - Super lightweight web framework based on SWSGI. (Libs / Network)
- awesome-swift - Ambassador - Super lightweight web framework based on SWSGI. (Libs / Network)
- awesome-swift - Ambassador - Super lightweight web framework in Swift based on SWSGI ` 📝 2 years ago` (Network [🔝](#readme))
- awesome-swift - Ambassador - Super lightweight web framework based on SWSGI. (Libs / Network)
README
# Ambassador
[![Build Status](https://travis-ci.org/envoy/Ambassador.svg?branch=master)](https://travis-ci.org/envoy/Ambassador)
[![CocoaPods](https://img.shields.io/cocoapods/v/EnvoyAmbassador.svg)]()
[![Code Climate](https://codeclimate.com/repos/575b39108524ed0091001237/badges/4c5ceffe02f98eb2159d/gpa.svg)](https://codeclimate.com/repos/575b39108524ed0091001237/feed)
[![Issue Count](https://codeclimate.com/repos/575b39108524ed0091001237/badges/4c5ceffe02f98eb2159d/issue_count.svg)](https://codeclimate.com/repos/575b39108524ed0091001237/feed)
[![GitHub license](https://img.shields.io/github/license/envoy/Ambassador.svg)](https://github.com/envoy/Ambassador/blob/master/LICENSE)Super lightweight web framework in Swift based on [SWSGI](https://github.com/envoy/Embassy#whats-swsgi-swift-web-server-gateway-interface)
## Features
- Super lightweight
- Easy to use, designed for UI automatic testing API mocking
- Based on [SWSGI](https://github.com/envoy/Embassy#whats-swsgi-swift-web-server-gateway-interface), can run with HTTP server other than [Embassy](https://github.com/envoy/Embassy)
- Response handlers designed as middlewares, easy to composite
- Async friendly## Example
Here's an example how to mock API endpoints with Ambassador and [Embassy](https://github.com/envoy/Embassy) as the HTTP server.
```Swift
import Embassy
import EnvoyAmbassadorlet loop = try! SelectorEventLoop(selector: try! KqueueSelector())
let router = Router()
let server = DefaultHTTPServer(eventLoop: loop, port: 8080, app: router.app)router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
return [
["id": "01", "name": "john"],
["id": "02", "name": "tom"]
]
}))// Start HTTP server to listen on the port
try! server.start()// Run event loop
loop.runForever()
```Then you can visit [http://[::1]:8080/api/v2/users](http://[::1]:8080/api/v2/users) in the browser, or use HTTP client to GET the URL and see
```
[
{
"id" : "01",
"name" : "john"
},
{
"id" : "02",
"name" : "tom"
}
]
```## Router
`Router` allows you to map different path to different `WebApp`. Like what you saw in the previous example, to route path `/api/v2/users` to our response handler, you simply set the desired path with the `WebApp` as the value
```Swift
let router = Router()
router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
return [
["id": "01", "name": "john"],
["id": "02", "name": "tom"]
]
}))
```and pass `router.app` as the SWSGI interface to the HTTP server. When the visit path is not found, `router.notFoundResponse` will be used, it simply returns 404. You can overwrite the `notFoundResponse` to customize the not found behavior.
You can also map URL with regular expression. For example, you can write this
```Swift
let router = Router()
router["/api/v2/users/([0-9]+)"] = DelayResponse(JSONResponse(handler: { environ -> Any in
let captures = environ["ambassador.router_captures"] as! [String]
return ["id": captures[0], "name": "john"]
}))
```Then all requests with URL matching `/api/v2/users/([0-9]+)` regular expression will be routed here. For all match groups, they will be passed into environment with key `ambassador.router_captures` as an array of string.
## DataResponse
`DataResponse` is a helper for sending back data. For example, say if you want to make an endpoint to return status code 500, you can do
```Swift
router["/api/v2/return-error"] = DataResponse(statusCode: 500, statusMessage: "server error")
```Status is `200 OK`, and content type is `application/octet-stream` by default, they all can be overwritten via init parameters. You can also provide custom headers and a handler for returning the data. For example:
```Swift
router["/api/v2/xml"] = DataResponse(
statusCode: 201,
statusMessage: "created",
contentType: "application/xml",
headers: [("X-Foo-Bar", "My header")]
) { environ -> Data in
return Data("who uses xml nowadays?".utf8)
}
```If you prefer to send body back in async manner, you can also use another init that comes with extra `sendData` function as parameter
```Swift
router["/api/v2/xml"] = DataResponse(
statusCode: 201,
statusMessage: "created",
contentType: "application/xml",
headers: [("X-Foo-Bar", "My header")]
) { (environ, sendData) in
sendData(Data("who uses xml nowadays?".utf8))
}
```Please notice, unlike `sendBody` for SWSGI, `sendData` only expects to be called once with the whole chunk of data.
## JSONResponse
Almost identical to `DataResponse`, except it takes `Any` instead of bytes and dump the object as JSON format and response it for you. For example:
```Swift
router["/api/v2/users"] = JSONResponse() { _ -> Any in
return [
["id": "01", "name": "john"],
["id": "02", "name": "tom"]
]
}
```## DelayResponse
`DelayResponse` is a **decorator** response that delays given response for a while. In real-world, there will always be network latency, to simulte the latency, `DelayResponse` is very helpful. To delay a response, just do
```Swift
router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
return [
["id": "01", "name": "john"],
["id": "02", "name": "tom"]
]
}))
```By default, it delays the response randomly. You can modify it by passing `delay` parameter. Like, say if you want to delay it for 10 seconds, then here you do
```Swift
router["/api/v2/users"] = DelayResponse(JSONResponse(handler: { _ -> Any in
return [
["id": "01", "name": "john"],
["id": "02", "name": "tom"]
]
}), delay: .delay(10))
```The available delay options are
- **.random(min: TimeInterval, max: TimeInterval)** delay random, it's also the default one as .random(min: 0.1, max: 3)
- **.delay(seconds: TimeInterval)** delay specific period of time
- **.never** delay forever, the response will never be returned
- **.none** no delay, i.e. the response will be returned immediately## DataReader
To read POST body or any other HTTP body from the request, you need to use `swsgi.input` function provided in the `environ` parameter of SWSGI. For example, you can do it like this
```Swift
router["/api/v2/users"] = JSONResponse() { environ -> Any in
let input = environ["swsgi.input"] as! SWSGIInput
input { data in
// handle the data stream here
}
}
```It's not too hard to do so, however, the data comes in as stream, like
- "first chunk"
- "second chunk"
- ....
- "" (empty data array indicates EOF)In most cases, you won't like to handle the data stream manually. To wait all data received and process them at once, you can use `DataReader`. For instance
```Swift
router["/api/v2/users"] = JSONResponse() { environ -> Any in
let input = environ["swsgi.input"] as! SWSGIInput
DataReader.read(input) { data in
// handle the whole data here
}
}
```## JSONReader
Like `DataReader`, besides reading the whole chunk of data, `JSONReader` also parses it as JSON format. Herer's how you do
```Swift
router["/api/v2/users"] = JSONResponse() { environ -> Any in
let input = environ["swsgi.input"] as! SWSGIInput
JSONReader.read(input) { json in
// handle the json object here
}
}
```## URLParametersReader
`URLParametersReader` waits all data to be received and parses them all at once as URL encoding parameters, like `foo=bar&eggs=spam`. The parameters will be passed as an array key value pairs as `(String, String)`.
```Swift
router["/api/v2/users"] = JSONResponse() { environ -> Any in
let input = environ["swsgi.input"] as! SWSGIInput
URLParametersReader.read(input) { params in
// handle the params object here
}
}
```You can also use `URLParametersReader.parseURLParameters` to parse the URL encoded parameter string if you want. Just do it like
```Swift
let params = URLParametersReader.parseURLParameters("foo=bar&eggs=spam")
```## Install
### CocoaPods
To install with CocoaPod, add Embassy to your Podfile:
```
pod 'EnvoyAmbassador', '~> 4.0'
```### Carthage
To install with Carthage, add Ambassador to your Cartfile:
```
github "envoy/Ambassador" ~> 4.0
```Please notice that you should import `Ambassador` instead of `EnvoyAmbassador`. We use `EnvoyAmbassador` for Cocoapods simply because the name `Ambassador` was already taken.
### Package Manager
To do this, add the repo to `Package.swift`, like this:
```swift
import PackageDescriptionlet package = Package(
name: "AmbassadorExample",
dependencies: [
.package(url: "https://github.com/envoy/Ambassador.git",
from: "4.0.0"),
]
)
```