Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(792)

Side by Side Diff: ios/net/protocol_handler_util.mm

Issue 994823004: [iOS] Upstream //ios/net (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "ios/net/protocol_handler_util.h"
6
7 #include <string>
8
9 #include "base/base64.h"
10 #include "base/i18n/icu_encoding_detection.h"
11 #include "base/i18n/icu_string_conversions.h"
12 #include "base/logging.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/macros.h"
15 #include "base/strings/sys_string_conversions.h"
16 #include "base/time/time.h"
17 #include "ios/net/crn_http_url_response.h"
18 #import "net/base/mac/url_conversions.h"
19 #include "net/base/net_errors.h"
20 #include "net/http/http_request_headers.h"
21 #include "net/http/http_response_headers.h"
22 #include "net/http/http_version.h"
23 #include "net/url_request/url_request.h"
24 #include "url/gurl.h"
25
26 namespace {
27
28 // "Content-Type" HTTP header.
29 NSString* const kContentType = @"Content-Type";
30
31 } // namespace
32
33 namespace net {
34
35 NSError* GetIOSError(NSInteger ns_error_code,
36 int net_error_code,
37 NSString* url,
38 const base::Time& creation_time) {
39 // The error we pass through has the domain NSURLErrorDomain, an IOS error
40 // code, and a userInfo dictionary in which we smuggle more detailed info
41 // about the error from our network stack. This dictionary contains the
42 // failing URL, and a nested error in which we deposit the original error code
43 // passed in from the Chrome network stack.
44 // The nested error has domain:kErrorDomain, code:|original_error_code|, and
45 // userInfo:nil; this NSError is keyed in the dictionary with
46 // NSUnderlyingErrorKey.
47 NSDate* creation_date = [NSDate
48 dateWithTimeIntervalSinceReferenceDate:creation_time.ToCFAbsoluteTime()];
49 DCHECK(creation_date);
50 NSError* underlying_error =
51 [NSError errorWithDomain:base::SysUTF8ToNSString(kErrorDomain)
52 code:net_error_code
53 userInfo:nil];
54 DCHECK(url);
55 NSDictionary* dictionary = @{
56 NSURLErrorFailingURLStringErrorKey : url,
57 @"CreationDate" : creation_date,
58 NSUnderlyingErrorKey : underlying_error,
59 };
60 return [NSError errorWithDomain:NSURLErrorDomain
61 code:ns_error_code
62 userInfo:dictionary];
63 }
64
65 NSURLResponse* GetNSURLResponseForRequest(URLRequest* request) {
66 NSURL* url = NSURLWithGURL(request->url());
67 DCHECK(url);
68
69 // The default iOS stack returns a NSURLResponse when the request has a data
70 // scheme, and a NSHTTPURLResponse otherwise.
71 if (request->url().SchemeIs("data")) {
72 std::string mt;
73 request->GetMimeType(&mt);
74 NSString* mime_type = base::SysUTF8ToNSString(mt);
75 DCHECK(mime_type);
76 std::string cs;
77 request->GetCharset(&cs);
78 NSString* charset = base::SysUTF8ToNSString(cs);
79 DCHECK(charset);
80 // The default iOS stack computes the length of the decoded string. If we
81 // wanted to do that we would have to decode the string now. However, using
82 // the unknown length (-1) seems to be working.
83 return [[[NSURLResponse alloc] initWithURL:url
84 MIMEType:mime_type
85 expectedContentLength:-1
86 textEncodingName:charset] autorelease];
87 } else {
88 // Iterate over all the headers and copy them.
89 bool has_content_type_header = false;
90 NSMutableDictionary* header_fields = [NSMutableDictionary dictionary];
91 HttpResponseHeaders* headers = request->response_headers();
92 if (headers != nullptr) {
93 void* iter = nullptr;
94 std::string name, value;
95 while (headers->EnumerateHeaderLines(&iter, &name, &value)) {
96 NSString* key = base::SysUTF8ToNSString(name);
97 if (!key) {
98 DLOG(ERROR) << "Header name is not in UTF8: " << name;
99 // Skip the invalid header.
100 continue;
101 }
102 // Do not copy "Cache-Control" headers as we provide our own controls.
103 if ([key caseInsensitiveCompare:@"cache-control"] == NSOrderedSame)
104 continue;
105 if ([key caseInsensitiveCompare:kContentType] == NSOrderedSame) {
106 key = kContentType;
107 has_content_type_header = true;
108 }
109
110 // Handle bad encoding.
111 NSString* v = base::SysUTF8ToNSString(value);
112 if (!v) {
113 DLOG(ERROR) << "Header \"" << name << "\" is not in UTF8: " << value;
114 // Infer the encoding, or skip the header if it's not possible.
115 std::string encoding;
116 if (!base::DetectEncoding(value, &encoding))
117 continue;
118 std::string value_utf8;
119 if (!base::ConvertToUtf8AndNormalize(value, encoding, &value_utf8))
120 continue;
121 v = base::SysUTF8ToNSString(value_utf8);
122 DCHECK(v);
123 }
124
125 // Duplicate keys are appended using a comma separator (RFC 2616).
126 NSMutableString* existing = [header_fields objectForKey:key];
127 if (existing) {
128 [existing appendFormat:@",%@", v];
129 } else {
130 [header_fields setObject:[NSMutableString stringWithString:v]
131 forKey:key];
132 }
133 }
134 }
135
136 // WebUI does not define a "Content-Type" header. Use the MIME type instead.
137 if (!has_content_type_header) {
138 std::string mime_type = "";
139 request->GetMimeType(&mime_type);
140 NSString* type = base::SysUTF8ToNSString(mime_type);
141 if ([type length])
142 [header_fields setObject:type forKey:kContentType];
143 }
144 NSString* content_type = [header_fields objectForKey:kContentType];
145 if (content_type) {
146 NSRange range = [content_type rangeOfString:@","];
147 // If there are several "Content-Type" headers, keep only the first one.
148 if (range.location != NSNotFound) {
149 [header_fields setObject:[content_type substringToIndex:range.location]
150 forKey:kContentType];
151 }
152 }
153
154 // Use a "no-store" cache control to ensure that the response is not cached
155 // by the system. See b/7045043.
156 [header_fields setObject:@"no-store" forKey:@"Cache-Control"];
157
158 // Parse the HTTP version.
159 NSString* version_string = @"HTTP/1.1";
160 if (headers) {
161 const HttpVersion& http_version = headers->GetHttpVersion();
162 version_string = [NSString stringWithFormat:@"HTTP/%hu.%hu",
163 http_version.major_value(),
164 http_version.minor_value()];
165 }
166
167 return [[[CRNHTTPURLResponse alloc] initWithURL:url
168 statusCode:request->GetResponseCode()
169 HTTPVersion:version_string
170 headerFields:header_fields] autorelease];
171 }
172 }
173
174 void CopyHttpHeaders(NSURLRequest* in_request, URLRequest* out_request) {
175 DCHECK(out_request->extra_request_headers().IsEmpty());
176 NSDictionary* headers = [in_request allHTTPHeaderFields];
177 HttpRequestHeaders net_headers;
178 NSString* key;
179 for (key in headers) {
180 if ([key isEqualToString:@"Referer"]) {
181 // The referrer must be set through the set_referrer method rather than as
182 // a header.
183 out_request->SetReferrer(
184 base::SysNSStringToUTF8([headers objectForKey:key]));
185 // If the referrer is explicitly set, we don't want the network stack to
186 // strip it.
187 out_request->set_referrer_policy(URLRequest::NEVER_CLEAR_REFERRER);
188 continue;
189 }
190 if (![key isEqualToString:@"User-Agent"]) {
191 // The user agent string is added by the network stack, and might be
192 // different from the one provided by UIWebView. Do not copy it.
193 NSString* value = [headers objectForKey:key];
194 net_headers.SetHeader(base::SysNSStringToUTF8(key),
195 base::SysNSStringToUTF8(value));
196 }
197 }
198 // Set default values for some missing headers.
199 // The "Accept" header is defined by Webkit on the desktop version.
200 net_headers.SetHeaderIfMissing("Accept", "*/*");
201 // The custom NSURLProtocol example from Apple adds a default "Content-Type"
202 // header for non-empty POST requests. This suggests that this header can be
203 // missing, and Chrome network stack does not add it by itself.
204 if (out_request->has_upload() && out_request->method() == "POST") {
205 DLOG_IF(WARNING, !net_headers.HasHeader(HttpRequestHeaders::kContentType))
206 << "Missing \"Content-Type\" header in POST request.";
207 net_headers.SetHeaderIfMissing(HttpRequestHeaders::kContentType,
208 "application/x-www-form-urlencoded");
209 }
210 out_request->SetExtraRequestHeaders(net_headers);
211 }
212
213 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698