1 // Copyright (c) 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.
5 #include "xwalk/application/browser/application_protocols.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/string_util.h"
17 #include "base/threading/thread_restrictions.h"
18 #include "base/threading/worker_pool.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/resource_request_info.h"
22 #include "url/url_util.h"
23 #include "net/base/net_errors.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/http/http_response_info.h"
26 #include "net/url_request/url_request_error_job.h"
27 #include "net/url_request/url_request_file_job.h"
28 #include "net/url_request/url_request_simple_job.h"
29 #include "xwalk/application/browser/application_service.h"
30 #include "xwalk/application/common/application_data.h"
31 #include "xwalk/application/common/application_file_util.h"
32 #include "xwalk/application/common/application_manifest_constants.h"
33 #include "xwalk/application/common/application_resource.h"
34 #include "xwalk/application/common/constants.h"
35 #include "xwalk/application/common/manifest_handlers/csp_handler.h"
36 #include "xwalk/application/common/manifest_handlers/main_document_handler.h"
38 using content::BrowserThread;
39 using content::ResourceRequestInfo;
43 namespace keys = application_manifest_keys;
45 namespace application {
49 net::HttpResponseHeaders* BuildHttpHeaders(
50 const std::string& content_security_policy,
51 const std::string& mime_type, const std::string& method,
52 const base::FilePath& file_path, const base::FilePath& relative_path,
53 bool is_authority_match) {
54 std::string raw_headers;
55 if (method == "GET") {
56 if (relative_path.empty())
57 raw_headers.append("HTTP/1.1 400 Bad Request");
58 else if (!is_authority_match)
59 raw_headers.append("HTTP/1.1 403 Forbidden");
60 else if (file_path.empty())
61 raw_headers.append("HTTP/1.1 404 Not Found");
63 raw_headers.append("HTTP/1.1 200 OK");
65 raw_headers.append("HTTP/1.1 501 Not Implemented");
68 if (!content_security_policy.empty()) {
69 raw_headers.append(1, '\0');
70 raw_headers.append("Content-Security-Policy: ");
71 raw_headers.append(content_security_policy);
74 raw_headers.append(1, '\0');
75 raw_headers.append("Access-Control-Allow-Origin: *");
77 if (!mime_type.empty()) {
78 raw_headers.append(1, '\0');
79 raw_headers.append("Content-Type: ");
80 raw_headers.append(mime_type);
83 raw_headers.append(2, '\0');
84 return new net::HttpResponseHeaders(raw_headers);
87 class GeneratedMainDocumentJob: public net::URLRequestSimpleJob {
89 GeneratedMainDocumentJob(
90 net::URLRequest* request,
91 net::NetworkDelegate* network_delegate,
92 const base::FilePath& relative_path,
93 const scoped_refptr<const ApplicationData> application,
94 const std::string& content_security_policy)
95 : net::URLRequestSimpleJob(request, network_delegate),
96 application_(application),
97 mime_type_("text/html"),
98 relative_path_(relative_path),
99 content_security_policy_(content_security_policy) {
102 // Overridden from URLRequestSimpleJob:
103 virtual int GetData(std::string* mime_type,
104 std::string* charset,
106 const net::CompletionCallback& callback) const OVERRIDE {
107 *mime_type = mime_type_;
109 *data = "<!DOCTYPE html>\n<body>\n";
111 MainDocumentInfo* main_info = xwalk::application::ToMainDocumentInfo(
112 application_->GetManifestData(keys::kAppMainKey));
113 const std::vector<std::string>& main_scripts = main_info->GetMainScripts();
114 for (size_t i = 0; i < main_scripts.size(); ++i) {
115 *data += "<script src=\"";
116 *data += main_scripts[i];
117 *data += "\"></script>\n";
122 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
123 response_info_.headers = BuildHttpHeaders(content_security_policy_,
124 mime_type_, "GET", relative_path_,
125 relative_path_, true);
126 *info = response_info_;
130 virtual ~GeneratedMainDocumentJob() {}
132 scoped_refptr<const ApplicationData> application_;
133 const std::string mime_type_;
134 const base::FilePath relative_path_;
135 net::HttpResponseInfo response_info_;
136 std::string content_security_policy_;
139 void ReadResourceFilePath(
140 const ApplicationResource& resource,
141 base::FilePath* file_path) {
142 *file_path = resource.GetFilePath();
145 class URLRequestApplicationJob : public net::URLRequestFileJob {
147 URLRequestApplicationJob(
148 net::URLRequest* request,
149 net::NetworkDelegate* network_delegate,
150 const scoped_refptr<base::TaskRunner>& file_task_runner,
151 const std::string& application_id,
152 const base::FilePath& directory_path,
153 const base::FilePath& relative_path,
154 const std::string& content_security_policy,
155 bool is_authority_match)
156 : net::URLRequestFileJob(
157 request, network_delegate, base::FilePath(), file_task_runner),
158 relative_path_(relative_path),
159 content_security_policy_(content_security_policy),
160 is_authority_match_(is_authority_match),
161 resource_(application_id, directory_path, relative_path),
162 weak_factory_(this) {
165 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
166 std::string mime_type;
167 GetMimeType(&mime_type);
168 std::string method = request()->method();
169 response_info_.headers = BuildHttpHeaders(
170 content_security_policy_, mime_type, method, file_path_,
171 relative_path_, is_authority_match_);
172 *info = response_info_;
175 virtual void Start() OVERRIDE {
176 base::FilePath* read_file_path = new base::FilePath;
178 bool posted = base::WorkerPool::PostTaskAndReply(
180 base::Bind(&ReadResourceFilePath, resource_,
181 base::Unretained(read_file_path)),
182 base::Bind(&URLRequestApplicationJob::OnFilePathRead,
183 weak_factory_.GetWeakPtr(),
184 base::Owned(read_file_path)),
185 true /* task is slow */);
190 virtual ~URLRequestApplicationJob() {}
192 void OnFilePathRead(base::FilePath* read_file_path) {
193 file_path_ = *read_file_path;
194 if (file_path_.empty())
195 NotifyHeadersComplete();
197 URLRequestFileJob::Start();
200 net::HttpResponseInfo response_info_;
201 base::FilePath relative_path_;
202 std::string content_security_policy_;
203 bool is_authority_match_;
204 ApplicationResource resource_;
205 base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
208 // This class is a thread-safe cache of active application's data.
209 // This class is used by ApplicationProtocolHandler as it lives on IO thread
210 // and hence cannot access ApplicationService directly.
211 class ApplicationDataCache : public ApplicationService::Observer {
213 scoped_refptr<ApplicationData> GetApplicationData(
214 const std::string& application_id) const {
215 base::AutoLock lock(lock_);
216 ApplicationData::ApplicationDataMap::const_iterator it =
217 cache_.find(application_id);
218 if (it != cache_.end()) {
224 virtual void DidLaunchApplication(Application* app) OVERRIDE {
225 base::AutoLock lock(lock_);
226 cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
227 app->id(), app->data()));
230 virtual void WillDestroyApplication(Application* app) OVERRIDE {
231 base::AutoLock lock(lock_);
232 cache_.erase(app->id());
236 ApplicationData::ApplicationDataMap cache_;
237 mutable base::Lock lock_;
240 class ApplicationProtocolHandler
241 : public net::URLRequestJobFactory::ProtocolHandler {
243 explicit ApplicationProtocolHandler(ApplicationService* service) {
245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246 // ApplicationProtocolHandler lives longer than ApplicationService,
247 // so we do not need to remove cache_ from ApplicationService
249 service->AddObserver(&cache_);
252 virtual ~ApplicationProtocolHandler() {}
254 virtual net::URLRequestJob* MaybeCreateJob(
255 net::URLRequest* request,
256 net::NetworkDelegate* network_delegate) const OVERRIDE;
259 ApplicationDataCache cache_;
260 DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
264 ApplicationProtocolHandler::MaybeCreateJob(
265 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
266 const std::string& application_id = request->url().host();
267 scoped_refptr<ApplicationData> application =
268 cache_.GetApplicationData(application_id);
269 base::FilePath relative_path =
270 ApplicationURLToRelativeFilePath(request->url());
271 base::FilePath directory_path;
272 std::string content_security_policy;
274 directory_path = application->Path();
276 const char* csp_key = GetCSPKey(application->GetPackageType());
277 const CSPInfo* csp_info = static_cast<CSPInfo*>(
278 application->GetManifestData(csp_key));
280 const std::map<std::string, std::vector<std::string> >& policies =
281 csp_info->GetDirectives();
282 std::map<std::string, std::vector<std::string> >::const_iterator it =
284 for (; it != policies.end(); ++it) {
285 content_security_policy.append(
286 it->first + ' ' + JoinString(it->second, ' ') + ';');
291 const std::string& path = request->url().path();
294 path.substr(1) == kGeneratedMainDocumentFilename) {
295 return new GeneratedMainDocumentJob(request, network_delegate,
296 relative_path, application,
297 content_security_policy);
300 return new URLRequestApplicationJob(
303 content::BrowserThread::GetBlockingPool()->
304 GetTaskRunnerWithShutdownBehavior(
305 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
309 content_security_policy,
315 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
316 CreateApplicationProtocolHandler(ApplicationService* service) {
317 return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
318 new ApplicationProtocolHandler(service));
321 } // namespace application