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/runtime/browser/xwalk_runner.h"
31 #include "xwalk/application/browser/application_service.h"
32 #include "xwalk/application/common/application_data.h"
33 #include "xwalk/application/common/application_file_util.h"
34 #include "xwalk/application/common/application_manifest_constants.h"
35 #include "xwalk/application/common/application_resource.h"
36 #include "xwalk/application/common/constants.h"
37 #include "xwalk/application/common/manifest_handlers/csp_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 void ReadResourceFilePath(
89 const ApplicationResource& resource,
90 base::FilePath* file_path) {
91 *file_path = resource.GetFilePath();
94 class URLRequestApplicationJob : public net::URLRequestFileJob {
96 URLRequestApplicationJob(
97 net::URLRequest* request,
98 net::NetworkDelegate* network_delegate,
99 const scoped_refptr<base::TaskRunner>& file_task_runner,
100 const std::string& application_id,
101 const base::FilePath& directory_path,
102 const base::FilePath& relative_path,
103 const std::string& content_security_policy,
104 const std::list<std::string>& locales,
105 bool is_authority_match)
106 : net::URLRequestFileJob(
107 request, network_delegate, base::FilePath(), file_task_runner),
108 relative_path_(relative_path),
109 content_security_policy_(content_security_policy),
110 is_authority_match_(is_authority_match),
111 resource_(application_id, directory_path, relative_path),
113 weak_factory_(this) {
116 virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
117 std::string mime_type;
118 GetMimeType(&mime_type);
119 std::string method = request()->method();
120 response_info_.headers = BuildHttpHeaders(
121 content_security_policy_, mime_type, method, file_path_,
122 relative_path_, is_authority_match_);
123 *info = response_info_;
126 virtual void Start() OVERRIDE {
127 base::FilePath* read_file_path = new base::FilePath;
129 resource_.SetLocales(locales_);
130 bool posted = base::WorkerPool::PostTaskAndReply(
132 base::Bind(&ReadResourceFilePath, resource_,
133 base::Unretained(read_file_path)),
134 base::Bind(&URLRequestApplicationJob::OnFilePathRead,
135 weak_factory_.GetWeakPtr(),
136 base::Owned(read_file_path)),
137 true /* task is slow */);
142 virtual ~URLRequestApplicationJob() {}
144 void OnFilePathRead(base::FilePath* read_file_path) {
145 file_path_ = *read_file_path;
146 if (file_path_.empty())
147 NotifyHeadersComplete();
149 URLRequestFileJob::Start();
152 net::HttpResponseInfo response_info_;
153 base::FilePath relative_path_;
154 std::string content_security_policy_;
155 bool is_authority_match_;
156 ApplicationResource resource_;
157 std::list<std::string> locales_;
158 base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
161 // This class is a thread-safe cache of active application's data.
162 // This class is used by ApplicationProtocolHandler as it lives on IO thread
163 // and hence cannot access ApplicationService directly.
164 class ApplicationDataCache : public ApplicationService::Observer {
166 scoped_refptr<ApplicationData> GetApplicationData(
167 const std::string& application_id) const {
168 base::AutoLock lock(lock_);
169 ApplicationData::ApplicationDataMap::const_iterator it =
170 cache_.find(application_id);
171 if (it != cache_.end()) {
177 virtual void DidLaunchApplication(Application* app) OVERRIDE {
178 base::AutoLock lock(lock_);
179 cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
180 app->id(), app->data()));
183 virtual void WillDestroyApplication(Application* app) OVERRIDE {
184 base::AutoLock lock(lock_);
185 cache_.erase(app->id());
189 ApplicationData::ApplicationDataMap cache_;
190 mutable base::Lock lock_;
193 class ApplicationProtocolHandler
194 : public net::URLRequestJobFactory::ProtocolHandler {
196 explicit ApplicationProtocolHandler(ApplicationService* service) {
198 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199 // ApplicationProtocolHandler lives longer than ApplicationService,
200 // so we do not need to remove cache_ from ApplicationService
202 service->AddObserver(&cache_);
205 virtual ~ApplicationProtocolHandler() {}
207 virtual net::URLRequestJob* MaybeCreateJob(
208 net::URLRequest* request,
209 net::NetworkDelegate* network_delegate) const OVERRIDE;
212 ApplicationDataCache cache_;
213 DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
216 // The |locale| should be expanded to user agent locale.
217 // Such as, "en-us" will be expaned as "en-us, en".
218 void GetUserAgentLocales(const std::string& sys_locale,
219 std::list<std::string>& ua_locales) {
220 if (sys_locale.empty())
223 std::string locale = StringToLowerASCII(sys_locale);
226 ua_locales.push_back(locale);
227 position = locale.find_last_of("-");
228 locale = locale.substr(0, position);
229 } while (position != std::string::npos);
233 ApplicationProtocolHandler::MaybeCreateJob(
234 net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
235 const std::string& application_id = request->url().host();
236 scoped_refptr<ApplicationData> application =
237 cache_.GetApplicationData(application_id);
238 base::FilePath relative_path =
239 ApplicationURLToRelativeFilePath(request->url());
240 base::FilePath directory_path;
241 std::string content_security_policy;
243 directory_path = application->Path();
245 const char* csp_key = GetCSPKey(application->GetPackageType());
246 const CSPInfo* csp_info = static_cast<CSPInfo*>(
247 application->GetManifestData(csp_key));
249 const std::map<std::string, std::vector<std::string> >& policies =
250 csp_info->GetDirectives();
251 std::map<std::string, std::vector<std::string> >::const_iterator it =
253 for (; it != policies.end(); ++it) {
254 content_security_policy.append(
255 it->first + ' ' + JoinString(it->second, ' ') + ';');
260 const std::string& path = request->url().path();
262 std::list<std::string> locales;
263 if (application && application->GetPackageType() == Manifest::TYPE_WGT) {
265 xwalk::XWalkRunner::GetInstance()->GetLocale(), locales);
266 GetUserAgentLocales(application->GetManifest()->default_locale(), locales);
269 return new URLRequestApplicationJob(
272 content::BrowserThread::GetBlockingPool()->
273 GetTaskRunnerWithShutdownBehavior(
274 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
278 content_security_policy,
285 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
286 CreateApplicationProtocolHandler(ApplicationService* service) {
287 return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
288 new ApplicationProtocolHandler(service));
291 } // namespace application