Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/envoy/embassy
Super lightweight async HTTP server library in pure Swift runs in iOS / MacOS / Linux
https://github.com/envoy/embassy
asynchronous ios lightweight linux swift webserver
Last synced: 1 day ago
JSON representation
Super lightweight async HTTP server library in pure Swift runs in iOS / MacOS / Linux
- Host: GitHub
- URL: https://github.com/envoy/embassy
- Owner: envoy
- License: mit
- Created: 2016-05-20T00:14:53.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2024-02-19T20:26:05.000Z (9 months ago)
- Last Synced: 2024-10-28T12:54:10.348Z (16 days ago)
- Topics: asynchronous, ios, lightweight, linux, swift, webserver
- Language: Swift
- Homepage:
- Size: 326 KB
- Stars: 600
- Watchers: 21
- Forks: 74
- Open Issues: 29
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Embassy
[![Build Status](https://travis-ci.org/envoy/Embassy.svg?branch=master)](https://travis-ci.org/envoy/Embassy)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage)
[![SwiftPM compatible](https://img.shields.io/badge/SwiftPM-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager)
[![CocoaPods](https://img.shields.io/cocoapods/v/Embassy.svg)]()
![Swift Version](https://img.shields.io/badge/Swift-5.0-orange.svg)
![Plaform](https://img.shields.io/badge/Platform-macOS|iOS|Linux-lightgrey.svg)
[![GitHub license](https://img.shields.io/github/license/envoy/Embassy.svg)](https://github.com/envoy/Embassy/blob/master/LICENSE)Super lightweight async HTTP server in pure Swift.
**Please read**: [Embedded web server for iOS UI testing](https://envoy.engineering/embedded-web-server-for-ios-ui-testing-8ff3cef513df#.c2i5tx380).
**See also**: Our lightweight web framework [Ambassador](https://github.com/envoy/Ambassador) based on Embassy
## Features
- Swift 4 & 5
- iOS / tvOS / MacOS / Linux
- Super lightweight, only 1.5 K of lines
- Zero third-party dependency
- Async event loop based HTTP server, makes long-polling, delay and bandwidth throttling all possible
- HTTP Application based on [SWSGI](#whats-swsgi-swift-web-server-gateway-interface), super flexible
- IPV6 ready, also supports IPV4 (dual stack)
- Automatic testing covered## Example
Here's a simple example shows how Embassy works.
```Swift
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()
```Then you can visit `http://[::1]:8080/foo-bar` in the browser and see
```
the path you're visiting is "/foo-bar"
```By default, the server will be bound only to the localhost interface (::1) for security reasons. If you want to access your server over an external network, you'll need to change the bind interface to all addresses:
```Swift
let server = DefaultHTTPServer(eventLoop: loop, interface: "::", port: 8080)
```## Async Event Loop
To use the async event loop, you can get it via key `embassy.event_loop` in `environ` dictionary and cast it to `EventLoop`. For example, you can create an SWSGI app which delays `sendBody` call like this
```Swift
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())
}
}
```Please notice that functions passed into SWSGI should be only called within the same thread for running the `EventLoop`, they are all not threadsafe, therefore, **you should not use [GCD](https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/) for delaying any call**. Instead, there are some methods from `EventLoop` you can use, and they are all threadsafe
### call(callback: (Void) -> Void)
Call given callback as soon as possible in the event loop
### call(withDelay: TimeInterval, callback: (Void) -> Void)
Schedule given callback to `withDelay` seconds then call it in the event loop.
### call(atTime: Date, callback: (Void) -> Void)
Schedule given callback to be called at `atTime` in the event loop. If the given time is in the past or zero, this methods works exactly like `call` with only callback parameter.
## What's SWSGI (Swift Web Server Gateway Interface)?
SWSGI is a hat tip to Python's [WSGI (Web Server Gateway Interface)](https://www.python.org/dev/peps/pep-3333/). It's a gateway interface enables web applications to talk to HTTP clients without knowing HTTP server implementation details.
It's defined as
```Swift
public typealias SWSGI = (
[String: Any],
@escaping ((String, [(String, String)]) -> Void),
@escaping ((Data) -> Void)
) -> Void
```### `environ`
It's a dictionary contains all necessary information about the request. It basically follows WSGI standard, except `wsgi.*` keys will be `swsgi.*` instead. For example:
```Swift
[
"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
]
```To read request from body, you can use `swsgi.input`, for example
```Swift
let input = environ["swsgi.input"] as! SWSGIInput
input { data in
// handle the body data here
}
```An empty Data will be passed into the input data handler when EOF
reached. Also please notice that, request body won't be read if `swsgi.input`
is not set or set to nil. You can use `swsgi.input` as bandwidth control, set
it to nil when you don't want to receive any data from client.Some extra Embassy server specific keys are
- `embassy.connection` - `HTTPConnection` object for the request
- `embassy.event_loop` - `EventLoop` object
- `embassy.version` - Version of embassy as a String, e.g. `3.0.0`### `startResponse`
Function for starting to send HTTP response header to client, the first argument is status code with message, e.g. "200 OK". The second argument is headers, as a list of key value tuple.
To response HTTP header, you can do
```Swift
startResponse("200 OK", [("Set-Cookie", "foo=bar")])
````startResponse` can only be called once per request, extra call will be simply ignored.
### `sendBody`
Function for sending body data to client. You need to call `startResponse` first in order to call `sendBody`. If you don't call `startResponse` first, all calls to `sendBody` will be ignored. To send data, here you can do
```Swift
sendBody(Data("hello".utf8))
```To end the response data stream simply call `sendBody` with an empty Data.
```Swift
sendBody(Data())
```## Install
### CocoaPods
To install with CocoaPod, add Embassy to your Podfile:
```
pod 'Embassy', '~> 4.1'
```### Carthage
To install with Carthage, add Embassy to your Cartfile:
```
github "envoy/Embassy" ~> 4.1
```### Package Manager
Add it this Embassy repo in `Package.swift`, like this
```swift
import PackageDescriptionlet package = Package(
name: "EmbassyExample",
dependencies: [
.package(url: "https://github.com/envoy/Embassy.git",
from: "4.1.4"),
]
)
```You can read this [example project](https://github.com/envoy/example-embassy) here.