OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 // This job type handles Chrome plugin network interception. When a plugin | |
6 // wants to intercept a request, a job of this type is created. The intercept | |
7 // job communicates with the plugin to retrieve the response headers and data. | |
8 | |
9 #include "chrome/common/net/url_request_intercept_job.h" | |
10 | |
11 #include <vector> | |
12 | |
13 #include "base/compiler_specific.h" | |
14 #include "base/message_loop.h" | |
15 #include "base/string_util.h" | |
16 #include "chrome/common/chrome_plugin_lib.h" | |
17 #include "chrome/common/notification_source.h" | |
18 #include "net/base/io_buffer.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/base/x509_certificate.h" | |
21 #include "net/http/http_response_headers.h" | |
22 #include "net/url_request/url_request.h" | |
23 | |
24 using base::Time; | |
25 using base::TimeDelta; | |
26 | |
27 // | |
28 // URLRequestInterceptJob | |
29 // | |
30 | |
31 URLRequestInterceptJob::URLRequestInterceptJob(net::URLRequest* request, | |
32 ChromePluginLib* plugin, | |
33 ScopableCPRequest* cprequest) | |
34 : net::URLRequestJob(request), | |
35 cprequest_(cprequest), | |
36 plugin_(plugin), | |
37 read_buffer_(NULL), | |
38 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { | |
39 cprequest_->data = this; // see FromCPRequest(). | |
40 | |
41 registrar_.Add(this, NotificationType::CHROME_PLUGIN_UNLOADED, | |
42 Source<ChromePluginLib>(plugin_)); | |
43 } | |
44 | |
45 URLRequestInterceptJob::~URLRequestInterceptJob() { | |
46 if (plugin_) { | |
47 plugin_->functions().request_funcs->end_request(cprequest_.get(), | |
48 CPERR_SUCCESS); | |
49 } | |
50 } | |
51 | |
52 void URLRequestInterceptJob::DetachPlugin() { | |
53 registrar_.RemoveAll(); | |
54 plugin_ = NULL; | |
55 } | |
56 | |
57 void URLRequestInterceptJob::Start() { | |
58 // Start reading asynchronously so that all error reporting and data | |
59 // callbacks happen as they would for network requests. | |
60 MessageLoop::current()->PostTask( | |
61 FROM_HERE, | |
62 method_factory_.NewRunnableMethod( | |
63 &URLRequestInterceptJob::StartAsync)); | |
64 } | |
65 | |
66 void URLRequestInterceptJob::Kill() { | |
67 if (plugin_) { | |
68 plugin_->functions().request_funcs->end_request(cprequest_.get(), | |
69 CPERR_CANCELLED); | |
70 DetachPlugin(); | |
71 } | |
72 net::URLRequestJob::Kill(); | |
73 method_factory_.RevokeAll(); | |
74 } | |
75 | |
76 bool URLRequestInterceptJob::ReadRawData(net::IOBuffer* dest, int dest_size, | |
77 int* bytes_read) { | |
78 DCHECK_NE(dest_size, 0); | |
79 DCHECK(bytes_read); | |
80 | |
81 if (!plugin_) | |
82 return false; | |
83 | |
84 int rv = plugin_->functions().request_funcs->read(cprequest_.get(), | |
85 dest->data(), dest_size); | |
86 if (rv >= 0) { | |
87 *bytes_read = rv; | |
88 return true; | |
89 } | |
90 | |
91 if (rv == CPERR_IO_PENDING) { | |
92 read_buffer_ = dest; | |
93 read_buffer_size_ = dest_size; | |
94 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0)); | |
95 } else { | |
96 // TODO(mpcomplete): better error code | |
97 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
98 net::ERR_FAILED)); | |
99 } | |
100 | |
101 return false; | |
102 } | |
103 | |
104 bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) const { | |
105 return request_->response_headers()->GetMimeType(mime_type); | |
106 } | |
107 | |
108 bool URLRequestInterceptJob::GetCharset(std::string* charset) { | |
109 return request_->response_headers()->GetCharset(charset); | |
110 } | |
111 | |
112 bool URLRequestInterceptJob::GetContentEncodings( | |
113 std::vector<Filter::FilterType>* encoding_types) { | |
114 DCHECK(encoding_types->empty()); | |
115 if (!request_->response_headers()) | |
116 return false; | |
117 | |
118 std::string encoding_type; | |
119 void* iter = NULL; | |
120 while (request_->response_headers()->EnumerateHeader( | |
121 &iter, "Content-Encoding", &encoding_type)) { | |
122 encoding_types->push_back(Filter::ConvertEncodingToType(encoding_type)); | |
123 } | |
124 | |
125 // Even if encoding types are empty, there is a chance that we need to add | |
126 // some decoding, as some proxies strip encoding completely. In such cases, | |
127 // we may need to add (for example) SDCH filtering (when the context suggests | |
128 // it is appropriate). | |
129 Filter::FixupEncodingTypes(*this, encoding_types); | |
130 return !encoding_types->empty(); | |
131 } | |
132 | |
133 void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) { | |
134 if (!plugin_) | |
135 return; | |
136 | |
137 std::string raw_headers; | |
138 int size = plugin_->functions().request_funcs->get_response_info( | |
139 cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); | |
140 int rv = size < 0 ? size : | |
141 plugin_->functions().request_funcs->get_response_info( | |
142 cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, | |
143 WriteInto(&raw_headers, size+1), size); | |
144 if (rv != CPERR_SUCCESS) { | |
145 // TODO(mpcomplete): what should we do here? | |
146 raw_headers = "HTTP/1.1 404 Not Found"; | |
147 raw_headers.push_back('\0'); | |
148 } | |
149 | |
150 info->headers = new net::HttpResponseHeaders(raw_headers); | |
151 | |
152 if (request_->url().SchemeIsSecure()) { | |
153 // Make up a fake certificate for intercepted data since we don't have | |
154 // access to the real SSL info. | |
155 // TODO(mpcomplete): we should probably have the interception API transmit | |
156 // the SSL info, but the only consumer of this API (Gears) doesn't keep that | |
157 // around. We should change that. | |
158 const char* kCertIssuer = "Chrome Internal"; | |
159 const int kLifetimeDays = 100; | |
160 | |
161 DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL " | |
162 << request_->url(); | |
163 | |
164 info->ssl_info.cert = | |
165 new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), | |
166 kCertIssuer, | |
167 Time::Now(), | |
168 Time::Now() + | |
169 TimeDelta::FromDays(kLifetimeDays)); | |
170 info->ssl_info.cert_status = 0; | |
171 info->ssl_info.security_bits = -1; | |
172 } | |
173 } | |
174 | |
175 int URLRequestInterceptJob::GetResponseCode() const { | |
176 if (!plugin_) | |
177 return -1; | |
178 | |
179 int status = 200; | |
180 plugin_->functions().request_funcs->get_response_info( | |
181 cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status)); | |
182 | |
183 return status; | |
184 } | |
185 | |
186 bool URLRequestInterceptJob::IsRedirectResponse(GURL* location, | |
187 int* http_status_code) { | |
188 if (!request_->response_headers()) | |
189 return false; | |
190 | |
191 std::string value; | |
192 if (!request_->response_headers()->IsRedirect(&value)) | |
193 return false; | |
194 | |
195 *location = request_->url().Resolve(value); | |
196 *http_status_code = request_->response_headers()->response_code(); | |
197 return true; | |
198 } | |
199 | |
200 void URLRequestInterceptJob::StartAsync() { | |
201 // We may have been orphaned... | |
202 if (!request_ || !plugin_) | |
203 return; | |
204 | |
205 int rv = plugin_->functions().request_funcs->start_request(cprequest_.get()); | |
206 if (rv != CPERR_IO_PENDING) | |
207 OnStartCompleted(rv); | |
208 } | |
209 | |
210 void URLRequestInterceptJob::OnStartCompleted(int result) { | |
211 if (result != CPERR_SUCCESS) { | |
212 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
213 net::ERR_CONNECTION_FAILED)); | |
214 return; | |
215 } | |
216 | |
217 NotifyHeadersComplete(); | |
218 } | |
219 | |
220 void URLRequestInterceptJob::OnReadCompleted(int bytes_read) { | |
221 if (bytes_read < 0) { | |
222 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED, | |
223 net::ERR_FAILED)); | |
224 return; | |
225 } | |
226 | |
227 SetStatus(net::URLRequestStatus()); // clear the async flag | |
228 NotifyReadComplete(bytes_read); | |
229 } | |
230 | |
231 void URLRequestInterceptJob::Observe(NotificationType type, | |
232 const NotificationSource& source, | |
233 const NotificationDetails& details) { | |
234 DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED); | |
235 DCHECK(plugin_ == Source<ChromePluginLib>(source).ptr()); | |
236 DetachPlugin(); | |
237 } | |
OLD | NEW |