2 * Copyright (C) 2006, 2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #import "ResourceResponse.h"
31 #import "CFNetworkSPI.h"
32 #import "HTTPParsers.h"
33 #import "WebCoreURLResponse.h"
34 #import <Foundation/Foundation.h>
36 #import <wtf/StdLibExtras.h>
40 void ResourceResponse::initNSURLResponse() const
42 if (!m_httpStatusCode || !m_url.protocolIsInHTTPFamily()) {
43 // Work around a mistake in the NSURLResponse class - <rdar://problem/6875219>.
44 // The init function takes an NSInteger, even though the accessor returns a long long.
45 // For values that won't fit in an NSInteger, pass -1 instead.
46 NSInteger expectedContentLength;
47 if (m_expectedContentLength < 0 || m_expectedContentLength > std::numeric_limits<NSInteger>::max())
48 expectedContentLength = -1;
50 expectedContentLength = static_cast<NSInteger>(m_expectedContentLength);
52 NSString* encodingNSString = nsStringNilIfEmpty(m_textEncodingName);
53 m_nsResponse = adoptNS([[NSURLResponse alloc] initWithURL:m_url MIMEType:m_mimeType expectedContentLength:expectedContentLength textEncodingName:encodingNSString]);
57 // FIXME: We lose the status text and the HTTP version here.
58 NSMutableDictionary* headerDictionary = [NSMutableDictionary dictionary];
59 for (auto& header : m_httpHeaderFields)
60 [headerDictionary setObject:(NSString *)header.value forKey:(NSString *)header.key];
62 m_nsResponse = adoptNS([[NSHTTPURLResponse alloc] initWithURL:m_url statusCode:m_httpStatusCode HTTPVersion:(NSString*)kCFHTTPVersion1_1 headerFields:headerDictionary]);
64 // Mime type sniffing doesn't work with a synthesized response.
65 [m_nsResponse.get() _setMIMEType:(NSString *)m_mimeType];
68 CertificateInfo ResourceResponse::platformCertificateInfo() const
72 CFURLResponseRef cfResponse = m_cfResponse.get();
75 CFURLResponseRef cfResponse = [m_nsResponse _CFURLResponse];
81 CFDictionaryRef context = _CFURLResponseGetSSLCertificateContext(cfResponse);
85 auto trustValue = CFDictionaryGetValue(context, kCFStreamPropertySSLPeerTrust);
88 ASSERT(CFGetTypeID(trustValue) == SecTrustGetTypeID());
89 auto trust = (SecTrustRef)trustValue;
91 SecTrustResultType trustResultType;
92 OSStatus result = SecTrustGetTrustResult(trust, &trustResultType);
93 if (result != errSecSuccess)
96 if (trustResultType == kSecTrustResultInvalid) {
97 result = SecTrustEvaluate(trust, &trustResultType);
98 if (result != errSecSuccess)
102 CFIndex count = SecTrustGetCertificateCount(trust);
103 auto certificateChain = CFArrayCreateMutable(0, count, &kCFTypeArrayCallBacks);
104 for (CFIndex i = 0; i < count; i++)
105 CFArrayAppendValue(certificateChain, SecTrustGetCertificateAtIndex(trust, i));
107 return CertificateInfo(adoptCF(certificateChain));
112 NSURLResponse *ResourceResponse::nsURLResponse() const
114 if (!m_nsResponse && !m_cfResponse && !m_isNull) {
116 m_cfResponse = [m_nsResponse.get() _CFURLResponse];
117 return m_nsResponse.get();
124 m_nsResponse = [NSURLResponse _responseWithCFURLResponse:m_cfResponse.get()];
126 return m_nsResponse.get();
129 ResourceResponse::ResourceResponse(NSURLResponse* nsResponse)
130 : m_initLevel(Uninitialized)
131 , m_platformResponseIsUpToDate(true)
132 , m_cfResponse([nsResponse _CFURLResponse])
133 , m_nsResponse(nsResponse)
135 m_isNull = !nsResponse;
140 static NSString* const commonHeaderFields[] = {
141 @"Age", @"Cache-Control", @"Content-Type", @"Date", @"Etag", @"Expires", @"Last-Modified", @"Pragma"
144 NSURLResponse *ResourceResponse::nsURLResponse() const
146 if (!m_nsResponse && !m_isNull)
148 return m_nsResponse.get();
151 static NSString *copyNSURLResponseStatusLine(NSURLResponse *response)
153 CFURLResponseRef cfResponse = [response _CFURLResponse];
157 CFHTTPMessageRef cfHTTPMessage = CFURLResponseGetHTTPResponse(cfResponse);
161 return (NSString *)CFHTTPMessageCopyResponseStatusLine(cfHTTPMessage);
164 void ResourceResponse::platformLazyInit(InitLevel initLevel)
166 if (m_initLevel >= initLevel)
169 if (m_isNull || !m_nsResponse)
172 if (m_initLevel < CommonFieldsOnly && initLevel >= CommonFieldsOnly) {
173 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
175 m_httpHeaderFields.clear();
176 m_url = [m_nsResponse.get() URL];
177 m_mimeType = [m_nsResponse.get() MIMEType];
178 m_expectedContentLength = [m_nsResponse.get() expectedContentLength];
179 m_textEncodingName = [m_nsResponse.get() textEncodingName];
181 // Workaround for <rdar://problem/8757088>, can be removed once that is fixed.
182 unsigned textEncodingNameLength = m_textEncodingName.length();
183 if (textEncodingNameLength >= 2 && m_textEncodingName[0U] == '"' && m_textEncodingName[textEncodingNameLength - 1] == '"')
184 m_textEncodingName = m_textEncodingName.string().substring(1, textEncodingNameLength - 2);
186 if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
187 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
189 CFHTTPMessageRef messageRef = CFURLResponseGetHTTPResponse([httpResponse _CFURLResponse]);
190 RetainPtr<CFStringRef> messageString = adoptCF(CFHTTPMessageCopyVersion(messageRef));
191 m_httpVersion = String(messageString.get()).upper();
192 m_httpStatusCode = [httpResponse statusCode];
194 if (initLevel < AllFields) {
195 NSDictionary *headers = [httpResponse allHeaderFields];
196 for (NSString *name : commonHeaderFields) {
197 if (NSString* headerValue = [headers objectForKey:name])
198 m_httpHeaderFields.set(name, headerValue);
202 m_httpStatusCode = 0;
207 if (m_initLevel < AllFields && initLevel == AllFields) {
208 if ([m_nsResponse.get() isKindOfClass:[NSHTTPURLResponse class]]) {
209 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
211 NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)m_nsResponse.get();
212 if (RetainPtr<NSString> httpStatusLine = adoptNS(copyNSURLResponseStatusLine(httpResponse)))
213 m_httpStatusText = extractReasonPhraseFromHTTPStatusLine(httpStatusLine.get());
215 m_httpStatusText = AtomicString("OK", AtomicString::ConstructFromLiteral);
217 NSDictionary *headers = [httpResponse allHeaderFields];
218 for (NSString *name in headers)
219 m_httpHeaderFields.set(name, [headers objectForKey:name]);
225 m_initLevel = initLevel;
228 String ResourceResponse::platformSuggestedFilename() const
230 return [nsURLResponse() suggestedFilename];
233 bool ResourceResponse::platformCompare(const ResourceResponse& a, const ResourceResponse& b)
235 return a.nsURLResponse() == b.nsURLResponse();
238 #endif // USE(CFNETWORK)
240 } // namespace WebCore
242 #endif // PLATFORM(COCOA)