Minor protocol fixes

This commit is contained in:
Shadowfacts 2020-07-23 09:18:59 -04:00
parent 06a9c1fcac
commit 2c023bc02e
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
3 changed files with 48 additions and 23 deletions

View File

@ -29,7 +29,7 @@ class BrowserWindowController: NSWindowController {
contentViewController = NSHostingController(rootView: ContentView(navigator: navigator)) contentViewController = NSHostingController(rootView: ContentView(navigator: navigator))
urlUpdater = navigator.$currentURL.sink(receiveValue: self.currentURLChanged(_:)) urlUpdater = navigator.$currentURL.receive(on: DispatchQueue.main).sink(receiveValue: self.currentURLChanged(_:))
} }
private func currentURLChanged(_ newValue: URL) { private func currentURLChanged(_ newValue: URL) {

View File

@ -21,13 +21,16 @@ public class GeminiDataTask {
public init(request: GeminiRequest, completion: @escaping Completion) { public init(request: GeminiRequest, completion: @escaping Completion) {
self.request = request self.request = request
self.completion = completion self.completion = completion
self.connection = NWConnection(to: .url(request.url), using: .gemini) let port = request.url.port != nil ? UInt16(request.url.port!) : 1965
let endpoint: NWEndpoint = .hostPort(host: NWEndpoint.Host(request.url.host!), port: NWEndpoint.Port(rawValue: port)!)
self.connection = NWConnection(to: endpoint, using: .gemini)
self.connection.stateUpdateHandler = { (newState) in self.connection.stateUpdateHandler = { (newState) in
switch newState { switch newState {
case .ready: case .ready:
self.sendRequest() self.sendRequest()
case let .failed(error): case let .failed(error):
self.state = .completed
self.connection.cancel() self.connection.cancel()
self.completion(.failure(.connectionError(error))) self.completion(.failure(.connectionError(error)))
default: default:
@ -47,6 +50,7 @@ public class GeminiDataTask {
public func resume() { public func resume() {
guard state == .unstarted else { return } guard state == .unstarted else { return }
state = .started
connection.start(queue: GeminiDataTask.queue) connection.start(queue: GeminiDataTask.queue)
} }
@ -59,8 +63,13 @@ public class GeminiDataTask {
private func sendRequest() { private func sendRequest() {
let message = NWProtocolFramer.Message(geminiRequest: request) let message = NWProtocolFramer.Message(geminiRequest: request)
let context = NWConnection.ContentContext(identifier: "GeminiRequest", metadata: [message]) let context = NWConnection.ContentContext(identifier: "GeminiRequest", metadata: [message])
connection.send(content: nil, contentContext: context, isComplete: true, completion: .contentProcessed({ (_) in })) connection.send(content: nil, contentContext: context, isComplete: true, completion: .contentProcessed({ (error) in
state = .started if let error = error {
self.state = .completed
self.connection.cancel()
self.completion(.failure(.connectionError(error)))
}
}))
receive() receive()
} }

View File

@ -12,6 +12,9 @@ class GeminiProtocol: NWProtocolFramerImplementation {
static let label = "Gemini" static let label = "Gemini"
private var tempStatusCode: GeminiResponseHeader.StatusCode?
private var tempMeta: String?
required init(framer: NWProtocolFramer.Instance) { required init(framer: NWProtocolFramer.Instance) {
} }
@ -30,23 +33,25 @@ class GeminiProtocol: NWProtocolFramerImplementation {
} }
func handleInput(framer: NWProtocolFramer.Instance) -> Int { func handleInput(framer: NWProtocolFramer.Instance) -> Int {
while true { if tempStatusCode == nil {
var tempStatusCode: GeminiResponseHeader.StatusCode? _ = framer.parseInput(minimumIncompleteLength: 3, maximumLength: 3) { (buffer, isComplete) -> Int in
let parsedStatusCodeAndSpace = framer.parseInput(minimumIncompleteLength: 3, maximumLength: 3) { (buffer, isComplete) -> Int in
guard let buffer = buffer, guard let buffer = buffer,
buffer.count == 3 else { return 0 } buffer.count == 3 else { return 0 }
tempStatusCode = GeminiResponseHeader.StatusCode(buffer) self.tempStatusCode = GeminiResponseHeader.StatusCode(buffer)
return 3 return 3
} }
guard parsedStatusCodeAndSpace, let statusCode = tempStatusCode else { }
return 3 guard let statusCode = tempStatusCode else {
} return 3
}
var tempMeta: String?
var attemptedMetaLength: Int?
if tempMeta == nil {
// Minimum length is 2 bytes, spec does not say meta string is required // Minimum length is 2 bytes, spec does not say meta string is required
let parsedMeta = framer.parseInput(minimumIncompleteLength: 2, maximumLength: 1024 + 2) { (buffer, isComplete) -> Int in _ = framer.parseInput(minimumIncompleteLength: 2, maximumLength: 1024 + 2) { (buffer, isComplete) -> Int in
guard let buffer = buffer, guard let buffer = buffer,
buffer.count >= 2 else { return 0 } buffer.count >= 2 else { return 0 }
attemptedMetaLength = buffer.count
let lastPossibleCRIndex = buffer.index(before: buffer.index(before: buffer.endIndex)) let lastPossibleCRIndex = buffer.index(before: buffer.index(before: buffer.endIndex))
var index = buffer.startIndex var index = buffer.startIndex
@ -60,19 +65,30 @@ class GeminiProtocol: NWProtocolFramerImplementation {
index = buffer.index(after: index) index = buffer.index(after: index)
} }
guard found else { fatalError("Didn't find <CR><LF> in buffer. Meta string was longer than 1024 bytes") } if !found {
if buffer.count < 1026 {
return 0
} else {
fatalError("Didn't find <CR><LF> in buffer. Meta string was longer than 1024 bytes")
}
}
tempMeta = String(bytes: buffer[..<index], encoding: .utf8) self.tempMeta = String(bytes: buffer[..<index], encoding: .utf8)
return buffer.startIndex.distance(to: index) return buffer.startIndex.distance(to: index) + 2
} }
guard parsedMeta, let meta = tempMeta else { }
// todo: what should actually be returned here? the minimum number of bytes necessary? guard let meta = tempMeta else {
if let attempted = attemptedMetaLength {
return attempted + 1
} else {
return 2 return 2
} }
}
let header = GeminiResponseHeader(status: statusCode, meta: meta)
let header = GeminiResponseHeader(status: statusCode, meta: meta)
let message = NWProtocolFramer.Message(geminiResponseHeader: header)
let message = NWProtocolFramer.Message(geminiResponseHeader: header)
while true {
if !framer.deliverInputNoCopy(length: .max, message: message, isComplete: true) { if !framer.deliverInputNoCopy(length: .max, message: message, isComplete: true) {
return 0 return 0
} }