Sources/Instrumentation/WKWebView/BodyCache/URLRequestSerialization.swift (93 lines of code) (raw):
//
// Copyright 2023 aliyun-sls Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import Foundation
class URLRequestSerialization {
private static let CharactersGeneralDelimitersToEncode = ":#[]@"
private static let CharactersSubDelimitersToEncode = "!$&'()*+,;="
private static let batchSize = 50
static var shared: URLRequestSerialization {
let instance = URLRequestSerialization()
return instance
}
func multipartFormRequestWithRequest(request: inout URLRequest, params: [String: Any], callback: ((_ formData: MultipartFormData) -> ())?) {
guard let method = request.httpMethod, method != "GET", method != "HEAD" else {
return
}
var formData = StreamingMultipartFormData(urlRequest: request, stringEncoding: String.Encoding.utf8)
for pair in URLRequestSerialization.queryStringPairsFromDictionary(dictionary: params) {
var data: Data?
if let value = pair.value as? Data {
data = value
} else if let _ = pair.value as? NSNull {
data = Data()
} else {
data = "\(pair.value ?? "")".data(using: String.Encoding.utf8)
}
if let d = data {
formData.appendPart(data: d, name: "\(pair.field ?? "")")
}
}
callback?(formData)
request = formData.requestByFinalizingMultipartFormData()
}
static func queryStringPairsFromDictionary(dictionary: [String: Any]) -> [QueryStringPairModule] {
return self.queryStringParisFromKeyAndValue(key: nil, value: dictionary)
}
static func queryStringParisFromKeyAndValue(key: String?, value: Any) ->[QueryStringPairModule] {
var queryStringComponents = [QueryStringPairModule]()
// let sortDescriptor = NSSortDescriptor.init(key: "description", ascending: true, selector: Selector("compare:"))
let sortDescriptor = NSSortDescriptor.init(key: "description", ascending: true, selector: #selector(NSString.compare(_:)))
if let vlu = value as? NSDictionary {
let keys = vlu.allKeys as NSArray
for k in keys.sortedArray(using: [sortDescriptor]) {
if let value = vlu[k] {
var kk: String
if let _ = key {
kk = "\(key!)\(k)"
} else {
kk = "\(k)"
}
queryStringComponents.append(contentsOf: self.queryStringParisFromKeyAndValue(key: kk, value: value))
}
}
} else if let vlu = value as? NSArray {
for v in vlu {
queryStringComponents.append(contentsOf: self.queryStringParisFromKeyAndValue(key: "\(key ?? "")", value: v))
}
} else if let vlu = value as? NSSet {
for v in vlu.sortedArray(using: [sortDescriptor]) {
queryStringComponents.append(contentsOf: self.queryStringParisFromKeyAndValue(key: key, value: v))
}
} else {
queryStringComponents.append(QueryStringPairModule(field: key, value: value))
}
return queryStringComponents
}
static func percentEscapedString(from: NSString) -> String {
var allowedCharacterSet = CharacterSet.urlQueryAllowed
allowedCharacterSet.remove(charactersIn: self.CharactersGeneralDelimitersToEncode.appending(self.CharactersSubDelimitersToEncode))
var index = 0
var escaped = ""
while index < from.length {
let length: UInt = UInt(min(from.length - index, self.batchSize))
var range: NSRange = NSMakeRange(index, Int(length))
range = from.rangeOfComposedCharacterSequences(for: range)
let subString: NSString = from.substring(with: range) as NSString
let encoded = subString.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet)
escaped.append(encoded!)
index += range.length
}
return escaped
}
}
struct QueryStringPairModule {
var field: Any?
var value: Any?
init(field: Any?, value: Any) {
self.field = field
self.value = value
}
func urlEncodedStringValue() -> String {
guard let value = value, let field = field else {
return URLRequestSerialization.percentEscapedString(from: field as! NSString)
}
return "\(URLRequestSerialization.percentEscapedString(from: field as! NSString))=\(URLRequestSerialization.percentEscapedString(from: value as! NSString))"
}
}