src/Utils/HTTPStreamScanner.swift
import Foundation
class HTTPStreamScanner {
enum ReadAction {
case readHeader, readContent(Int), stop
}
enum Result {
case header(HTTPHeader), content(Data)
}
enum HTTPStreamScannerError: Error {
case contentIsTooLong, scannerIsStopped, unsupportedStreamType
}
var nextAction: ReadAction = .readHeader
var remainContentLength: Int = 0
var currentHeader: HTTPHeader!
var isConnect: Bool = false
func input(_ data: Data) throws -> Result {
switch nextAction {
case .readHeader:
let header: HTTPHeader
do {
header = try HTTPHeader(headerData: data)
// To temporarily solve a bug in firefox for mac
if currentHeader != nil && header.host != currentHeader.host {
throw HTTPStreamScannerError.unsupportedStreamType
}
} catch let error {
nextAction = .stop
throw error
}
if currentHeader == nil {
if header.isConnect {
isConnect = true
remainContentLength = -1
} else {
isConnect = false
remainContentLength = header.contentLength
}
} else {
remainContentLength = header.contentLength
}
currentHeader = header
setNextAction()
return .header(header)
case .readContent:
remainContentLength -= data.count
if !isConnect && remainContentLength < 0 {
nextAction = .stop
throw HTTPStreamScannerError.contentIsTooLong
}
setNextAction()
return .content(data)
case .stop:
throw HTTPStreamScannerError.scannerIsStopped
}
}
fileprivate func setNextAction() {
switch remainContentLength {
case 0:
nextAction = .readHeader
case _ where remainContentLength < 0:
nextAction = .readContent(-1)
default:
nextAction = .readContent(min(remainContentLength, Opt.MAXHTTPContentBlockLength))
}
}
}