Skip to content

Commit

Permalink
Added in FlameType to allow for choice between single threaded and mu…
Browse files Browse the repository at this point in the history
…ltiple
  • Loading branch information
Elliott Minns committed Jan 20, 2017
1 parent cae47f7 commit f38b214
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 83 deletions.
35 changes: 25 additions & 10 deletions Sources/Blackfire/App.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import Foundation

public enum FlameType {
case serial
case concurrent
}

extension FlameType {
var queueType: QueueType {
switch self {
case .serial:
return QueueType.serial
case .concurrent:
return QueueType.concurrent
}
}
}

public class Flame {
let pathHandler: PathHandler = PathHandler()
public init() {}

public let type: FlameType

public init(type: FlameType = .serial) {
self.type = type
}
}

extension Flame {

public func start(port: Int, _ callback: ((_ result: Result<Void>) -> ())?) {
let server = HTTPServer(delegate: self)
let server = HTTPServer(delegate: self, type: self.type.queueType)

do {
try server.listen(port: port)
Expand All @@ -27,13 +48,7 @@ extension Flame: PathRouting {
extension Flame: HTTPServerDelegate {
func server(_ server: HTTPServer, didReceive request: HTTPRequest,
response: HTTPResponse) {
let handlers = self.handlers(with: request.path, for: request.method)
guard handlers.count > 0 else {
response.send(status: 404)
return
}
handlers.forEach { handler in
handler(request, response)
}
let handler = self.handler(for: request.path)
handler.handle(request: request, response: response)
}
}
9 changes: 6 additions & 3 deletions Sources/Blackfire/Connection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,18 @@ public class SocketConnection: Connection {

var readSource: DispatchSourceRead?

init(socket: Socket) {
var queueType: QueueType

init(socket: Socket, queueType: QueueType) {
self.socket = socket
self.writeData = Data()
self.queueType = queueType
}

public func read(callback: @escaping (_ data: Buffer, _ amount: Int) -> ()) {
let fd = Int32(socket.raw)
readSource = DispatchSource.makeReadSource(fileDescriptor: fd,
queue: DispatchQueue.global())
queue: queueType.dispatchQueue())
let buffer = Buffer(size: 256)

readSource?.setEventHandler {
Expand Down Expand Up @@ -61,7 +64,7 @@ public class SocketConnection: Connection {
public func write(data: Data) {
self.writeData = data
let writeSource = DispatchSource.makeWriteSource(fileDescriptor: socket.raw,
queue: DispatchQueue.global())
queue: queueType.dispatchQueue())

var amount = 0
writeSource.setEventHandler {
Expand Down
135 changes: 78 additions & 57 deletions Sources/Blackfire/HTTPServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,92 @@
import Foundation

protocol HTTPServerDelegate {
func server(_ server: HTTPServer, didReceive request: HTTPRequest,
response: HTTPResponse)
func server(_ server: HTTPServer, didReceive request: HTTPRequest,
response: HTTPResponse)
}

class HTTPServer {

public var port: Int {
return currentPort
}

var currentPort: Int

var server: Server?

public var delegate: HTTPServerDelegate?

public init() {
currentPort = 80
self.delegate = nil
}

public init(delegate: HTTPServerDelegate) {
currentPort = 80
self.delegate = delegate
}

public func listen(port: Int) throws {
self.currentPort = port
if server == nil {
server = try Server(port: port, delegate: self, type: .tcp)
try server?.listen()
}
enum QueueType {
case serial
case concurrent
}

extension QueueType {
func dispatchQueue() -> DispatchQueue {
switch self {
case .concurrent:
return DispatchQueue.global()
case .serial:
return DispatchQueue.main
}

func sendErrorResponse(toConnection connection: Connection) {
let response = "HTTP/1.1 400 Client Error"
connection.write(response)
}
}

class HTTPServer {

public var port: Int {
return currentPort
}

var currentPort: Int

var server: Server?

var delegate: HTTPServerDelegate?

let queueType: QueueType


init(type: QueueType) {
currentPort = 80
self.delegate = nil
self.queueType = type
}

init(delegate: HTTPServerDelegate, type: QueueType) {
currentPort = 80
self.delegate = delegate
self.queueType = type
}

func listen(port: Int) throws {
self.currentPort = port
if server == nil {
server = try Server(port: port, delegate: self, type: .tcp, queueType: self.queueType)
try server?.listen()
}

}

func sendErrorResponse(toConnection connection: Connection) {
let response = "HTTP/1.1 400 Client Error"
connection.write(response)
}

}

extension HTTPServer: ServerDelegate {

public func server(_ server: Server, didCreateConnection connection: Connection) {

public func server(_ server: Server, didCreateConnection connection: Connection) {

var data = Data()
var data = Data()

connection.read { buffer, amount in

data.append(buffer.buffer, length: amount)

do {
guard let request = try HTTPParser(data: data).parse() else { return }
request.connection = connection

connection.read { buffer, amount in

data.append(buffer.buffer, length: amount)

do {
guard let request = try HTTPParser(data: data).parse() else { return }
request.connection = connection

let response = HTTPResponse(connection: connection)
self.delegate?.server(self, didReceive: request,
response: response)
} catch {
// if let error = error as? ParserError {
// print(error.message)
// print(error.problemArea)
// }
// self.sendErrorResponse(toConnection: connection)
}
}
let response = HTTPResponse(connection: connection)
self.delegate?.server(self, didReceive: request,
response: response)
} catch {
// if let error = error as? ParserError {
// print(error.message)
// print(error.problemArea)
// }
// self.sendErrorResponse(toConnection: connection)
}
}
}
}
22 changes: 16 additions & 6 deletions Sources/Blackfire/PathHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ class PathHandler {
next = child
} else {
next = Node(path: path)
node.children[path] = next
if (!path.isEmpty && path[path.startIndex] == ":") {
node.children[":*"] = next
} else {
node.children[path] = next
}
}
return next
}
Expand All @@ -48,14 +52,20 @@ class PathHandler {
node.children[last] = child
}

func handlers(for path: String, with method: HTTPMethod) -> [RouteHandler] {
func nodes(for path: String) -> [Node] {
let comps = self.comps(for: path)

let node = comps.reduce(base) { (node, path) -> Node? in
return node?.children[path]
let begin: [Node?] = [base]
let nodes = comps.reduce(begin) { (nodes, path) -> [Node?] in
guard let last = nodes.last else { return [] }

if let child = last?.children[path] {
return nodes + [child]
} else {
return nodes + [last?.children[":*"]]
}
}

return node?.handlers[method] ?? []
return nodes.flatMap { $0 }
}
}

Expand Down
19 changes: 19 additions & 0 deletions Sources/Blackfire/Request.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Request.swift
// Blackfire
//
// Created by Elliott Minns on 20/01/2017.
//
//

import Foundation

public class Request: HTTPRequest {
public let params: [String: String]

init(params: [String: String], raw: HTTPRequest) {
self.params = params
super.init(headers: raw.headers, method: raw.method,
body: raw.body, path: raw.path, httpProtocol: raw.httpProtocol)
}
}
43 changes: 43 additions & 0 deletions Sources/Blackfire/RequestHandler.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// RequestHandler.swift
// Blackfire
//
// Created by Elliott Minns on 20/01/2017.
//
//

import Foundation

struct RequestHandler {
let nodes: [Node]

init(nodes: [Node]) {
self.nodes = nodes
}

func handle(request: HTTPRequest, response: HTTPResponse) {
let handlers = nodes.last?.handlers[request.method] ?? []

guard handlers.count > 0 else {
response.send(status: 404)
return
}
/*
let comps = request.path.components(separatedBy: "/")
let params = self.nodes.reduce((0, [:])) { (result, node) -> (Int, [String: String]) in
if !node.path.isEmpty && node.path[node.path.startIndex] == ":" {
let key = node.path.substring(from: node.path.index(after: node.path.startIndex))
let value = comps[result.0]
return (result.0 + 1, [key: value])
} else {
return (result.0 + 1, result.1)
}
}.1
*/
let params: [String: String] = [:]
let request = Request(params: params, raw: request)
handlers.forEach { handler in
handler(request, response)
}
}
}
6 changes: 3 additions & 3 deletions Sources/Blackfire/Routing.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
public typealias Request = HTTPRequest
public typealias Response = HTTPResponse
public typealias RouteHandler = (Request, Response) -> ()

Expand All @@ -18,8 +17,9 @@ protocol PathRouting: Routing {

extension PathRouting {

func handlers(with path: String, for method: HTTPMethod) -> [RouteHandler] {
return pathHandler.handlers(for: path, with: method)
func handler(for path: String) -> RequestHandler {
let nodes = pathHandler.nodes(for: path)
return RequestHandler(nodes: nodes)
}

public func use(_ path: String, _ handler: @escaping RouteHandler) {
Expand Down
11 changes: 7 additions & 4 deletions Sources/Blackfire/Server.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,20 @@ class Server {

let delegate: ServerDelegate

init(socket: Socket, port: Int, delegate: ServerDelegate, type: SocketType = .tcp) {
let queueType: QueueType

init(socket: Socket, port: Int, delegate: ServerDelegate, type: SocketType = .tcp, queueType: QueueType) {
self.socket = socket
self.port = port
self.delegate = delegate
self.type = type
self.queueType = queueType
dispatcher = DispatchSource.makeReadSource(fileDescriptor: socket.raw)
}

public convenience init(port: Int, delegate: ServerDelegate, type: SocketType) throws {
public convenience init(port: Int, delegate: ServerDelegate, type: SocketType, queueType: QueueType) throws {
let socket = try Socket()
self.init(socket: socket, port: port, delegate: delegate)
self.init(socket: socket, port: port, delegate: delegate, type: type, queueType: queueType)
}

public func listen() throws {
Expand Down Expand Up @@ -76,7 +79,7 @@ class Server {
let fd = systemAccept(self.socket.raw, addr, &len)
let client = try Socket(raw: fd)
addr.deallocate(capacity: 1)
return SocketConnection(socket: client)
return SocketConnection(socket: client, queueType: queueType)
}

private func bind(socket: Socket, address: Address) throws {
Expand Down

0 comments on commit f38b214

Please sign in to comment.