From b0be2ba0e60f4892d09361b32dbfd1670766dd07 Mon Sep 17 00:00:00 2001 From: Marc Rominger Date: Sun, 12 Mar 2023 03:32:44 +0100 Subject: [PATCH] easier gzip --- .../Plugins/CapacitorUrlRequest.swift | 38 ++- .../Capacitor/Plugins/Data+Gzip.swift | 290 ------------------ 2 files changed, 37 insertions(+), 291 deletions(-) delete mode 100644 ios/Capacitor/Capacitor/Plugins/Data+Gzip.swift diff --git a/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift b/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift index 09edbe646..ff411156f 100644 --- a/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift +++ b/ios/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift @@ -1,4 +1,5 @@ import Foundation +import Compression open class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate { public var request: URLRequest @@ -125,12 +126,47 @@ open class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate { } } + private func gzipCompress(data: Data) throws -> Data { + let stream = OutputStream(toMemory: ()) + let windowBits = Int32(15) + let level = Int32(COMPRESSION_LEVEL_DEFAULT) + let strategy = Int32(COMPRESSION_STRATEGY_DEFAULT) + let status = compression_stream_init(&stream.stream, COMPRESSION_STREAM_ENCODE, COMPRESSION_ZLIB, windowBits) + guard status == COMPRESSION_STATUS_OK else { + throw NSError(domain: "compression_stream_init", code: Int(status), userInfo: nil) + } + var compressedData = Data() + let bufferSize = 1024 * 1024 + var buffer = UnsafeMutablePointer.allocate(capacity: bufferSize) + defer { + buffer.deallocate() + } + stream.open() + defer { + stream.close() + } + var inputSize = data.count + var outputSize = 0 + while inputSize > 0 { + let count = min(inputSize, bufferSize) + data.copyBytes(to: buffer, from: 0.. Data? { // string to Data let dataBody = try getRequestDataAsString(body) // gzip compression - let compressedData: Data = try dataBody.gzipped() + let compressedData: Data = self.gzipCompress(dataBody) return compressedData } diff --git a/ios/Capacitor/Capacitor/Plugins/Data+Gzip.swift b/ios/Capacitor/Capacitor/Plugins/Data+Gzip.swift deleted file mode 100644 index c3e1ad37a..000000000 --- a/ios/Capacitor/Capacitor/Plugins/Data+Gzip.swift +++ /dev/null @@ -1,290 +0,0 @@ -// -// Data+Gzip.swift -// - -/* - The MIT License (MIT) - - © 2014-2020 1024jp - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. - */ - -import struct Foundation.Data - -#if os(Linux) - import zlibLinux -#else - import zlib -#endif - -/// Compression level whose rawValue is based on the zlib's constants. -public struct CompressionLevel: RawRepresentable { - - /// Compression level in the range of `0` (no compression) to `9` (maximum compression). - public let rawValue: Int32 - - public static let noCompression = CompressionLevel(Z_NO_COMPRESSION) - public static let bestSpeed = CompressionLevel(Z_BEST_SPEED) - public static let bestCompression = CompressionLevel(Z_BEST_COMPRESSION) - - public static let defaultCompression = CompressionLevel(Z_DEFAULT_COMPRESSION) - - - public init(rawValue: Int32) { - - self.rawValue = rawValue - } - - - public init(_ rawValue: Int32) { - - self.rawValue = rawValue - } - -} - - -/// Errors on gzipping/gunzipping based on the zlib error codes. -public struct GzipError: Swift.Error { - // cf. http://www.zlib.net/manual.html - - public enum Kind: Equatable { - /// The stream structure was inconsistent. - /// - /// - underlying zlib error: `Z_STREAM_ERROR` (-2) - case stream - - /// The input data was corrupted - /// (input stream not conforming to the zlib format or incorrect check value). - /// - /// - underlying zlib error: `Z_DATA_ERROR` (-3) - case data - - /// There was not enough memory. - /// - /// - underlying zlib error: `Z_MEM_ERROR` (-4) - case memory - - /// No progress is possible or there was not enough room in the output buffer. - /// - /// - underlying zlib error: `Z_BUF_ERROR` (-5) - case buffer - - /// The zlib library version is incompatible with the version assumed by the caller. - /// - /// - underlying zlib error: `Z_VERSION_ERROR` (-6) - case version - - /// An unknown error occurred. - /// - /// - parameter code: return error by zlib - case unknown(code: Int) - } - - /// Error kind. - public let kind: Kind - - /// Returned message by zlib. - public let message: String - - - internal init(code: Int32, msg: UnsafePointer?) { - - self.message = { - guard let msg = msg, let message = String(validatingUTF8: msg) else { - return "Unknown gzip error" - } - return message - }() - - self.kind = { - switch code { - case Z_STREAM_ERROR: - return .stream - case Z_DATA_ERROR: - return .data - case Z_MEM_ERROR: - return .memory - case Z_BUF_ERROR: - return .buffer - case Z_VERSION_ERROR: - return .version - default: - return .unknown(code: Int(code)) - } - }() - } - - - public var localizedDescription: String { - - return self.message - } - -} - - -extension Data { - - /// Whether the receiver is compressed in gzip format. - public var isGzipped: Bool { - - return self.starts(with: [0x1f, 0x8b]) // check magic number - } - - - /// Create a new `Data` instance by compressing the receiver using zlib. - /// Throws an error if compression failed. - /// - /// - Parameter level: Compression level. - /// - Returns: Gzip-compressed `Data` instance. - /// - Throws: `GzipError` - public func gzipped(level: CompressionLevel = .defaultCompression) throws -> Data { - - guard !self.isEmpty else { - return Data() - } - - var stream = z_stream() - var status: Int32 - - status = deflateInit2_(&stream, level.rawValue, Z_DEFLATED, MAX_WBITS + 16, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY, ZLIB_VERSION, Int32(DataSize.stream)) - - guard status == Z_OK else { - // deflateInit2 returns: - // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. - // Z_MEM_ERROR There was not enough memory. - // Z_STREAM_ERROR A parameter is invalid. - - throw GzipError(code: status, msg: stream.msg) - } - - var data = Data(capacity: DataSize.chunk) - repeat { - if Int(stream.total_out) >= data.count { - data.count += DataSize.chunk - } - - let inputCount = self.count - let outputCount = data.count - - self.withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in - stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(stream.total_in)) - stream.avail_in = uint(inputCount) - uInt(stream.total_in) - - data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in - stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(stream.total_out)) - stream.avail_out = uInt(outputCount) - uInt(stream.total_out) - - status = deflate(&stream, Z_FINISH) - - stream.next_out = nil - } - - stream.next_in = nil - } - - } while stream.avail_out == 0 - - guard deflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { - throw GzipError(code: status, msg: stream.msg) - } - - data.count = Int(stream.total_out) - - return data - } - - - /// Create a new `Data` instance by decompressing the receiver using zlib. - /// Throws an error if decompression failed. - /// - /// - Returns: Gzip-decompressed `Data` instance. - /// - Throws: `GzipError` - public func gunzipped() throws -> Data { - - guard !self.isEmpty else { - return Data() - } - - var stream = z_stream() - var status: Int32 - - status = inflateInit2_(&stream, MAX_WBITS + 32, ZLIB_VERSION, Int32(DataSize.stream)) - - guard status == Z_OK else { - // inflateInit2 returns: - // Z_VERSION_ERROR The zlib library version is incompatible with the version assumed by the caller. - // Z_MEM_ERROR There was not enough memory. - // Z_STREAM_ERROR A parameters are invalid. - - throw GzipError(code: status, msg: stream.msg) - } - - var data = Data(capacity: self.count * 2) - repeat { - if Int(stream.total_out) >= data.count { - data.count += self.count / 2 - } - - let inputCount = self.count - let outputCount = data.count - - self.withUnsafeBytes { (inputPointer: UnsafeRawBufferPointer) in - stream.next_in = UnsafeMutablePointer(mutating: inputPointer.bindMemory(to: Bytef.self).baseAddress!).advanced(by: Int(stream.total_in)) - stream.avail_in = uint(inputCount) - uInt(stream.total_in) - - data.withUnsafeMutableBytes { (outputPointer: UnsafeMutableRawBufferPointer) in - stream.next_out = outputPointer.bindMemory(to: Bytef.self).baseAddress!.advanced(by: Int(stream.total_out)) - stream.avail_out = uInt(outputCount) - uInt(stream.total_out) - - status = inflate(&stream, Z_SYNC_FLUSH) - - stream.next_out = nil - } - - stream.next_in = nil - } - - } while status == Z_OK - - guard inflateEnd(&stream) == Z_OK, status == Z_STREAM_END else { - // inflate returns: - // Z_DATA_ERROR The input data was corrupted (input stream not conforming to the zlib format or incorrect check value). - // Z_STREAM_ERROR The stream structure was inconsistent (for example if next_in or next_out was NULL). - // Z_MEM_ERROR There was not enough memory. - // Z_BUF_ERROR No progress is possible or there was not enough room in the output buffer when Z_FINISH is used. - - throw GzipError(code: status, msg: stream.msg) - } - - data.count = Int(stream.total_out) - - return data - } - -} - - -private enum DataSize { - - static let chunk = 1 << 14 - static let stream = MemoryLayout.size -} \ No newline at end of file