mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Use content proxy to configure all proxied content requests.
This commit is contained in:
parent
a2d48aa020
commit
5eaeeff838
|
@ -310,6 +310,12 @@ extension GiphyError: LocalizedError {
|
|||
}
|
||||
let urlString = "/v1/gifs/search?api_key=\(kGiphyApiKey)&offset=\(kGiphyPageOffset)&limit=\(kGiphyPageSize)&q=\(queryEncoded)"
|
||||
|
||||
guard ContentProxy.configureSessionManager(sessionManager: sessionManager, forUrl: urlString) else {
|
||||
owsFailDebug("Could not configure query: \(query).")
|
||||
failure(nil)
|
||||
return
|
||||
}
|
||||
|
||||
sessionManager.get(urlString,
|
||||
parameters: [String: AnyObject](),
|
||||
progress: nil,
|
||||
|
|
|
@ -589,10 +589,10 @@ public class OWSLinkPreview: MTLModel {
|
|||
}
|
||||
}
|
||||
|
||||
class func downloadLink(url: String,
|
||||
class func downloadLink(url urlString: String,
|
||||
remainingRetries: UInt = 3) -> Promise<Data> {
|
||||
|
||||
Logger.verbose("url: \(url)")
|
||||
Logger.verbose("url: \(urlString)")
|
||||
|
||||
let sessionConfiguration = ContentProxy.sessionConfiguration()
|
||||
|
||||
|
@ -605,13 +605,13 @@ public class OWSLinkPreview: MTLModel {
|
|||
sessionManager.requestSerializer = AFHTTPRequestSerializer()
|
||||
sessionManager.responseSerializer = AFHTTPResponseSerializer()
|
||||
|
||||
// Remove all headers from the request.
|
||||
for headerField in sessionManager.requestSerializer.httpRequestHeaders.keys {
|
||||
sessionManager.requestSerializer.setValue(nil, forHTTPHeaderField: headerField)
|
||||
guard ContentProxy.configureSessionManager(sessionManager: sessionManager, forUrl: urlString) else {
|
||||
owsFailDebug("Could not configure url: \(urlString).")
|
||||
return Promise(error: LinkPreviewError.assertionFailure)
|
||||
}
|
||||
|
||||
let (promise, resolver) = Promise<Data>.pending()
|
||||
sessionManager.get(url,
|
||||
sessionManager.get(urlString,
|
||||
parameters: [String: AnyObject](),
|
||||
progress: nil,
|
||||
success: { task, value in
|
||||
|
@ -654,7 +654,7 @@ public class OWSLinkPreview: MTLModel {
|
|||
resolver.reject(LinkPreviewError.couldNotDownload)
|
||||
return
|
||||
}
|
||||
OWSLinkPreview.downloadLink(url: url, remainingRetries: remainingRetries - 1)
|
||||
OWSLinkPreview.downloadLink(url: urlString, remainingRetries: remainingRetries - 1)
|
||||
.done(on: DispatchQueue.global()) { (data) in
|
||||
resolver.fulfill(data)
|
||||
}.catch(on: DispatchQueue.global()) { (error) in
|
||||
|
|
|
@ -51,4 +51,102 @@ public class ContentProxy: NSObject {
|
|||
sessionManager.responseSerializer = AFJSONResponseSerializer()
|
||||
return sessionManager
|
||||
}
|
||||
}
|
||||
|
||||
static let userAgent = "Signal iOS (+https://signal.org/download)"
|
||||
|
||||
public class func configureProxiedRequest(request: inout URLRequest) -> Bool {
|
||||
request.addValue(userAgent, forHTTPHeaderField: "User-Agent")
|
||||
|
||||
padRequestSize(request: &request)
|
||||
|
||||
guard let url = request.url,
|
||||
let scheme = url.scheme,
|
||||
scheme.lowercased() == "https" else {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@objc
|
||||
public class func configureSessionManager(sessionManager: AFHTTPSessionManager,
|
||||
forUrl urlString: String) -> Bool {
|
||||
|
||||
guard let url = URL(string: urlString, relativeTo: sessionManager.baseURL) else {
|
||||
owsFailDebug("Invalid URL query: \(urlString).")
|
||||
return false
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
|
||||
guard configureProxiedRequest(request: &request) else {
|
||||
owsFailDebug("Invalid URL query: \(urlString).")
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove all headers from the request.
|
||||
for headerField in sessionManager.requestSerializer.httpRequestHeaders.keys {
|
||||
sessionManager.requestSerializer.setValue(nil, forHTTPHeaderField: headerField)
|
||||
}
|
||||
// Honor the request's headers.
|
||||
if let allHTTPHeaderFields = request.allHTTPHeaderFields {
|
||||
for (headerField, headerValue) in allHTTPHeaderFields {
|
||||
sessionManager.requestSerializer.setValue(headerValue, forHTTPHeaderField: headerField)
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public class 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: "X-SignalPadding")
|
||||
}
|
||||
|
||||
private class 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 {
|
||||
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.")
|
||||
}
|
||||
if let httpBody = request.httpBody {
|
||||
result += httpBody.count
|
||||
}
|
||||
return UInt(result)
|
||||
}}
|
||||
|
|
|
@ -657,8 +657,6 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
|
|||
return
|
||||
}
|
||||
|
||||
let userAgent = "Signal iOS (+https://signal.org/download)"
|
||||
|
||||
if assetRequest.state == .waiting {
|
||||
// If asset request hasn't yet determined the resource size,
|
||||
// try to do so now, by requesting a small initial segment.
|
||||
|
@ -671,8 +669,14 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
|
|||
request.httpShouldUsePipelining = true
|
||||
let rangeHeaderValue = "bytes=\(segmentStart)-\(segmentStart + segmentLength - 1)"
|
||||
request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range")
|
||||
request.addValue(userAgent, forHTTPHeaderField: "User-Agent")
|
||||
padRequestSize(request: &request)
|
||||
|
||||
guard ContentProxy.configureProxiedRequest(request: &request) else {
|
||||
assetRequest.state = .failed
|
||||
assetRequestDidFail(assetRequest: assetRequest)
|
||||
processRequestQueueSync()
|
||||
return
|
||||
}
|
||||
|
||||
let task = downloadSession.dataTask(with: request, completionHandler: { data, response, error -> Void in
|
||||
self.handleAssetSizeResponse(assetRequest: assetRequest, data: data, response: response, error: error)
|
||||
})
|
||||
|
@ -692,8 +696,14 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
|
|||
request.httpShouldUsePipelining = true
|
||||
let rangeHeaderValue = "bytes=\(assetSegment.segmentStart)-\(assetSegment.segmentStart + assetSegment.segmentLength - 1)"
|
||||
request.addValue(rangeHeaderValue, forHTTPHeaderField: "Range")
|
||||
request.addValue(userAgent, forHTTPHeaderField: "User-Agent")
|
||||
padRequestSize(request: &request)
|
||||
|
||||
guard ContentProxy.configureProxiedRequest(request: &request) else {
|
||||
assetRequest.state = .failed
|
||||
assetRequestDidFail(assetRequest: assetRequest)
|
||||
processRequestQueueSync()
|
||||
return
|
||||
}
|
||||
|
||||
let task: URLSessionDataTask = downloadSession.dataTask(with: request)
|
||||
task.assetRequest = assetRequest
|
||||
task.assetSegment = assetSegment
|
||||
|
@ -705,64 +715,6 @@ open class ProxiedContentDownloader: NSObject, URLSessionTaskDelegate, URLSessio
|
|||
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: "X-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 != 2 {
|
||||
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.")
|
||||
}
|
||||
if let httpBody = request.httpBody {
|
||||
result += httpBody.count
|
||||
}
|
||||
return UInt(result)
|
||||
}
|
||||
|
||||
private func handleAssetSizeResponse(assetRequest: ProxiedContentAssetRequest, data: Data?, response: URLResponse?, error: Error?) {
|
||||
guard error == nil else {
|
||||
assetRequest.state = .failed
|
||||
|
|
Loading…
Reference in a new issue