Read the archive's header when there is a url request

This commit is contained in:
Cheng Zhao 2014-09-23 19:14:30 +08:00
parent 9b71117171
commit 6d712da7e3
6 changed files with 326 additions and 9 deletions

View file

@ -121,6 +121,10 @@
'atom/browser/native_window_observer.h',
'atom/browser/net/adapter_request_job.cc',
'atom/browser/net/adapter_request_job.h',
'atom/browser/net/asar/asar_protocol_handler.cc',
'atom/browser/net/asar/asar_protocol_handler.h',
'atom/browser/net/asar/url_request_asar_job.cc',
'atom/browser/net/asar/url_request_asar_job.h',
'atom/browser/net/atom_url_request_job_factory.cc',
'atom/browser/net/atom_url_request_job_factory.h',
'atom/browser/net/url_request_string_job.cc',
@ -186,6 +190,8 @@
'atom/common/api/atom_bindings.h',
'atom/common/api/object_life_monitor.cc',
'atom/common/api/object_life_monitor.h',
'atom/common/asar/archive.cc',
'atom/common/asar/archive.h',
'atom/common/common_message_generator.cc',
'atom/common/common_message_generator.h',
'atom/common/crash_reporter/crash_reporter.cc',

View file

@ -4,25 +4,60 @@
#include "atom/browser/net/asar/asar_protocol_handler.h"
#include "atom/browser/net/asar/url_request_asar_job.h"
#include "net/base/filename_util.h"
#include "net/url_request/url_request_file_job.h"
namespace asar {
AsarProtocolHandler::AsarProtocolHandler(
const scoped_refptr<base::TaskRunner>& file_task_runner)
: file_task_runner_(file_task_runner) {
namespace {
const base::FilePath::CharType kAsarExtension[] = FILE_PATH_LITERAL(".asar");
// Get the relative path in asar archive.
bool GetAsarPath(const base::FilePath& full_path,
base::FilePath* asar_path,
base::FilePath* relative_path) {
base::FilePath iter = full_path;
while (true) {
base::FilePath dirname = iter.DirName();
if (iter.MatchesExtension(kAsarExtension))
break;
else if (iter == dirname)
return false;
iter = dirname;
}
AsarProtocolHandler::~AsarProtocolHandler() {
base::FilePath tail;
if (!iter.AppendRelativePath(full_path, &tail))
return false;
*asar_path = iter;
*relative_path = tail;
return true;
}
} // namespace
AsarProtocolHandler::AsarProtocolHandler(
const scoped_refptr<base::TaskRunner>& file_task_runner)
: file_task_runner_(file_task_runner) {}
AsarProtocolHandler::~AsarProtocolHandler() {}
net::URLRequestJob* AsarProtocolHandler::MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const {
base::FilePath file_path;
net::FileURLToFilePath(request->url(), &file_path);
return new net::URLRequestFileJob(request, network_delegate, file_path,
base::FilePath full_path;
net::FileURLToFilePath(request->url(), &full_path);
// Create asar:// job when the path contains "xxx.asar/".
base::FilePath asar_path, relative_path;
if (GetAsarPath(full_path, &asar_path, &relative_path))
return new URLRequestAsarJob(request, network_delegate, asar_path,
relative_path, file_task_runner_);
else
return new net::URLRequestFileJob(request, network_delegate, full_path,
file_task_runner_);
}

View file

@ -0,0 +1,54 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/browser/net/asar/url_request_asar_job.h"
#include "net/base/mime_util.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_request_status.h"
namespace asar {
URLRequestAsarJob::URLRequestAsarJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const base::FilePath& asar_path,
const base::FilePath& file_path,
const scoped_refptr<base::TaskRunner>& file_task_runner)
: net::URLRequestJob(request, network_delegate),
archive_(asar_path),
file_path_(file_path),
file_task_runner_(file_task_runner),
weak_ptr_factory_(this) {}
URLRequestAsarJob::~URLRequestAsarJob() {}
void URLRequestAsarJob::Start() {
Archive::FileInfo info;
if (!archive_.Init() || !archive_.GetFileInfo(file_path_, &info)) {
NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
net::ERR_FILE_NOT_FOUND));
return;
}
NotifyHeadersComplete();
}
void URLRequestAsarJob::Kill() {
weak_ptr_factory_.InvalidateWeakPtrs();
URLRequestJob::Kill();
}
bool URLRequestAsarJob::ReadRawData(net::IOBuffer* buf,
int buf_size,
int* bytes_read) {
*bytes_read = 0;
return true;
}
bool URLRequestAsarJob::GetMimeType(std::string* mime_type) const {
return net::GetMimeTypeFromFile(file_path_, mime_type);
}
} // namespace asar

View file

@ -0,0 +1,51 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
#define ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_
#include "atom/common/asar/archive.h"
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "net/url_request/url_request_job.h"
namespace base {
class TaskRunner;
}
namespace asar {
class URLRequestAsarJob : public net::URLRequestJob {
public:
URLRequestAsarJob(net::URLRequest* request,
net::NetworkDelegate* network_delegate,
const base::FilePath& asar_path,
const base::FilePath& file_path,
const scoped_refptr<base::TaskRunner>& file_task_runner);
// net::URLRequestJob:
virtual void Start() OVERRIDE;
virtual void Kill() OVERRIDE;
virtual bool ReadRawData(net::IOBuffer* buf,
int buf_size,
int* bytes_read) OVERRIDE;
virtual bool GetMimeType(std::string* mime_type) const OVERRIDE;
protected:
virtual ~URLRequestAsarJob();
private:
Archive archive_;
base::FilePath file_path_;
const scoped_refptr<base::TaskRunner> file_task_runner_;
base::WeakPtrFactory<URLRequestAsarJob> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(URLRequestAsarJob);
};
} // namespace asar
#endif // ATOM_BROWSER_NET_ASAR_URL_REQUEST_ASAR_JOB_H_

127
atom/common/asar/archive.cc Normal file
View file

@ -0,0 +1,127 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "atom/common/asar/archive.h"
#include <string>
#include <vector>
#include "base/files/file.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "base/json/json_string_value_serializer.h"
#include "base/strings/string_number_conversions.h"
namespace asar {
namespace {
bool GetChildNode(const std::string& name,
const base::DictionaryValue* root,
const base::DictionaryValue** out) {
const base::DictionaryValue* files = NULL;
return root->GetDictionaryWithoutPathExpansion("files", &files) &&
files->GetDictionaryWithoutPathExpansion(name, out);
}
bool GetNodeFromPath(std::string path,
const base::DictionaryValue* root,
const base::DictionaryValue** out) {
for (size_t delimiter_position = path.find('/');
delimiter_position != std::string::npos;
delimiter_position = path.find('/')) {
const base::DictionaryValue* child = NULL;
if (!GetChildNode(path.substr(0, delimiter_position), root, &child))
return false;
root = child;
path.erase(0, delimiter_position + 1);
}
return GetChildNode(path, root, out);
}
} // namespace
Archive::Archive(const base::FilePath& path) : path_(path) {
}
Archive::~Archive() {
}
bool Archive::Init() {
base::File file(path_, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
PLOG(ERROR) << "Unable to open " << path_.value();
return false;
}
std::vector<char> buf;
int len;
buf.resize(8);
len = file.ReadAtCurrentPos(buf.data(), buf.size());
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header size from " << path_.value();
return false;
}
uint32 size;
if (!PickleIterator(Pickle(buf.data(), buf.size())).ReadUInt32(&size)) {
LOG(ERROR) << "Failed to parse header size from " << path_.value();
return false;
}
buf.resize(size);
len = file.ReadAtCurrentPos(buf.data(), buf.size());
if (len != static_cast<int>(buf.size())) {
PLOG(ERROR) << "Failed to read header from " << path_.value();
return false;
}
std::string header;
if (!PickleIterator(Pickle(buf.data(), buf.size())).ReadString(&header)) {
LOG(ERROR) << "Failed to parse header from " << path_.value();
return false;
}
std::string error;
JSONStringValueSerializer serializer(&header);
base::Value* value = serializer.Deserialize(NULL, &error);
if (!value || !value->IsType(base::Value::TYPE_DICTIONARY)) {
LOG(ERROR) << "Failed to parse header: " << error;
return false;
}
header_.reset(static_cast<base::DictionaryValue*>(value));
return true;
}
bool Archive::GetFileInfo(const base::FilePath& path, FileInfo* info) {
if (!header_)
return false;
const base::DictionaryValue* node;
if (!GetNodeFromPath(path.AsUTF8Unsafe(), header_.get(), &node))
return false;
std::string link;
if (node->GetString("link", &link))
return GetFileInfo(base::FilePath::FromUTF8Unsafe(link), info);
std::string offset;
if (!node->GetString("offset", &offset))
return false;
if (!base::StringToUint64(offset, &info->offset))
return false;
int size;
if (!node->GetInteger("size", &size))
return false;
info->size = static_cast<uint32>(size);
return true;
}
} // namespace asar

View file

@ -0,0 +1,44 @@
// Copyright (c) 2014 GitHub, Inc. All rights reserved.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef ATOM_COMMON_ASAR_ARCHIVE_H_
#define ATOM_COMMON_ASAR_ARCHIVE_H_
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
namespace base {
class DictionaryValue;
}
namespace asar {
class Archive {
public:
struct FileInfo {
uint32 size;
uint64 offset;
};
explicit Archive(const base::FilePath& path);
virtual ~Archive();
// Read and parse the header.
bool Init();
// Get the info of a file.
bool GetFileInfo(const base::FilePath& path, FileInfo* info);
base::FilePath path() const { return path_; }
private:
base::FilePath path_;
scoped_ptr<base::DictionaryValue> header_;
DISALLOW_COPY_AND_ASSIGN(Archive);
};
} // namespace asar
#endif // ATOM_COMMON_ASAR_ARCHIVE_H_