0001    // Validation.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    extension Request {
0026    
0027        /**
0028            Used to represent whether validation was successful or encountered an error resulting in a failure.
0029    
0030            - Success: The validation was successful.
0031            - Failure: The validation failed encountering the provided error.
0032        */
0033        public enum ValidationResult
Validation.swift:42
    public typealias Validation = (NSURLRequest?, NSHTTPURLResponse) -> ValidationResult
{ 0034 case Success
Validation.swift:80
                return .Success
Validation.swift:133
            guard let validData = self.delegate.data where validData.length > 0 else { return .Success }
Validation.swift:141
                        return .Success
Validation.swift:147
                        return .Success
0035 case Failure
Validation.swift:57
                case let .Failure(error) = validation(self.request, response)
Validation.swift:83
                return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason))
Validation.swift:163
            return .Failure(Error.errorWithCode(.ContentTypeValidationFailed, failureReason: failureReason))
(NSError) 0036 } 0037 0038 /** 0039 A closure used to validate a request that takes a URL request and URL response, and returns whether the 0040 request was valid. 0041 */ 0042 public typealias Validation
Validation.swift:53
    public func validate(validation: Validation) -> Self {
= (NSURLRequest?, NSHTTPURLResponse) -> ValidationResult 0043 0044 /** 0045 Validates the request, using the specified closure. 0046 0047 If validation fails, subsequent calls to response handlers will have an associated error. 0048 0049 - parameter validation: A closure to validate the request. 0050 0051 - returns: The request. 0052 */ 0053 public func validate
Validation.swift:78
        return validate { _, response in
Validation.swift:132
        return validate { _, response in
(validation: Validation) -> Self { 0054 delegate.queue.addOperationWithBlock { 0055 if let 0056 response = self.response where self.delegate.error == nil, 0057 case let .Failure(error) = validation(self.request, response) 0058 { 0059 self.delegate.error = error 0060 } 0061 } 0062 0063 return self 0064 } 0065 0066 // MARK: - Status Code 0067 0068 /** 0069 Validates that the response has a status code in the specified range. 0070 0071 If validation fails, subsequent calls to response handlers will have an associated error. 0072 0073 - parameter range: The range of acceptable status codes. 0074 0075 - returns: The request. 0076 */ 0077 public func validate
Validation.swift:187
        return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
<S: SequenceType where S.Generator.Element == Int>(statusCode acceptableStatusCode: S) -> Self { 0078 return validate { _, response in 0079 if acceptableStatusCode.contains(response.statusCode) { 0080 return .Success 0081 } else { 0082 let failureReason = "Response status code was unacceptable: \(response.statusCode)" 0083 return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason)) 0084 } 0085 } 0086 } 0087 0088 // MARK: - Content-Type 0089 0090 private struct MIMEType
Validation.swift:112
        func matches(MIME: MIMEType) -> Bool {
Validation.swift:137
                responseMIMEType = MIMEType(responseContentType)
Validation.swift:140
                    if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) {
Validation.swift:146
                    if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" {
{ 0091 let type
Validation.swift:105
                self.type = type
Validation.swift:113
            switch (type, subtype) {
Validation.swift:114
            case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"):
Validation.swift:114
            case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"):
Validation.swift:146
                    if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" {
: String 0092 let subtype
Validation.swift:106
                self.subtype = subtype
Validation.swift:113
            switch (type, subtype) {
Validation.swift:114
            case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"):
Validation.swift:114
            case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"):
Validation.swift:146
                    if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" {
: String 0093 0094 init
Validation.swift:137
                responseMIMEType = MIMEType(responseContentType)
Validation.swift:140
                    if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) {
Validation.swift:146
                    if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" {
?(_ string: String) { 0095 let components: [String] = { 0096 let stripped = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) 0097 let split = stripped.substringToIndex(stripped.rangeOfString(";")?.startIndex ?? stripped.endIndex) 0098 return split.componentsSeparatedByString("/") 0099 }() 0100 0101 if let 0102 type = components.first, 0103 subtype = components.last 0104 { 0105 self.type = type 0106 self.subtype = subtype 0107 } else { 0108 return nil 0109 } 0110 } 0111 0112 func matches
Validation.swift:140
                    if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) {
(MIME: MIMEType) -> Bool { 0113 switch (type, subtype) { 0114 case (MIME.type, MIME.subtype), (MIME.type, "*"), ("*", MIME.subtype), ("*", "*"): 0115 return true 0116 default: 0117 return false 0118 } 0119 } 0120 } 0121 0122 /** 0123 Validates that the response has a content type in the specified array. 0124 0125 If validation fails, subsequent calls to response handlers will have an associated error. 0126 0127 - parameter contentType: The acceptable content types, which may specify wildcard types and/or subtypes. 0128 0129 - returns: The request. 0130 */ 0131 public func validate
Validation.swift:187
        return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes)
<S : SequenceType where S.Generator.Element == String>(contentType acceptableContentTypes: S) -> Self { 0132 return validate { _, response in 0133 guard let validData = self.delegate.data where validData.length > 0 else { return .Success } 0134 0135 if let 0136 responseContentType = response.MIMEType, 0137 responseMIMEType = MIMEType(responseContentType) 0138 { 0139 for contentType in acceptableContentTypes { 0140 if let acceptableMIMEType = MIMEType(contentType) where acceptableMIMEType.matches(responseMIMEType) { 0141 return .Success 0142 } 0143 } 0144 } else { 0145 for contentType in acceptableContentTypes { 0146 if let MIMEType = MIMEType(contentType) where MIMEType.type == "*" && MIMEType.subtype == "*" { 0147 return .Success 0148 } 0149 } 0150 } 0151 0152 let failureReason: String 0153 0154 if let responseContentType = response.MIMEType { 0155 failureReason = ( 0156 "Response content type \"\(responseContentType)\" does not match any acceptable " + 0157 "content types: \(acceptableContentTypes)" 0158 ) 0159 } else { 0160 failureReason = "Response content type was missing and acceptable content type does not match \"*/*\"" 0161 } 0162 0163 return .Failure(Error.errorWithCode(.ContentTypeValidationFailed, failureReason: failureReason)) 0164 } 0165 } 0166 0167 // MARK: - Automatic 0168 0169 /** 0170 Validates that the response has a status code in the default acceptable range of 200...299, and that the content 0171 type matches any specified in the Accept HTTP header field. 0172 0173 If validation fails, subsequent calls to response handlers will have an associated error. 0174 0175 - returns: The request. 0176 */ 0177 public func validate() -> Self { 0178 let acceptableStatusCodes: Range<Int> = 200..<300 0179 let acceptableContentTypes: [String] = { 0180 if let accept = request?.valueForHTTPHeaderField("Accept") { 0181 return accept.componentsSeparatedByString(",") 0182 } 0183 0184 return ["*/*"] 0185 }() 0186 0187 return validate(statusCode: acceptableStatusCodes).validate(contentType: acceptableContentTypes) 0188 } 0189 } 0190