A simple cross-platform web server, with little dependencies, that does not require switching its version based on the version of Swift being used.
This is helpful when sequentially testing against many different versions of Swift so different dependencies/versions are not required when switching Swift versions
Note: This package doesn't directly support HTTPS Listeners as of yet.
Protocols/classes are in place, like LittleWebServerListener and LittleWebServerSocketListener, to allow custom listeners to be implemented
import LittleWebServer
// Create a listener
let listener = try LittleWebServerHTTPListener(specificIP: .anyIPv4,
port: .firstAvailable,
reuseAddr: true)
// Create the server
let server = LittleWebServer(listener)
// Lets name the server
server.serverHeader = "CoolServer"
// Setup server error handler
server.serverErrorHandler = { err in
// Catch errors here for logging
Swift.debugPrint("SERVER ERROR: \(err)")
}
/// Do handler setup here
server.start()
// Setup a request handler for the root of the server
server.defaultHost["/"] = { (request: LittleWebServer.HTTP.Request) -> LittleWebServer.HTTP.Response in
return .ok(body: .html("HTML Content"))
}
// Setup a request handler for the root of the given domain
server.hosts["some.domain.com"]["/"] = { (request: LittleWebServer.HTTP.Request) -> LittleWebServer.HTTP.Response in
return .ok(body: .html("HTML Content"))
}
struct ServerAPIError: Encodable {
let id: Int
let message: String
}
struct ServerStatus: Encodable {
...
}
public struct ModifiableObject: Codable,
Comparable,
LittleWebServerIdentifiableObject {
public var id: Int
public var description: String
...
}
enum SharePathError: Swift.Error, CustomStringConvertible {
case objectAlreadyExists(Int)
public var description: String {
switch self {
case .objectAlreadyExists(let id): return "Object with id '\(id)' already exists"
}
}
}
let objectEncoder = JSONEncoder()
let objectDecoder = JSONDecoder()
let status = ServerStatus()
func errorResponse(_ event: LittleWebServer.ObjectSharing.ErrorResponseEvent,
_ error: Swift.Error) -> ServerAPIError {
return ServerAPIError(id: -1, message: "\(error)")
}
func notFoundResponse(_ request: LittleWebServer.HTTP.Request,
_ stringId: String,
_ objectId: Int?) -> ServerAPIError {
return ServerAPIError(id: -404, message: "Object with ID '\(stringId)' not found")
}
// Share a readonly object
server.defaultHost["/status"] = LittleWebServer.ObjectSharing.shareObject(encoder: objectEncoder,
object: status,
errorResponse: errorResponse)
// Share an updatable object
var updatableObject = ModifiableObject()
server.defaultHost["/object"] = LittleWebServer.ObjectSharing.shareObject(encoder: objectEncoder,
decoder: objectDecoder,
object: &updatableObject,
errorResponse: errorResponse)
var updatableList: [ModifiableObject] = []
server.defaultHost["/objects"] = LittleWebServer.ObjectSharing.sharePathObjects(encoder: objectEncoder,
decoder: objectDecoder,
objects: &updatableObject,
objectExistError: { id in
return SharePathError.objectAlreadyExists(id)
},
objectSorter: <,
notFoundResponse: notFoundResponse,
errorResponse: errorResponse)
// web root => /path/public
// path identity => :path{**}
// identity name => path (This specific identity is needed for the share method)
// rule Anything Here After => ** (This allows for any path including/after /path/public/)
// Limit the transfer seed of files
let speedLimiter: LittleWebServer.FileTransferSpeedLimiter = .unlimited
server.defaultHost["/path/public/:path{**}"] = LittleWebServer.FSSharing.share(resource: URL(fileURLWithPath: "..."),
speedLimiter: speedLimiter)
// Create a web socket endpoint
server.defaultHost["/socket"] = LittleWebServer.WebSocket.endpoint { client, event in
switch event {
...
}
}
The Routing Path is the path used to help route requests to the proper request handler Route paths are used on the subscript of a host router to define a default handler for a request or on the the subscript of a routing method to define the handler on a specific path for the given method
Route Path Component
A Route Path Component is an individual path component like a directory name or file name in the path. It does not contain a /
Basic Formatting: ":Identifier{ Path Condition? <Transformation>? { Parameter Conditions }? }"
No Identifier Formatting: "Path Condition{ <Transformation>? { Parameter Conditions }? }"
No Identifier & No Path Condition Formatting: { <Transformation>? { Parameter Conditions }? }
Identifier:
A path component can be assigned an identifier for access later from request.identities[...]
Format: ":{identifier name}..." eg: ":path{**}" <== This say the identifier is path the {} allows to set more properties to the component. The ** indicates any path hereafter
Path Condition:
When the web server is matching a path to path components the order it checks the path condition is:
This allows for specific route handlers to be set to locations that would normally fall under an Anything Hereafter condition
Transformation:
The transformation, which must be wrapped in <...> is a identifier name of a string transforming function that takes the string value and optionally converts it to another type. If the transforming function returns nil that would mean that the condition failed.
Initial registered transformers are the basic types Bool, Int, Int(8, 16, 32, 64), UInt, UInt(8, 16, 32, 64), Float, Double.
String formatted Int's are also supported:
Additional transformers can be registered by calling server.registerStringTransformer
Parameter Condition:
The format order of a parameter condition is important
Full Format: "? [ { Condition / Condition Group}, ...] <Transformation>"
Breakdown:
Examples:
Condition: An individual parameter condition to match
A condition can be a regular expression (Must start with ^ and end with $) or an exact text match
Conditions must be encapsulated in { ... }
Condition Group: A group of Conditions / Condition Groups to match
Conditions / Condition Groups can be AND' (&&) and OR'd (||) to make a more complex match
Condition Groups must be encapsulated in { ... }
Transformation See Transformation above
Parameter Conditions:
Parameter conditions is a collection of of individual parameter conditions encapsulated within { } and separated by ,
Each Parameter name has a prefix of @ that denotes the start of a parameter name/condition
Parameter Names should not contain the ':' character
eg: { @Parameter Name : { Parameter Condition / Condition Group } , ... }
A Route Path Slice is a collection of Route Path Components and must not start with /
Examples:
A Route Path Condition is the complete path from the root (/) to the endpoint to where a handler is to be set
Copyright 2021 Tyler Anger
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
HERE or http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
link |
Stars: 0 |
Last commit: 1 year ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics