0001    // MultipartFormData.swift
0002    //
0003    // Copyright (c) 2014–2016 Alamofire Software Foundation (http://alamofire.org/)
0004    //
0005    // Permission is hereby granted, free of charge, to any person obtaining a copy
0006    // of this software and associated documentation files (the "Software"), to deal
0007    // in the Software without restriction, including without limitation the rights
0008    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0009    // copies of the Software, and to permit persons to whom the Software is
0010    // furnished to do so, subject to the following conditions:
0011    //
0012    // The above copyright notice and this permission notice shall be included in
0013    // all copies or substantial portions of the Software.
0014    //
0015    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0020    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0021    // THE SOFTWARE.
0022    
0023    import Foundation
0024    
0025    #if os(iOS) || os(watchOS) || os(tvOS)
0026    import MobileCoreServices
0027    #elseif os(OSX)
0028    import CoreServices
0029    #endif
0030    
0031    /**
0032        Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode 
0033        multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead 
0034        to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the 
0035        data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for 
0036        larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
0037    
0038        For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
0039        and the w3 form documentation.
0040    
0041        - https://www.ietf.org/rfc/rfc2388.txt
0042        - https://www.ietf.org/rfc/rfc2045.txt
0043        - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
0044    */
0045    public class MultipartFormData
Alamofire.swift:269
    multipartFormData: MultipartFormData -> Void,
Alamofire.swift:294
    multipartFormData: MultipartFormData -> Void,
Upload.swift:238
        multipartFormData: MultipartFormData -> Void,
Upload.swift:278
        multipartFormData: MultipartFormData -> Void,
Upload.swift:283
            let formData = MultipartFormData()
{ 0046 0047 // MARK: - Helper Types 0048 0049 struct EncodingCharacters
MultipartFormData.swift:67
                boundaryText = "--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:69
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:69
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:71
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)"
MultipartFormData.swift:71
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)"
MultipartFormData.swift:470
            headerText += "\(key): \(value)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:472
        headerText += EncodingCharacters.CRLF
{ 0050 static let CRLF
MultipartFormData.swift:67
                boundaryText = "--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:69
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:69
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:71
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)"
MultipartFormData.swift:71
                boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)"
MultipartFormData.swift:470
            headerText += "\(key): \(value)\(EncodingCharacters.CRLF)"
MultipartFormData.swift:472
        headerText += EncodingCharacters.CRLF
= "\r\n" 0051 } 0052 0053 struct BoundaryGenerator
MultipartFormData.swift:115
        self.boundary = BoundaryGenerator.randomBoundary()
MultipartFormData.swift:651
        return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary)
MultipartFormData.swift:655
        return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary)
MultipartFormData.swift:659
        return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary)
{ 0054 enum BoundaryType
MultipartFormData.swift:62
        static func boundaryData(boundaryType boundaryType: BoundaryType, boundary: String) -> NSData {
{ 0055 case Initial
MultipartFormData.swift:66
            case .Initial:
MultipartFormData.swift:651
        return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary)
, Encapsulated
MultipartFormData.swift:68
            case .Encapsulated:
MultipartFormData.swift:655
        return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary)
, Final
MultipartFormData.swift:70
            case .Final:
MultipartFormData.swift:659
        return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary)
0056 } 0057 0058 static func randomBoundary
MultipartFormData.swift:115
        self.boundary = BoundaryGenerator.randomBoundary()
() -> String { 0059 return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random()) 0060 } 0061 0062 static func boundaryData
MultipartFormData.swift:651
        return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary)
MultipartFormData.swift:655
        return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary)
MultipartFormData.swift:659
        return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary)
(boundaryType boundaryType: BoundaryType, boundary: String) -> NSData { 0063 let boundaryText: String 0064 0065 switch boundaryType { 0066 case .Initial: 0067 boundaryText = "--\(boundary)\(EncodingCharacters.CRLF)" 0068 case .Encapsulated: 0069 boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)\(EncodingCharacters.CRLF)" 0070 case .Final: 0071 boundaryText = "\(EncodingCharacters.CRLF)--\(boundary)--\(EncodingCharacters.CRLF)" 0072 } 0073 0074 return boundaryText.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 0075 } 0076 } 0077 0078 class BodyPart
MultipartFormData.swift:103
    private var bodyParts: [BodyPart]
MultipartFormData.swift:364
        let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
MultipartFormData.swift:447
    private func encodeBodyPart(bodyPart: BodyPart) throws -> NSData {
MultipartFormData.swift:466
    private func encodeHeaderDataForBodyPart(bodyPart: BodyPart) -> NSData {
MultipartFormData.swift:477
    private func encodeBodyStreamDataForBodyPart(bodyPart: BodyPart) throws -> NSData {
MultipartFormData.swift:517
    private func writeBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws {
MultipartFormData.swift:525
        bodyPart: BodyPart,
MultipartFormData.swift:533
    private func writeHeaderDataForBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws {
MultipartFormData.swift:538
    private func writeBodyStreamForBodyPart(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws {
MultipartFormData.swift:570
        bodyPart: BodyPart,
{ 0079 let headers
MultipartFormData.swift:86
            self.headers = headers
MultipartFormData.swift:469
        for (key, value) in bodyPart.headers {
: [String: String] 0080 let bodyStream
MultipartFormData.swift:87
            self.bodyStream = bodyStream
MultipartFormData.swift:478
        let inputStream = bodyPart.bodyStream
MultipartFormData.swift:539
        let inputStream = bodyPart.bodyStream
: NSInputStream 0081 let bodyContentLength
MultipartFormData.swift:88
            self.bodyContentLength = bodyContentLength
MultipartFormData.swift:98
    public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
: UInt64 0082 var hasInitialBoundary
MultipartFormData.swift:388
        bodyParts.first?.hasInitialBoundary = true
MultipartFormData.swift:434
        self.bodyParts.first?.hasInitialBoundary = true
MultipartFormData.swift:450
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
MultipartFormData.swift:529
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
= false 0083 var hasFinalBoundary
MultipartFormData.swift:389
        bodyParts.last?.hasFinalBoundary = true
MultipartFormData.swift:435
        self.bodyParts.last?.hasFinalBoundary = true
MultipartFormData.swift:459
        if bodyPart.hasFinalBoundary {
MultipartFormData.swift:574
        if bodyPart.hasFinalBoundary {
= false 0084 0085 init
MultipartFormData.swift:364
        let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
(headers: [String: String], bodyStream: NSInputStream, bodyContentLength: UInt64) { 0086 self.headers = headers 0087 self.bodyStream = bodyStream 0088 self.bodyContentLength = bodyContentLength 0089 } 0090 } 0091 0092 // MARK: - Properties 0093 0094 /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`. 0095 public var contentType
Upload.swift:287
            URLRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")
: String { return "multipart/form-data; boundary=\(boundary)" } 0096 0097 /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries. 0098 public var contentLength
Upload.swift:291
            if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } } 0099 0100 /// The boundary used to separate the body parts in the encoded form data. 0101 public let boundary
MultipartFormData.swift:95
    public var contentType: String { return "multipart/form-data; boundary=\(boundary)" }
MultipartFormData.swift:115
        self.boundary = BoundaryGenerator.randomBoundary()
MultipartFormData.swift:651
        return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary)
MultipartFormData.swift:655
        return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary)
MultipartFormData.swift:659
        return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary)
: String 0102 0103 private var bodyParts
MultipartFormData.swift:98
    public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
MultipartFormData.swift:116
        self.bodyParts = []
MultipartFormData.swift:365
        bodyParts.append(bodyPart)
MultipartFormData.swift:388
        bodyParts.first?.hasInitialBoundary = true
MultipartFormData.swift:389
        bodyParts.last?.hasFinalBoundary = true
MultipartFormData.swift:391
        for bodyPart in bodyParts {
MultipartFormData.swift:434
        self.bodyParts.first?.hasInitialBoundary = true
MultipartFormData.swift:435
        self.bodyParts.last?.hasFinalBoundary = true
MultipartFormData.swift:437
        for bodyPart in self.bodyParts {
: [BodyPart] 0104 private var bodyPartError
MultipartFormData.swift:382
        if let bodyPartError = bodyPartError {
MultipartFormData.swift:410
        if let bodyPartError = bodyPartError {
MultipartFormData.swift:665
        if bodyPartError == nil {
MultipartFormData.swift:666
            bodyPartError = error
: NSError? 0105 private let streamBufferSize
MultipartFormData.swift:124
        self.streamBufferSize = 1024
MultipartFormData.swift:486
            var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0)
MultipartFormData.swift:487
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
MultipartFormData.swift:544
            var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0)
MultipartFormData.swift:545
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
: Int 0106 0107 // MARK: - Lifecycle 0108 0109 /** 0110 Creates a multipart form data object. 0111 0112 - returns: The multipart form data object. 0113 */ 0114 public init
Upload.swift:283
            let formData = MultipartFormData()
() { 0115 self.boundary = BoundaryGenerator.randomBoundary() 0116 self.bodyParts = [] 0117 0118 /** 0119 * The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more 0120 * information, please refer to the following article: 0121 * - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html 0122 */ 0123 0124 self.streamBufferSize = 1024 0125 } 0126 0127 // MARK: - Body Parts 0128 0129 /** 0130 Creates a body part from the data and appends it to the multipart form data object. 0131 0132 The body part data will be encoded using the following format: 0133 0134 - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 0135 - Encoded data 0136 - Multipart form boundary 0137 0138 - parameter data: The data to encode into the multipart form data. 0139 - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 0140 */ 0141 public func appendBodyPart(data data: NSData, name: String) { 0142 let headers = contentHeaders(name: name) 0143 let stream = NSInputStream(data: data) 0144 let length = UInt64(data.length) 0145 0146 appendBodyPart(stream: stream, length: length, headers: headers) 0147 } 0148 0149 /** 0150 Creates a body part from the data and appends it to the multipart form data object. 0151 0152 The body part data will be encoded using the following format: 0153 0154 - `Content-Disposition: form-data; name=#{name}` (HTTP Header) 0155 - `Content-Type: #{generated mimeType}` (HTTP Header) 0156 - Encoded data 0157 - Multipart form boundary 0158 0159 - parameter data: The data to encode into the multipart form data. 0160 - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 0161 - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header. 0162 */ 0163 public func appendBodyPart(data data: NSData, name: String, mimeType: String) { 0164 let headers = contentHeaders(name: name, mimeType: mimeType) 0165 let stream = NSInputStream(data: data) 0166 let length = UInt64(data.length) 0167 0168 appendBodyPart(stream: stream, length: length, headers: headers) 0169 } 0170 0171 /** 0172 Creates a body part from the data and appends it to the multipart form data object. 0173 0174 The body part data will be encoded using the following format: 0175 0176 - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 0177 - `Content-Type: #{mimeType}` (HTTP Header) 0178 - Encoded file data 0179 - Multipart form boundary 0180 0181 - parameter data: The data to encode into the multipart form data. 0182 - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header. 0183 - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header. 0184 - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header. 0185 */ 0186 public func appendBodyPart(data data: NSData, name: String, fileName: String, mimeType: String) { 0187 let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 0188 let stream = NSInputStream(data: data) 0189 let length = UInt64(data.length) 0190 0191 appendBodyPart(stream: stream, length: length, headers: headers) 0192 } 0193 0194 /** 0195 Creates a body part from the file and appends it to the multipart form data object. 0196 0197 The body part data will be encoded using the following format: 0198 0199 - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header) 0200 - `Content-Type: #{generated mimeType}` (HTTP Header) 0201 - Encoded file data 0202 - Multipart form boundary 0203 0204 The filename in the `Content-Disposition` HTTP header is generated from the last path component of the 0205 `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the 0206 system associated MIME type. 0207 0208 - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 0209 - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 0210 */ 0211 public func appendBodyPart(fileURL fileURL: NSURL, name: String) { 0212 if let 0213 fileName = fileURL.lastPathComponent, 0214 pathExtension = fileURL.pathExtension 0215 { 0216 let mimeType = mimeTypeForPathExtension(pathExtension) 0217 appendBodyPart(fileURL: fileURL, name: name, fileName: fileName, mimeType: mimeType) 0218 } else { 0219 let failureReason = "Failed to extract the fileName of the provided URL: \(fileURL)" 0220 setBodyPartError(Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason)) 0221 } 0222 } 0223 0224 /** 0225 Creates a body part from the file and appends it to the multipart form data object. 0226 0227 The body part data will be encoded using the following format: 0228 0229 - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header) 0230 - Content-Type: #{mimeType} (HTTP Header) 0231 - Encoded file data 0232 - Multipart form boundary 0233 0234 - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data. 0235 - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header. 0236 - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header. 0237 - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header. 0238 */ 0239 public func appendBodyPart
MultipartFormData.swift:217
            appendBodyPart(fileURL: fileURL, name: name, fileName: fileName, mimeType: mimeType)
(fileURL fileURL: NSURL, name: String, fileName: String, mimeType: String) { 0240 let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 0241 0242 //============================================================ 0243 // Check 1 - is file URL? 0244 //============================================================ 0245 0246 guard fileURL.fileURL else { 0247 let failureReason = "The file URL does not point to a file URL: \(fileURL)" 0248 let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 0249 setBodyPartError(error) 0250 return 0251 } 0252 0253 //============================================================ 0254 // Check 2 - is file URL reachable? 0255 //============================================================ 0256 0257 var isReachable = true 0258 0259 if #available(OSX 10.10, *) { 0260 isReachable = fileURL.checkPromisedItemIsReachableAndReturnError(nil) 0261 } 0262 0263 guard isReachable else { 0264 let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: "The file URL is not reachable: \(fileURL)") 0265 setBodyPartError(error) 0266 return 0267 } 0268 0269 //============================================================ 0270 // Check 3 - is file URL a directory? 0271 //============================================================ 0272 0273 var isDirectory: ObjCBool = false 0274 0275 guard let 0276 path = fileURL.path 0277 where NSFileManager.defaultManager().fileExistsAtPath(path, isDirectory: &isDirectory) && !isDirectory else 0278 { 0279 let failureReason = "The file URL is a directory, not a file: \(fileURL)" 0280 let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 0281 setBodyPartError(error) 0282 return 0283 } 0284 0285 //============================================================ 0286 // Check 4 - can the file size be extracted? 0287 //============================================================ 0288 0289 var bodyContentLength: UInt64? 0290 0291 do { 0292 if let 0293 path = fileURL.path, 0294 fileSize = try NSFileManager.defaultManager().attributesOfItemAtPath(path)[NSFileSize] as? NSNumber 0295 { 0296 bodyContentLength = fileSize.unsignedLongLongValue 0297 } 0298 } catch { 0299 // No-op 0300 } 0301 0302 guard let length = bodyContentLength else { 0303 let failureReason = "Could not fetch attributes from the file URL: \(fileURL)" 0304 let error = Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 0305 setBodyPartError(error) 0306 return 0307 } 0308 0309 //============================================================ 0310 // Check 5 - can a stream be created from file URL? 0311 //============================================================ 0312 0313 guard let stream = NSInputStream(URL: fileURL) else { 0314 let failureReason = "Failed to create an input stream from the file URL: \(fileURL)" 0315 let error = Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) 0316 setBodyPartError(error) 0317 return 0318 } 0319 0320 appendBodyPart(stream: stream, length: length, headers: headers) 0321 } 0322 0323 /** 0324 Creates a body part from the stream and appends it to the multipart form data object. 0325 0326 The body part data will be encoded using the following format: 0327 0328 - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header) 0329 - `Content-Type: #{mimeType}` (HTTP Header) 0330 - Encoded stream data 0331 - Multipart form boundary 0332 0333 - parameter stream: The input stream to encode in the multipart form data. 0334 - parameter length: The content length of the stream. 0335 - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header. 0336 - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header. 0337 - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header. 0338 */ 0339 public func appendBodyPart( 0340 stream stream: NSInputStream, 0341 length: UInt64, 0342 name: String, 0343 fileName: String, 0344 mimeType: String) 0345 { 0346 let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType) 0347 appendBodyPart(stream: stream, length: length, headers: headers) 0348 } 0349 0350 /** 0351 Creates a body part with the headers, stream and length and appends it to the multipart form data object. 0352 0353 The body part data will be encoded using the following format: 0354 0355 - HTTP headers 0356 - Encoded stream data 0357 - Multipart form boundary 0358 0359 - parameter stream: The input stream to encode in the multipart form data. 0360 - parameter length: The content length of the stream. 0361 - parameter headers: The HTTP headers for the body part. 0362 */ 0363 public func appendBodyPart
MultipartFormData.swift:146
        appendBodyPart(stream: stream, length: length, headers: headers)
MultipartFormData.swift:168
        appendBodyPart(stream: stream, length: length, headers: headers)
MultipartFormData.swift:191
        appendBodyPart(stream: stream, length: length, headers: headers)
MultipartFormData.swift:320
        appendBodyPart(stream: stream, length: length, headers: headers)
MultipartFormData.swift:347
        appendBodyPart(stream: stream, length: length, headers: headers)
(stream stream: NSInputStream, length: UInt64, headers: [String: String]) { 0364 let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length) 0365 bodyParts.append(bodyPart) 0366 } 0367 0368 // MARK: - Data Encoding 0369 0370 /** 0371 Encodes all the appended body parts into a single `NSData` object. 0372 0373 It is important to note that this method will load all the appended body parts into memory all at the same 0374 time. This method should only be used when the encoded data will have a small memory footprint. For large data 0375 cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method. 0376 0377 - throws: An `NSError` if encoding encounters an error. 0378 0379 - returns: The encoded `NSData` if encoding is successful. 0380 */ 0381 public func encode
Upload.swift:293
                    let data = try formData.encode()
() throws -> NSData { 0382 if let bodyPartError = bodyPartError { 0383 throw bodyPartError 0384 } 0385 0386 let encoded = NSMutableData() 0387 0388 bodyParts.first?.hasInitialBoundary = true 0389 bodyParts.last?.hasFinalBoundary = true 0390 0391 for bodyPart in bodyParts { 0392 let encodedData = try encodeBodyPart(bodyPart) 0393 encoded.appendData(encodedData) 0394 } 0395 0396 return encoded 0397 } 0398 0399 /** 0400 Writes the appended body parts into the given file URL. 0401 0402 This process is facilitated by reading and writing with input and output streams, respectively. Thus, 0403 this approach is very memory efficient and should be used for large body part data. 0404 0405 - parameter fileURL: The file URL to write the multipart form data into. 0406 0407 - throws: An `NSError` if encoding encounters an error. 0408 */ 0409 public func writeEncodedDataToDisk
Upload.swift:317
                    try formData.writeEncodedDataToDisk(fileURL)
(fileURL: NSURL) throws { 0410 if let bodyPartError = bodyPartError { 0411 throw bodyPartError 0412 } 0413 0414 if let path = fileURL.path where NSFileManager.defaultManager().fileExistsAtPath(path) { 0415 let failureReason = "A file already exists at the given file URL: \(fileURL)" 0416 throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 0417 } else if !fileURL.fileURL { 0418 let failureReason = "The URL does not point to a valid file: \(fileURL)" 0419 throw Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason) 0420 } 0421 0422 let outputStream: NSOutputStream 0423 0424 if let possibleOutputStream = NSOutputStream(URL: fileURL, append: false) { 0425 outputStream = possibleOutputStream 0426 } else { 0427 let failureReason = "Failed to create an output stream with the given URL: \(fileURL)" 0428 throw Error.errorWithCode(NSURLErrorCannotOpenFile, failureReason: failureReason) 0429 } 0430 0431 outputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0432 outputStream.open() 0433 0434 self.bodyParts.first?.hasInitialBoundary = true 0435 self.bodyParts.last?.hasFinalBoundary = true 0436 0437 for bodyPart in self.bodyParts { 0438 try writeBodyPart(bodyPart, toOutputStream: outputStream) 0439 } 0440 0441 outputStream.close() 0442 outputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0443 } 0444 0445 // MARK: - Private - Body Part Encoding 0446 0447 private func encodeBodyPart
MultipartFormData.swift:392
            let encodedData = try encodeBodyPart(bodyPart)
(bodyPart: BodyPart) throws -> NSData { 0448 let encoded = NSMutableData() 0449 0450 let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 0451 encoded.appendData(initialData) 0452 0453 let headerData = encodeHeaderDataForBodyPart(bodyPart) 0454 encoded.appendData(headerData) 0455 0456 let bodyStreamData = try encodeBodyStreamDataForBodyPart(bodyPart) 0457 encoded.appendData(bodyStreamData) 0458 0459 if bodyPart.hasFinalBoundary { 0460 encoded.appendData(finalBoundaryData()) 0461 } 0462 0463 return encoded 0464 } 0465 0466 private func encodeHeaderDataForBodyPart
MultipartFormData.swift:453
        let headerData = encodeHeaderDataForBodyPart(bodyPart)
MultipartFormData.swift:534
        let headerData = encodeHeaderDataForBodyPart(bodyPart)
(bodyPart: BodyPart) -> NSData { 0467 var headerText = "" 0468 0469 for (key, value) in bodyPart.headers { 0470 headerText += "\(key): \(value)\(EncodingCharacters.CRLF)" 0471 } 0472 headerText += EncodingCharacters.CRLF 0473 0474 return headerText.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)! 0475 } 0476 0477 private func encodeBodyStreamDataForBodyPart
MultipartFormData.swift:456
        let bodyStreamData = try encodeBodyStreamDataForBodyPart(bodyPart)
(bodyPart: BodyPart) throws -> NSData { 0478 let inputStream = bodyPart.bodyStream 0479 inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0480 inputStream.open() 0481 0482 var error: NSError? 0483 let encoded = NSMutableData() 0484 0485 while inputStream.hasBytesAvailable { 0486 var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0) 0487 let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 0488 0489 if inputStream.streamError != nil { 0490 error = inputStream.streamError 0491 break 0492 } 0493 0494 if bytesRead > 0 { 0495 encoded.appendBytes(buffer, length: bytesRead) 0496 } else if bytesRead < 0 { 0497 let failureReason = "Failed to read from input stream: \(inputStream)" 0498 error = Error.errorWithCode(.InputStreamReadFailed, failureReason: failureReason) 0499 break 0500 } else { 0501 break 0502 } 0503 } 0504 0505 inputStream.close() 0506 inputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0507 0508 if let error = error { 0509 throw error 0510 } 0511 0512 return encoded 0513 } 0514 0515 // MARK: - Private - Writing Body Part to Output Stream 0516 0517 private func writeBodyPart
MultipartFormData.swift:438
            try writeBodyPart(bodyPart, toOutputStream: outputStream)
(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 0518 try writeInitialBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) 0519 try writeHeaderDataForBodyPart(bodyPart, toOutputStream: outputStream) 0520 try writeBodyStreamForBodyPart(bodyPart, toOutputStream: outputStream) 0521 try writeFinalBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream) 0522 } 0523 0524 private func writeInitialBoundaryDataForBodyPart
MultipartFormData.swift:518
        try writeInitialBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream)
( 0525 bodyPart: BodyPart, 0526 toOutputStream outputStream: NSOutputStream) 0527 throws 0528 { 0529 let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData() 0530 return try writeData(initialData, toOutputStream: outputStream) 0531 } 0532 0533 private func writeHeaderDataForBodyPart
MultipartFormData.swift:519
        try writeHeaderDataForBodyPart(bodyPart, toOutputStream: outputStream)
(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 0534 let headerData = encodeHeaderDataForBodyPart(bodyPart) 0535 return try writeData(headerData, toOutputStream: outputStream) 0536 } 0537 0538 private func writeBodyStreamForBodyPart
MultipartFormData.swift:520
        try writeBodyStreamForBodyPart(bodyPart, toOutputStream: outputStream)
(bodyPart: BodyPart, toOutputStream outputStream: NSOutputStream) throws { 0539 let inputStream = bodyPart.bodyStream 0540 inputStream.scheduleInRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0541 inputStream.open() 0542 0543 while inputStream.hasBytesAvailable { 0544 var buffer = [UInt8](count: streamBufferSize, repeatedValue: 0) 0545 let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize) 0546 0547 if let streamError = inputStream.streamError { 0548 throw streamError 0549 } 0550 0551 if bytesRead > 0 { 0552 if buffer.count != bytesRead { 0553 buffer = Array(buffer[0..<bytesRead]) 0554 } 0555 0556 try writeBuffer(&buffer, toOutputStream: outputStream) 0557 } else if bytesRead < 0 { 0558 let failureReason = "Failed to read from input stream: \(inputStream)" 0559 throw Error.errorWithCode(.InputStreamReadFailed, failureReason: failureReason) 0560 } else { 0561 break 0562 } 0563 } 0564 0565 inputStream.close() 0566 inputStream.removeFromRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode) 0567 } 0568 0569 private func writeFinalBoundaryDataForBodyPart
MultipartFormData.swift:521
        try writeFinalBoundaryDataForBodyPart(bodyPart, toOutputStream: outputStream)
( 0570 bodyPart: BodyPart, 0571 toOutputStream outputStream: NSOutputStream) 0572 throws 0573 { 0574 if bodyPart.hasFinalBoundary { 0575 return try writeData(finalBoundaryData(), toOutputStream: outputStream) 0576 } 0577 } 0578 0579 // MARK: - Private - Writing Buffered Data to Output Stream 0580 0581 private func writeData
MultipartFormData.swift:530
        return try writeData(initialData, toOutputStream: outputStream)
MultipartFormData.swift:535
        return try writeData(headerData, toOutputStream: outputStream)
MultipartFormData.swift:575
            return try writeData(finalBoundaryData(), toOutputStream: outputStream)
(data: NSData, toOutputStream outputStream: NSOutputStream) throws { 0582 var buffer = [UInt8](count: data.length, repeatedValue: 0) 0583 data.getBytes(&buffer, length: data.length) 0584 0585 return try writeBuffer(&buffer, toOutputStream: outputStream) 0586 } 0587 0588 private func writeBuffer
MultipartFormData.swift:556
                try writeBuffer(&buffer, toOutputStream: outputStream)
MultipartFormData.swift:585
        return try writeBuffer(&buffer, toOutputStream: outputStream)
(inout buffer: [UInt8], toOutputStream outputStream: NSOutputStream) throws { 0589 var bytesToWrite = buffer.count 0590 0591 while bytesToWrite > 0 { 0592 if outputStream.hasSpaceAvailable { 0593 let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite) 0594 0595 if let streamError = outputStream.streamError { 0596 throw streamError 0597 } 0598 0599 if bytesWritten < 0 { 0600 let failureReason = "Failed to write to output stream: \(outputStream)" 0601 throw Error.errorWithCode(.OutputStreamWriteFailed, failureReason: failureReason) 0602 } 0603 0604 bytesToWrite -= bytesWritten 0605 0606 if bytesToWrite > 0 { 0607 buffer = Array(buffer[bytesWritten..<buffer.count]) 0608 } 0609 } else if let streamError = outputStream.streamError { 0610 throw streamError 0611 } 0612 } 0613 } 0614 0615 // MARK: - Private - Mime Type 0616 0617 private func mimeTypeForPathExtension
MultipartFormData.swift:216
            let mimeType = mimeTypeForPathExtension(pathExtension)
(pathExtension: String) -> String { 0618 if let 0619 id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension, nil)?.takeRetainedValue(), 0620 contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue() 0621 { 0622 return contentType as String 0623 } 0624 0625 return "application/octet-stream" 0626 } 0627 0628 // MARK: - Private - Content Headers 0629 0630 private func contentHeaders
MultipartFormData.swift:142
        let headers = contentHeaders(name: name)
(name name: String) -> [String: String] { 0631 return ["Content-Disposition": "form-data; name=\"\(name)\""] 0632 } 0633 0634 private func contentHeaders
MultipartFormData.swift:164
        let headers = contentHeaders(name: name, mimeType: mimeType)
(name name: String, mimeType: String) -> [String: String] { 0635 return [ 0636 "Content-Disposition": "form-data; name=\"\(name)\"", 0637 "Content-Type": "\(mimeType)" 0638 ] 0639 } 0640 0641 private func contentHeaders
MultipartFormData.swift:187
        let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
MultipartFormData.swift:240
        let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
MultipartFormData.swift:346
        let headers = contentHeaders(name: name, fileName: fileName, mimeType: mimeType)
(name name: String, fileName: String, mimeType: String) -> [String: String] { 0642 return [ 0643 "Content-Disposition": "form-data; name=\"\(name)\"; filename=\"\(fileName)\"", 0644 "Content-Type": "\(mimeType)" 0645 ] 0646 } 0647 0648 // MARK: - Private - Boundary Encoding 0649 0650 private func initialBoundaryData
MultipartFormData.swift:450
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
MultipartFormData.swift:529
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
() -> NSData { 0651 return BoundaryGenerator.boundaryData(boundaryType: .Initial, boundary: boundary) 0652 } 0653 0654 private func encapsulatedBoundaryData
MultipartFormData.swift:450
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
MultipartFormData.swift:529
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
() -> NSData { 0655 return BoundaryGenerator.boundaryData(boundaryType: .Encapsulated, boundary: boundary) 0656 } 0657 0658 private func finalBoundaryData
MultipartFormData.swift:460
            encoded.appendData(finalBoundaryData())
MultipartFormData.swift:575
            return try writeData(finalBoundaryData(), toOutputStream: outputStream)
() -> NSData { 0659 return BoundaryGenerator.boundaryData(boundaryType: .Final, boundary: boundary) 0660 } 0661 0662 // MARK: - Private - Errors 0663 0664 private func setBodyPartError
MultipartFormData.swift:220
            setBodyPartError(Error.errorWithCode(NSURLErrorBadURL, failureReason: failureReason))
MultipartFormData.swift:249
            setBodyPartError(error)
MultipartFormData.swift:265
            setBodyPartError(error)
MultipartFormData.swift:281
            setBodyPartError(error)
MultipartFormData.swift:305
            setBodyPartError(error)
MultipartFormData.swift:316
            setBodyPartError(error)
(error: NSError) { 0665 if bodyPartError == nil { 0666 bodyPartError = error 0667 } 0668 } 0669 } 0670