Pad proxied request sizes.

This commit is contained in:
Matthew Chen 2019-02-20 16:25:07 -05:00
parent daa58c2ac8
commit 40768825c8

View file

@ -669,6 +669,7 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
request.httpShouldUsePipelining = true request.httpShouldUsePipelining = true
let rangeHeaderValue = "bytes=\(segmentStart)-\(segmentStart + segmentLength - 1)" let rangeHeaderValue = "bytes=\(segmentStart)-\(segmentStart + segmentLength - 1)"
request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range") request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range")
padRequestSize(request: &request)
let task = downloadSession.dataTask(with: request, completionHandler: { data, response, error -> Void in let task = downloadSession.dataTask(with: request, completionHandler: { data, response, error -> Void in
self.handleAssetSizeResponse(assetRequest: assetRequest, data: data, response: response, error: error) self.handleAssetSizeResponse(assetRequest: assetRequest, data: data, response: response, error: error)
}) })
@ -688,6 +689,7 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
request.httpShouldUsePipelining = true request.httpShouldUsePipelining = true
let rangeHeaderValue = "bytes=\(assetSegment.segmentStart)-\(assetSegment.segmentStart + assetSegment.segmentLength - 1)" let rangeHeaderValue = "bytes=\(assetSegment.segmentStart)-\(assetSegment.segmentStart + assetSegment.segmentLength - 1)"
request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range") request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range")
padRequestSize(request: &request)
let task: URLSessionDataTask = downloadSession.dataTask(with: request) let task: URLSessionDataTask = downloadSession.dataTask(with: request)
task.assetRequest = assetRequest task.assetRequest = assetRequest
task.assetSegment = assetSegment task.assetSegment = assetSegment
@ -699,6 +701,61 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
processRequestQueueSync() processRequestQueueSync()
} }
private func padRequestSize(request: inout URLRequest) {
guard let sizeEstimate: UInt = estimateRequestSize(request: request) else {
owsFailDebug("Could not estimate request size.")
return
}
// We pad the estimated size to an even multiple of paddingQuantum (plus the
// extra ": " and "\r\n"). The exact size doesn't matter so long as the
// padding is consistent.
let paddingQuantum: UInt = 1024
let paddingSize = paddingQuantum - (sizeEstimate % paddingQuantum)
let padding = String(repeating: ".", count: Int(paddingSize))
request.addValue(padding, forHTTPHeaderField: "SignalPadding")
}
private func estimateRequestSize(request: URLRequest) -> UInt? {
// iOS doesn't offer an exact way to measure request sizes on the wire,
// but we can reliably estimate request sizes using the "knowns", e.g.
// HTTP method, path, querystring, headers. The "unknowns" should be
// consistent between requests.
guard let url = request.url?.absoluteString else {
owsFailDebug("Request missing URL.")
return nil
}
guard let components = URLComponents(string: url) else {
owsFailDebug("Request has invalid URL.")
return nil
}
var result: Int = 0
if let httpMethod = request.httpMethod {
result += httpMethod.count
}
result += components.percentEncodedPath.count
if let percentEncodedQuery = components.percentEncodedQuery {
result += percentEncodedQuery.count
}
if let allHTTPHeaderFields = request.allHTTPHeaderFields {
if allHTTPHeaderFields.count != 1 {
owsFailDebug("Request has unexpected number of headers.")
}
for (key, value) in allHTTPHeaderFields {
// Each header has 4 extra bytes:
//
// * Two for the key/value separator ": "
// * Two for "\r\n", the line break in the HTTP protocol spec.
result += key.count + value.count + 4
}
} else {
owsFailDebug("Request has no headers.")
}
return UInt(result)
}
private func handleAssetSizeResponse(assetRequest: ProxiedContentAssetRequest, data: Data?, response: URLResponse?, error: Error?) { private func handleAssetSizeResponse(assetRequest: ProxiedContentAssetRequest, data: Data?, response: URLResponse?, error: Error?) {
guard error == nil else { guard error == nil else {
assetRequest.state = .failed assetRequest.state = .failed