82 lines
2.3 KiB
Swift
Executable File
82 lines
2.3 KiB
Swift
Executable File
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))
|
|
}
|
|
}
|
|
}
|