in Sources/SwiftDocCUtilities/PreviewServer/RequestHandler/FileRequestHandler.swift [130:189]
func create<ChannelHandler: ChannelInboundHandler>(channelHandler: ChannelHandler) -> RequestHandler
where ChannelHandler.OutboundOut == HTTPServerResponsePart {
return { ctx, head in
// Guards for a valid URL request
guard let components = URLComponents(string: head.uri) else { throw RequestError(status: .badRequest) }
// Guard that the path is authorized for serving assets
let mimetype: String
if let assetMetadata = FileRequestHandler.matchingAssetMetadata(components.path) {
mimetype = assetMetadata.mimetype(components.path)
} else if let topLevelAssetMetadata = FileRequestHandler.matchingTopLevelAssetMetadata(components.path) {
mimetype = topLevelAssetMetadata.mimetype
} else {
throw RequestError(status: .unauthorized)
}
let fileURL = self.rootURL.appendingPathComponent(components.path.removingLeadingSlash)
// Discard requests to components starting with a period or referring to the user directory
guard components.path.components(separatedBy: "/")
.allSatisfy({ !$0.hasPrefix(".") && $0 != "~" }) else { throw RequestError(status: .unauthorized) }
var data: Data
let totalLength: Int
// Read the file contents
do {
data = try Data(contentsOf: fileURL, options: .mappedIfSafe)
totalLength = data.count
} catch {
throw RequestError(status: .notFound)
}
// Add Range header if neccessary
var headers = HTTPHeaders()
let range = head.headers["Range"].first.flatMap(RangeHeader.init)
if let range = range {
data = data.subdata(in: Range<Data.Index>(uncheckedBounds: (lower: range.min, upper: range.max+1)))
headers.add(name: "Content-Range", value: "bytes \(range.min)-\(range.max)/\(totalLength)")
headers.add(name: "Accept-Ranges", value: "bytes")
}
// Write the response to the output channel
var content = ctx.channel.allocator.buffer(capacity: totalLength)
content.writeBytes(data)
headers.add(name: "Content-Length", value: "\(data.count)")
headers.add(name: "Content-Type", value: mimetype)
// No caching of live preview
headers.add(name: "Cache-Control", value: "no-store, no-cache, must-revalidate, post-check=0, pre-check=0")
headers.add(name: "Pragma", value: "no-cache")
let responseHead = HTTPResponseHead(matchingRequestHead: head, status: range != nil ? .partialContent : .ok, headers: headers)
ctx.write(channelHandler.wrapOutboundOut(.head(responseHead)), promise: nil)
ctx.write(channelHandler.wrapOutboundOut(.body(.byteBuffer(content))), promise: nil)
}
}