[swift-server-dev] HTTP (server) APIs sketch of what we use internally

Joy Keys proyb7 at gmail.com
Sat Mar 25 21:37:47 CDT 2017


I like to mention Edge reimplement HTTP include POSIX.
https://github.com/SwiftOnEdge/Edge

Will SSS use POSIX?

On Sat, Mar 25, 2017 at 12:00 AM, Johannes Weiß via swift-server-dev <
swift-server-dev at swift.org> wrote:

> Hi swift-server-dev,
>
> First of all, sorry for the delay!
>
> On the last HTTP meeting we were talking about how to represent HTTP/1.1
> and how to handle trailers and request/response body streaming. I was
> describing what we use internally. There was some interest to see the
> actual APIs, please find them below.
>
> The sketch doesn't cover the full spectrum of what we want to achieve. The
> main thing that's missing is that there is currently no data type for a
> HTTP response. We just build the response on the fly using the
> HTTPResponseWriter. IMHO that can easily be added by basically making the
> response headers, response code, etc a new type HTTPResponse.
> HTTPResponseWriter would then take a value of that.
>
> The main design choices we made are
>  - value type for HTTP request
>  - having the HTTP body (&trailers) not part of the request type as we
> need to support request body streaming
>  - writing the response is asynchronous, the result (success/failure) of
> the operation reported in the callback
>  - headers not being a dictionary but its own data structure, basically
> [(String, String)] but when queried with a String, you'll get all the
> values as a list of strings. (headers["set-cookie"] returns ["foo", "bar"]
> for "Set-Cookie: foo\r\nSET-COOKIE: bar\r\n")
>
> Not sure if that's interesting but our stuff is implemented on top of
> DispatchIO. We support both request body as well as response body streaming
> without sacrificing a thread per connection/request.
>
> For HTTP, this is our lowest level API, we built a higher-level web
> framework on top of that and so far we're happy with what we got.
>
> If you're interested, there's some very simple demo code (HTTP echo
> server) below the APIs.
>
> Please let us know what you think.
>
> --- SNIP ---
> /* a web app is a function that gets a HTTPRequest and a
> HTTPResponseWriter and returns a function which processes the HTTP request
> body in chunks as they arrive */
> public typealias WebApp = (HTTPRequest, HTTPResponseWriter) ->
> HTTPBodyProcessing
>
> public struct HTTPRequest {
>    public let method : HTTPMethod
>    public let target : String /* e.g. "/foo/bar?buz=qux" */
>    public let httpVersion : HTTPVersion
>    public let headers : HTTPHeaders
> }
>
> public protocol HTTPResponseWriter: class {
>    func writeResponse(status: HTTPResponseStatus, transferEncoding:
> HTTPTransferEncoding)
>
>    func writeHeader(key: String, value: String)
>
>    func writeTrailer(key: String, value: String)
>
>    func writeBody(data: DispatchData) /* convenience */
>    func writeBody(data: Data) /* convenience */
>    func writeBody(data: DispatchData, completion: @escaping
> (Result<POSIXError, ()>) -> Void)
>    func writeBody(data: Data, completion: @escaping (Result<POSIXError,
> ()>) -> Void)
>
>    func done() /* convenience */
>    func done(completion: @escaping (Result<POSIXError, ()>) -> Void))
>    func abort()
> }
>
> public typealias HTTPBodyHandler = (HTTPBodyChunk) -> Void
>
> public enum HTTPBodyProcessing {
>     case discardBody /* if you're not interested in the body, equivalent
> to `.processBody { _ in }` */
>     case processBody(handler: HTTPBodyHandler)
> }
>
> public enum HTTPBodyChunk {
>    case chunk(data: DispatchData) /* a new bit of the HTTP request body
> has arrived */
>    case failed(error: HTTPParserError) /* error while streaming the HTTP
> request body, eg. connection closed */
>    case trailer(key: String, value: String) /* trailer has arrived (this
> we actually haven't implemented yet) */
>    case end /* body and trailers finished */
> }
>
> public struct HTTPHeaders : Sequence {
>    private let storage: [String:[String]]     /* lower cased keys */
>    private let original: [(String, String)]   /* original casing */
>    public var description: String { return original.description }
>
>    public subscript(key: String) -> [String]
>    public func makeIterator() -> IndexingIterator<Array<(String, String)>>
> }
>
> /* from here on just for completeness really */
>
> public typealias HTTPVersion = (Int, Int)
>
> public enum HTTPTransferEncoding {
>    case identity(contentLength: UInt)
>    case chunked
> }
>
> public enum HTTPResponseStatus: Equatable {
>    /* use custom if you want to use a non-standard response code or
>       have it available in a (UInt, String) pair from a higher-level web
> framework. */
>    case custom(code: UInt, reasonPhrase: String)
>
>    /* all the codes from http://www.iana.org/assignments/http-status-codes
> */
>    case `continue`
>    case switchingProtocols
>    case processing
>    case ok
>    case created
>    case accepted
>    case nonAuthoritativeInformation
>    case noContent
>    case resetContent
>    case partialContent
>    case multiStatus
>    case alreadyReported
>    case imUsed
>    case multipleChoices
>    case movedPermanently
>    case found
>    case seeOther
>    case notModified
>    case useProxy
>    case temporaryRedirect
>    case permanentRedirect
>    case badRequest
>    case unauthorized
>    case paymentRequired
>    case forbidden
>    case notFound
>    case methodNotAllowed
>    case notAcceptable
>    case proxyAuthenticationRequired
>    case requestTimeout
>    case conflict
>    case gone
>    case lengthRequired
>    case preconditionFailed
>    case payloadTooLarge
>    case uriTooLong
>    case unsupportedMediaType
>    case rangeNotSatisfiable
>    case expectationFailed
>    case misdirectedRequest
>    case unprocessableEntity
>    case locked
>    case failedDependency
>    case upgradeRequired
>    case preconditionRequired
>    case tooManyRequests
>    case requestHeaderFieldsTooLarge
>    case unavailableForLegalReasons
>    case internalServerError
>    case notImplemented
>    case badGateway
>    case serviceUnavailable
>    case gatewayTimeout
>    case httpVersionNotSupported
>    case variantAlsoNegotiates
>    case insufficientStorage
>    case loopDetected
>    case notExtended
>    case networkAuthenticationRequired
> }
>
> public enum HTTPMethod {
>    /* everything that http_parser.[ch] supports */
>    case DELETE
>    case GET
>    case HEAD
>    case POST
>    case PUT
>    case CONNECT
>    case OPTIONS
>    case TRACE
>    case COPY
>    case LOCK
>    case MKCOL
>    case MOVE
>    case PROPFIND
>    case PROPPATCH
>    case SEARCH
>    case UNLOCK
>    case BIND
>    case REBIND
>    case UNBIND
>    case ACL
>    case REPORT
>    case MKACTIVITY
>    case CHECKOUT
>    case MERGE
>    case MSEARCH
>    case NOTIFY
>    case SUBSCRIBE
>    case UNSUBSCRIBE
>    case PATCH
>    case PURGE
>    case MKCALENDAR
>    case LINK
>    case UNLINK
> }
> --- SNAP ---
>
> Here's the demo code for a simple echo server
>
> --- SNIP ---
> serve { (req, res) in
>     if req.target == "/echo" {
>         guard req.httpVersion == (1, 1) else {
>             /* HTTP/1.0 doesn't support chunked encoding */
>             res.writeResponse(status: .httpVersionNotSupported,
> transferEncoding: .identity(contentLength: 0))
>             res.done()
>             return .discardBody
>         }
>         res.writeResponse(status: .ok, transferEncoding: .chunked)
>         return .processBody { chunk in
>             switch chunk {
>                 case .chunk(let data):
>                     res.writeBody(data: data)
>                 case .end:
>                     res.done()
>                 default:
>                     res.abort()
>             }
>         }
>     } else { ... }
> }
> --- SNAP ---
>
> --
>   Johannes
>
> _______________________________________________
> swift-server-dev mailing list
> swift-server-dev at swift.org
> https://lists.swift.org/mailman/listinfo/swift-server-dev
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-server-dev/attachments/20170326/00d62ac9/attachment.html>


More information about the swift-server-dev mailing list