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"
14 #include "base/files/file_path.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/string_util.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/threading/worker_pool.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/resource_request_info.h"
23 #include "url/url_util.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_response_headers.h"
26 #include "net/http/http_response_info.h"
27 #include "net/url_request/url_request_error_job.h"
28 #include "net/url_request/url_request_file_job.h"
29 #include "net/url_request/url_request_simple_job.h"
30 #include "xwalk/application/browser/application_service.h"
31 #include "xwalk/application/common/application_data.h"
32 #include "xwalk/application/common/application_file_util.h"
33 #include "xwalk/application/common/application_manifest_constants.h"
34 #include "xwalk/application/common/application_resource.h"
35 #include "xwalk/application/common/constants.h"
36 #include "xwalk/application/common/manifest_handlers/csp_handler.h"
37 #include "xwalk/application/common/manifest_handlers/main_document_handler.h"
39 using content::BrowserThread;
40 using content::ResourceRequestInfo;
44 namespace keys = application_manifest_keys;
46 namespace application {
50 net::HttpResponseHeaders* BuildHttpHeaders(
51 const std::string& content_security_policy,
52 const std::string& mime_type, const std::string& method,
53 const base::FilePath& file_path, const base::FilePath& relative_path,
54 bool is_authority_match) {
55 std::string raw_headers;
56 if (method == "GET") {
57 if (relative_path.empty())
58 raw_headers.append("HTTP/1.1 400 Bad Request");
59 else if (!is_authority_match)
60 raw_headers.append("HTTP/1.1 403 Forbidden");
61 else if (file_path.empty())
62 raw_headers.append("HTTP/1.1 404 Not Found");
64 raw_headers.append("HTTP/1.1 200 OK");
66 raw_headers.append("HTTP/1.1 501 Not Implemented");
69 if (!content_security_policy.empty()) {
70 raw_headers.append(1, '\0');
71 raw_headers.append("Content-Security-Policy: ");
72 raw_headers.append(content_security_policy);
75 raw_headers.append(1, '\0');
76 raw_headers.append("Access-Control-Allow-Origin: *");
78 if (!mime_type.empty()) {
79 raw_headers.append(1, '\0');
80 raw_headers.append("Content-Type: ");
81 raw_headers.append(mime_type);
84 raw_headers.append(2, '\0');
85 return new net::HttpResponseHeaders(raw_headers);
88 class GeneratedMainDocumentJob: public net::URLRequestSimpleJob {
90 GeneratedMainDocumentJob(
91 net::URLRequest* request,
92 net::NetworkDelegate* network_delegate,
93 const base::FilePath& relative_path,
94 const scoped_refptr<const ApplicationData> application,
95 const std::string& content_security_policy)
96 : net::URLRequestSimpleJob(request, network_delegate),
97 application_(application),
98 mime_type_("text/html"),
99 relative_path_(relative_path),
100 content_security_policy_(content_security_policy) {
103 // Overridden from URLRequestSimpleJob:
104 virtual int GetData(std::string* mime_type,
105 std::string* charset,
107 const net::CompletionCallback& callback) const OVERRIDE {
108 *mime_type = mime_type_;
110 *data = "<!DOCTYPE html>\n<body>\n";
112 MainDocumentInfo* main_info = xwalk::application::ToMainDocumentInfo(
113 application_->GetManifestData(keys::kAppMainKey));
114 const std::vector<std::string>& main_scripts = main_info->GetMainScripts();
115 for (size_t i = 0; i < main_scripts.size(); ++i) {
116 *data += "<script src=\"";
117 *data += main_scripts[i];
118 *data += "\"></script>\n";
123 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
124 response_info_.headers = BuildHttpHeaders(content_security_policy_,
125 mime_type_, "GET", relative_path_,
126 relative_path_, true);
127 *info = response_info_;
131 virtual ~GeneratedMainDocumentJob() {}
133 scoped_refptr<const ApplicationData> application_;
134 const std::string mime_type_;
135 const base::FilePath relative_path_;
136 net::HttpResponseInfo response_info_;
137 std::string content_security_policy_;
140 void ReadResourceFilePath(
141 const ApplicationResource& resource,
142 base::FilePath* file_path) {
143 *file_path = resource.GetFilePath();
146 class URLRequestApplicationJob : public net::URLRequestFileJob {
148 URLRequestApplicationJob(
149 net::URLRequest* request,
150 net::NetworkDelegate* network_delegate,
151 const scoped_refptr<base::TaskRunner>& file_task_runner,
152 const std::string& application_id,
153 const base::FilePath& directory_path,
154 const base::FilePath& relative_path,
155 const std::string& content_security_policy,
156 const std::list<std::string>& locales,
157 bool is_authority_match)
158 : net::URLRequestFileJob(
159 request, network_delegate, base::FilePath(), file_task_runner),
160 relative_path_(relative_path),
161 content_security_policy_(content_security_policy),
162 is_authority_match_(is_authority_match),
163 resource_(application_id, directory_path, relative_path),
165 weak_factory_(this) {
168 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
169 std::string mime_type;
170 GetMimeType(&mime_type);
171 std::string method = request()->method();
172 response_info_.headers = BuildHttpHeaders(
173 content_security_policy_, mime_type, method, file_path_,
174 relative_path_, is_authority_match_);
175 *info = response_info_;
178 virtual void Start() OVERRIDE {
179 base::FilePath* read_file_path = new base::FilePath;
181 resource_.SetLocales(locales_);
182 bool posted = base::WorkerPool::PostTaskAndReply(
184 base::Bind(&ReadResourceFilePath, resource_,
185 base::Unretained(read_file_path)),
186 base::Bind(&URLRequestApplicationJob::OnFilePathRead,
187 weak_factory_.GetWeakPtr(),
188 base::Owned(read_file_path)),
189 true /* task is slow */);
194 virtual ~URLRequestApplicationJob() {}
196 void OnFilePathRead(base::FilePath* read_file_path) {
197 file_path_ = *read_file_path;
198 if (file_path_.empty())
199 NotifyHeadersComplete();
201 URLRequestFileJob::Start();
204 net::HttpResponseInfo response_info_;
205 base::FilePath relative_path_;
206 std::string content_security_policy_;
207 bool is_authority_match_;
208 ApplicationResource resource_;
209 std::list<std::string> locales_;
210 base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
213 // This class is a thread-safe cache of active application's data.
214 // This class is used by ApplicationProtocolHandler as it lives on IO thread
215 // and hence cannot access ApplicationService directly.
216 class ApplicationDataCache : public ApplicationService::Observer {
218 scoped_refptr<ApplicationData> GetApplicationData(
219 const std::string& application_id) const {
220 base::AutoLock lock(lock_);
221 ApplicationData::ApplicationDataMap::const_iterator it =
222 cache_.find(application_id);
223 if (it != cache_.end()) {
229 virtual void DidLaunchApplication(Application* app) OVERRIDE {
230 base::AutoLock lock(lock_);
231 cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
232 app->id(), app->data()));
235 virtual void WillDestroyApplication(Application* app) OVERRIDE {
236 base::AutoLock lock(lock_);
237 cache_.erase(app->id());
241 ApplicationData::ApplicationDataMap cache_;
242 mutable base::Lock lock_;
245 class ApplicationProtocolHandler
246 : public net::URLRequestJobFactory::ProtocolHandler {
248 explicit ApplicationProtocolHandler(ApplicationService* service) {
250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
251 // ApplicationProtocolHandler lives longer than ApplicationService,
252 // so we do not need to remove cache_ from ApplicationService
254 service->AddObserver(&cache_);
257 virtual ~ApplicationProtocolHandler() {}
259 virtual net::URLRequestJob* MaybeCreateJob(
260 net::URLRequest* request,
261 net::NetworkDelegate* network_delegate) const OVERRIDE;
264 ApplicationDataCache cache_;
265 DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
269 ApplicationProtocolHandler::MaybeCreateJob(
270 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
271 const std::string& application_id = request->url().host();
272 scoped_refptr<ApplicationData> application =
273 cache_.GetApplicationData(application_id);
274 base::FilePath relative_path =
275 ApplicationURLToRelativeFilePath(request->url());
276 base::FilePath directory_path;
277 std::string content_security_policy;
279 directory_path = application->Path();
281 const char* csp_key = GetCSPKey(application->GetPackageType());
282 const CSPInfo* csp_info = static_cast<CSPInfo*>(
283 application->GetManifestData(csp_key));
285 const std::map<std::string, std::vector<std::string> >& policies =
286 csp_info->GetDirectives();
287 std::map<std::string, std::vector<std::string> >::const_iterator it =
289 for (; it != policies.end(); ++it) {
290 content_security_policy.append(
291 it->first + ' ' + JoinString(it->second, ' ') + ';');
296 const std::string& path = request->url().path();
299 path.substr(1) == kGeneratedMainDocumentFilename) {
300 return new GeneratedMainDocumentJob(request, network_delegate,
301 relative_path, application,
302 content_security_policy);
305 std::list<std::string> locales;
306 // FIXME(Xinchao): Get the user agent locales into |locales|.
307 return new URLRequestApplicationJob(
310 content::BrowserThread::GetBlockingPool()->
311 GetTaskRunnerWithShutdownBehavior(
312 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
316 content_security_policy,
323 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
324 CreateApplicationProtocolHandler(ApplicationService* service) {
325 return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
326 new ApplicationProtocolHandler(service));
329 } // namespace application