mirror of https://github.com/aria2/aria2
Merge branch 'rfc6249'
commit
c536e460aa
|
@ -33,6 +33,7 @@
|
|||
*/
|
||||
/* copyright --> */
|
||||
#include "Checksum.h"
|
||||
#include "MessageDigest.h"
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
|
@ -62,4 +63,24 @@ void Checksum::setAlgo(const std::string& algo)
|
|||
algo_ = algo;
|
||||
}
|
||||
|
||||
void Checksum::swap(Checksum& other)
|
||||
{
|
||||
using std::swap;
|
||||
if(this != &other) {
|
||||
swap(algo_, other.algo_);
|
||||
swap(messageDigest_, other.messageDigest_);
|
||||
}
|
||||
}
|
||||
|
||||
void swap(Checksum& a, Checksum& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
bool HashTypeStronger::operator()
|
||||
(const Checksum& lhs, const Checksum& rhs) const
|
||||
{
|
||||
return MessageDigest::isStronger(lhs.getAlgo(), rhs.getAlgo());
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -64,6 +64,15 @@ public:
|
|||
{
|
||||
return algo_;
|
||||
}
|
||||
|
||||
void swap(Checksum& other);
|
||||
};
|
||||
|
||||
void swap(Checksum& a, Checksum& b);
|
||||
|
||||
class HashTypeStronger {
|
||||
public:
|
||||
bool operator()(const Checksum& lhs, const Checksum& rhs) const;
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -53,7 +53,8 @@ DownloadContext::DownloadContext():
|
|||
knowsTotalLength_(true),
|
||||
ownerRequestGroup_(0),
|
||||
downloadStartTime_(0),
|
||||
downloadStopTime_(downloadStartTime_) {}
|
||||
downloadStopTime_(downloadStartTime_),
|
||||
metalinkServerContacted_(false) {}
|
||||
|
||||
DownloadContext::DownloadContext(size_t pieceLength,
|
||||
uint64_t totalLength,
|
||||
|
@ -63,7 +64,8 @@ DownloadContext::DownloadContext(size_t pieceLength,
|
|||
knowsTotalLength_(true),
|
||||
ownerRequestGroup_(0),
|
||||
downloadStartTime_(0),
|
||||
downloadStopTime_(0)
|
||||
downloadStopTime_(0),
|
||||
metalinkServerContacted_(false)
|
||||
{
|
||||
SharedHandle<FileEntry> fileEntry(new FileEntry(path, totalLength, 0));
|
||||
fileEntries_.push_back(fileEntry);
|
||||
|
|
|
@ -84,6 +84,9 @@ private:
|
|||
Timer downloadStopTime_;
|
||||
|
||||
SharedHandle<Signature> signature_;
|
||||
// This member variable is required to avoid to parse Metalink/HTTP
|
||||
// Link header fields multiple times.
|
||||
bool metalinkServerContacted_;
|
||||
public:
|
||||
DownloadContext();
|
||||
|
||||
|
@ -224,6 +227,15 @@ public:
|
|||
SharedHandle<FileEntry> findFileEntryByOffset(off_t offset) const;
|
||||
|
||||
void releaseRuntimeResource();
|
||||
|
||||
void setMetalinkServerContacted(bool f)
|
||||
{
|
||||
metalinkServerContacted_ = f;
|
||||
}
|
||||
bool getMetalinkServerContacted() const
|
||||
{
|
||||
return metalinkServerContacted_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -66,6 +66,10 @@ const std::string HttpHeader::LAST_MODIFIED("Last-Modified");
|
|||
|
||||
const std::string HttpHeader::ACCEPT_ENCODING("Accept-Encoding");
|
||||
|
||||
const std::string HttpHeader::LINK("Link");
|
||||
|
||||
const std::string HttpHeader::DIGEST("Digest");
|
||||
|
||||
const char HttpHeader::HTTP_1_1[] = "HTTP/1.1";
|
||||
const char HttpHeader::CLOSE[] = "close";
|
||||
const char HttpHeader::CHUNKED[] = "chunked";
|
||||
|
@ -110,6 +114,14 @@ std::vector<std::string> HttpHeader::get(const std::string& name) const
|
|||
return v;
|
||||
}
|
||||
|
||||
std::pair<std::multimap<std::string, std::string>::const_iterator,
|
||||
std::multimap<std::string, std::string>::const_iterator>
|
||||
HttpHeader::getIterator(const std::string& name) const
|
||||
{
|
||||
std::string n(util::toLower(name));
|
||||
return table_.equal_range(n);
|
||||
}
|
||||
|
||||
unsigned int HttpHeader::getFirstAsUInt(const std::string& name) const {
|
||||
return getFirstAsULLInt(name);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,9 @@ public:
|
|||
bool defined(const std::string& name) const;
|
||||
const std::string& getFirst(const std::string& name) const;
|
||||
std::vector<std::string> get(const std::string& name) const;
|
||||
std::pair<std::multimap<std::string, std::string>::const_iterator,
|
||||
std::multimap<std::string, std::string>::const_iterator>
|
||||
getIterator(const std::string& name) const;
|
||||
unsigned int getFirstAsUInt(const std::string& name) const;
|
||||
uint64_t getFirstAsULLInt(const std::string& name) const;
|
||||
|
||||
|
@ -136,6 +139,10 @@ public:
|
|||
|
||||
static const std::string ACCEPT_ENCODING;
|
||||
|
||||
static const std::string LINK;
|
||||
|
||||
static const std::string DIGEST;
|
||||
|
||||
static const char HTTP_1_1[];
|
||||
|
||||
static const char CLOSE[];
|
||||
|
|
|
@ -51,6 +51,15 @@
|
|||
#include "AuthConfig.h"
|
||||
#include "ChunkedDecodingStreamFilter.h"
|
||||
#include "error_code.h"
|
||||
#include "prefs.h"
|
||||
#include "Option.h"
|
||||
#include "Checksum.h"
|
||||
#include "uri.h"
|
||||
#include "MetalinkHttpEntry.h"
|
||||
#include "Base64.h"
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
#include "MessageDigest.h"
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
#ifdef HAVE_ZLIB
|
||||
# include "GZipDecodingStreamFilter.h"
|
||||
#endif // HAVE_ZLIB
|
||||
|
@ -286,4 +295,147 @@ bool HttpResponse::supportsPersistentConnection() const
|
|||
!= std::string::npos);
|
||||
}
|
||||
|
||||
namespace {
|
||||
bool parseMetalinkHttpLink(MetalinkHttpEntry& result, const std::string& s)
|
||||
{
|
||||
std::string::const_iterator first = std::find(s.begin(), s.end(), '<');
|
||||
if(first == s.end()) {
|
||||
return false;
|
||||
}
|
||||
std::string::const_iterator last = std::find(first, s.end(), '>');
|
||||
if(last == s.end()) {
|
||||
return false;
|
||||
}
|
||||
std::string uri = util::stripIter(first+1, last);
|
||||
if(uri.empty()) {
|
||||
return false;
|
||||
} else {
|
||||
result.uri = uri;
|
||||
}
|
||||
last = std::find(last, s.end(), ';');
|
||||
if(last != s.end()) {
|
||||
++last;
|
||||
}
|
||||
bool ok = false;
|
||||
while(1) {
|
||||
std::string name, value;
|
||||
std::pair<std::string::const_iterator, bool> r =
|
||||
util::nextParam(name, value, last, s.end(), ';');
|
||||
last = r.first;
|
||||
if(!r.second) {
|
||||
break;
|
||||
}
|
||||
if(value.empty()) {
|
||||
if(name == "pref") {
|
||||
result.pref = true;
|
||||
}
|
||||
} else {
|
||||
if(name == "rel") {
|
||||
if(value == "duplicate") {
|
||||
ok = true;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if(name == "pri") {
|
||||
int32_t priValue;
|
||||
if(util::parseIntNoThrow(priValue, value)) {
|
||||
if(1 <= priValue && priValue <= 999999) {
|
||||
result.pri = priValue;
|
||||
}
|
||||
}
|
||||
} else if(name == "geo") {
|
||||
util::lowercase(value);
|
||||
result.geo = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Metalink/HTTP is defined by http://tools.ietf.org/html/rfc6249.
|
||||
// Link header field is defined by http://tools.ietf.org/html/rfc5988.
|
||||
void HttpResponse::getMetalinKHttpEntries
|
||||
(std::vector<MetalinkHttpEntry>& result,
|
||||
const SharedHandle<Option>& option) const
|
||||
{
|
||||
std::pair<std::multimap<std::string, std::string>::const_iterator,
|
||||
std::multimap<std::string, std::string>::const_iterator> p =
|
||||
httpHeader_->getIterator(HttpHeader::LINK);
|
||||
for(; p.first != p.second; ++p.first) {
|
||||
MetalinkHttpEntry e;
|
||||
if(parseMetalinkHttpLink(e, (*p.first).second)) {
|
||||
result.push_back(e);
|
||||
}
|
||||
}
|
||||
if(!result.empty()) {
|
||||
std::vector<std::string> locs;
|
||||
if(option->defined(PREF_METALINK_LOCATION)) {
|
||||
util::split(util::toLower(option->get(PREF_METALINK_LOCATION)),
|
||||
std::back_inserter(locs), ",", true);
|
||||
}
|
||||
for(std::vector<MetalinkHttpEntry>::iterator i = result.begin(),
|
||||
eoi = result.end(); i != eoi; ++i) {
|
||||
if(std::find(locs.begin(), locs.end(), (*i).geo) != locs.end()) {
|
||||
(*i).pri -= 999999;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
// Digest header field is defined by
|
||||
// http://tools.ietf.org/html/rfc3230.
|
||||
void HttpResponse::getDigest(std::vector<Checksum>& result) const
|
||||
{
|
||||
using std::swap;
|
||||
std::pair<std::multimap<std::string, std::string>::const_iterator,
|
||||
std::multimap<std::string, std::string>::const_iterator> p =
|
||||
httpHeader_->getIterator(HttpHeader::DIGEST);
|
||||
for(; p.first != p.second; ++p.first) {
|
||||
const std::string& s = (*p.first).second;
|
||||
std::string::const_iterator itr = s.begin();
|
||||
while(1) {
|
||||
std::string hashType, digest;
|
||||
std::pair<std::string::const_iterator, bool> r =
|
||||
util::nextParam(hashType, digest, itr, s.end(), ',');
|
||||
itr = r.first;
|
||||
if(!r.second) {
|
||||
break;
|
||||
}
|
||||
util::lowercase(hashType);
|
||||
if(!MessageDigest::supports(hashType)) {
|
||||
continue;
|
||||
}
|
||||
std::string hexDigest = util::toHex(Base64::decode(digest));
|
||||
if(!MessageDigest::isValidHash(hashType, hexDigest)) {
|
||||
continue;
|
||||
}
|
||||
result.push_back(Checksum(hashType, hexDigest));
|
||||
}
|
||||
}
|
||||
std::sort(result.begin(), result.end(), HashTypeStronger());
|
||||
std::vector<Checksum> temp;
|
||||
for(std::vector<Checksum>::iterator i = result.begin(),
|
||||
eoi = result.end(); i != eoi;) {
|
||||
bool ok = true;
|
||||
std::vector<Checksum>::iterator j = i+1;
|
||||
for(; j != eoi; ++j) {
|
||||
if((*i).getAlgo() != (*j).getAlgo()) {
|
||||
break;
|
||||
}
|
||||
if((*i).getMessageDigest() != (*j).getMessageDigest()) {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if(ok) {
|
||||
temp.push_back(*i);
|
||||
}
|
||||
i = j;
|
||||
}
|
||||
swap(temp, result);
|
||||
}
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "common.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "SharedHandle.h"
|
||||
#include "TimeA2.h"
|
||||
|
@ -48,6 +49,9 @@ namespace aria2 {
|
|||
class HttpRequest;
|
||||
class HttpHeader;
|
||||
class StreamFilter;
|
||||
class MetalinkHttpEntry;
|
||||
class Option;
|
||||
class Checksum;
|
||||
|
||||
class HttpResponse {
|
||||
private:
|
||||
|
@ -127,6 +131,17 @@ public:
|
|||
Time getLastModifiedTime() const;
|
||||
|
||||
bool supportsPersistentConnection() const;
|
||||
|
||||
void getMetalinKHttpEntries
|
||||
(std::vector<MetalinkHttpEntry>& result,
|
||||
const SharedHandle<Option>& option) const;
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
// Returns all digests specified in Digest header field. Sort
|
||||
// strong algorithm first. Strength is defined in MessageDigest. If
|
||||
// several same digest algorithms are available, but they have
|
||||
// different value, they are all ignored.
|
||||
void getDigest(std::vector<Checksum>& result) const;
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
};
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -72,6 +72,10 @@
|
|||
#include "ChunkedDecodingStreamFilter.h"
|
||||
#include "uri.h"
|
||||
#include "SocketRecvBuffer.h"
|
||||
#include "MetalinkHttpEntry.h"
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
#include "Checksum.h"
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
#ifdef HAVE_ZLIB
|
||||
# include "GZipDecodingStreamFilter.h"
|
||||
#endif // HAVE_ZLIB
|
||||
|
@ -189,6 +193,42 @@ bool HttpResponseCommand::executeInternal()
|
|||
getFileEntry()->poolRequest(getRequest());
|
||||
return true;
|
||||
}
|
||||
if(!getPieceStorage()) {
|
||||
// Metalink/HTTP
|
||||
if(!getDownloadContext()->getMetalinkServerContacted()) {
|
||||
if(httpHeader->defined(HttpHeader::LINK)) {
|
||||
getDownloadContext()->setMetalinkServerContacted(true);
|
||||
std::vector<MetalinkHttpEntry> entries;
|
||||
httpResponse->getMetalinKHttpEntries(entries, getOption());
|
||||
for(std::vector<MetalinkHttpEntry>::iterator i = entries.begin(),
|
||||
eoi = entries.end(); i != eoi; ++i) {
|
||||
getFileEntry()->addUri((*i).uri);
|
||||
A2_LOG_DEBUG(fmt("Adding URI=%s", (*i).uri.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
if(httpHeader->defined(HttpHeader::DIGEST)) {
|
||||
std::vector<Checksum> checksums;
|
||||
httpResponse->getDigest(checksums);
|
||||
for(std::vector<Checksum>::iterator i = checksums.begin(),
|
||||
eoi = checksums.end(); i != eoi; ++i) {
|
||||
if(getDownloadContext()->getChecksumHashAlgo().empty()) {
|
||||
A2_LOG_DEBUG(fmt("Setting digest: type=%s, digest=%s",
|
||||
(*i).getAlgo().c_str(),
|
||||
(*i).getMessageDigest().c_str()));
|
||||
getDownloadContext()->setChecksumHashAlgo((*i).getAlgo());
|
||||
getDownloadContext()->setChecksum((*i).getMessageDigest());
|
||||
break;
|
||||
} else {
|
||||
if(checkChecksum(getDownloadContext(), *i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
}
|
||||
if(statusCode >= 300) {
|
||||
if(statusCode == 404) {
|
||||
getRequestGroup()->increaseAndValidateFileNotFoundCount();
|
||||
|
@ -241,6 +281,19 @@ bool HttpResponseCommand::executeInternal()
|
|||
return handleDefaultEncoding(httpResponse);
|
||||
}
|
||||
} else {
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
if(!getDownloadContext()->getChecksumHashAlgo().empty() &&
|
||||
httpHeader->defined(HttpHeader::DIGEST)) {
|
||||
std::vector<Checksum> checksums;
|
||||
httpResponse->getDigest(checksums);
|
||||
for(std::vector<Checksum>::iterator i = checksums.begin(),
|
||||
eoi = checksums.end(); i != eoi; ++i) {
|
||||
if(checkChecksum(getDownloadContext(), *i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
// validate totalsize
|
||||
getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
|
||||
httpResponse->getEntityLength());
|
||||
|
@ -501,4 +554,21 @@ void HttpResponseCommand::onDryRunFileFound()
|
|||
poolConnection();
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
bool HttpResponseCommand::checkChecksum
|
||||
(const SharedHandle<DownloadContext>& dctx,
|
||||
const Checksum& checksum)
|
||||
{
|
||||
if(dctx->getChecksumHashAlgo() == checksum.getAlgo()) {
|
||||
if(dctx->getChecksum() == checksum.getMessageDigest()) {
|
||||
A2_LOG_INFO("Valid hash found in Digest header field.");
|
||||
return true;
|
||||
} else {
|
||||
throw DL_ABORT_EX("Invalid hash found in Digest header field.");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -45,6 +45,9 @@ class HttpDownloadCommand;
|
|||
class HttpResponse;
|
||||
class SocketCore;
|
||||
class StreamFilter;
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
class Checksum;
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
|
||||
// HttpResponseCommand receives HTTP response header from remote
|
||||
// server. Because network I/O is non-blocking, execute() returns
|
||||
|
@ -74,6 +77,14 @@ private:
|
|||
void poolConnection();
|
||||
|
||||
void onDryRunFileFound();
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
// Returns true if dctx and checksum has same hash type and hash
|
||||
// value. If they have same hash type but different hash value,
|
||||
// throws exception. Otherwise returns false.
|
||||
bool checkChecksum
|
||||
(const SharedHandle<DownloadContext>& dctx,
|
||||
const Checksum& checksum);
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
protected:
|
||||
bool executeInternal();
|
||||
|
||||
|
|
|
@ -224,7 +224,8 @@ SRCS = Socket.h\
|
|||
HttpServer.cc HttpServer.h\
|
||||
StreamPieceSelector.h\
|
||||
DefaultStreamPieceSelector.cc DefaultStreamPieceSelector.h\
|
||||
InorderStreamPieceSelector.cc InorderStreamPieceSelector.h
|
||||
InorderStreamPieceSelector.cc InorderStreamPieceSelector.h\
|
||||
MetalinkHttpEntry.cc MetalinkHttpEntry.h
|
||||
|
||||
if ENABLE_XML_RPC
|
||||
SRCS += XmlRpcRequestParserController.cc XmlRpcRequestParserController.h\
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2011 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
#include "MetalinkHttpEntry.h"
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
MetalinkHttpEntry::MetalinkHttpEntry()
|
||||
: pri(999999),
|
||||
pref(false)
|
||||
{}
|
||||
|
||||
MetalinkHttpEntry::~MetalinkHttpEntry() {}
|
||||
|
||||
void MetalinkHttpEntry::swap(MetalinkHttpEntry& other)
|
||||
{
|
||||
using std::swap;
|
||||
if(this != &other) {
|
||||
swap(uri, other.uri);
|
||||
swap(pri, other.pri);
|
||||
swap(pref, other.pref);
|
||||
swap(geo, other.geo);
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalinkHttpEntry::operator<(const MetalinkHttpEntry& rhs) const
|
||||
{
|
||||
if(pref^rhs.pref) {
|
||||
return pref;
|
||||
} else {
|
||||
return pri < rhs.pri;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(MetalinkHttpEntry& a, MetalinkHttpEntry& b)
|
||||
{
|
||||
a.swap(b);
|
||||
}
|
||||
|
||||
} // namespace aria2
|
|
@ -0,0 +1,62 @@
|
|||
/* <!-- copyright */
|
||||
/*
|
||||
* aria2 - The high speed download utility
|
||||
*
|
||||
* Copyright (C) 2011 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* In addition, as a special exception, the copyright holders give
|
||||
* permission to link the code of portions of this program with the
|
||||
* OpenSSL library under certain conditions as described in each
|
||||
* individual source file, and distribute linked combinations
|
||||
* including the two.
|
||||
* You must obey the GNU General Public License in all respects
|
||||
* for all of the code used other than OpenSSL. If you modify
|
||||
* file(s) with this exception, you may extend this exception to your
|
||||
* version of the file(s), but you are not obligated to do so. If you
|
||||
* do not wish to do so, delete this exception statement from your
|
||||
* version. If you delete this exception statement from all source
|
||||
* files in the program, then also delete it here.
|
||||
*/
|
||||
/* copyright --> */
|
||||
#ifndef D_METALINK_HTTP_ENTRY_H
|
||||
#define D_METALINK_HTTP_ENTRY_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
// Holds values of Metalink/HTTP in Link header fields. Metalink/HTTP
|
||||
// is defined by http://tools.ietf.org/html/rfc6249
|
||||
struct MetalinkHttpEntry {
|
||||
std::string uri;
|
||||
int pri;
|
||||
bool pref;
|
||||
std::string geo;
|
||||
|
||||
MetalinkHttpEntry();
|
||||
~MetalinkHttpEntry();
|
||||
void swap(MetalinkHttpEntry& c);
|
||||
bool operator<(const MetalinkHttpEntry& rhs) const;
|
||||
};
|
||||
|
||||
void swap(MetalinkHttpEntry& a, MetalinkHttpEntry& b);
|
||||
|
||||
} // namespace aria2
|
||||
|
||||
#endif // D_METALINK_HTTP_ENTRY_H
|
54
src/util.h
54
src/util.h
|
@ -438,6 +438,60 @@ std::string makeString(const char* str);
|
|||
// strerror returns NULL, this function returns empty string.
|
||||
std::string safeStrerror(int errNum);
|
||||
|
||||
// Parses sequence [first, last) and find name=value pair delimited by
|
||||
// delim character. If name(and optionally value) is found, returns
|
||||
// pair of iterator which can use as first parameter of next call of
|
||||
// this function, and true. If no name is found, returns the pair of
|
||||
// last and false.
|
||||
template<typename Iterator>
|
||||
std::pair<Iterator, bool>
|
||||
nextParam
|
||||
(std::string& name,
|
||||
std::string& value,
|
||||
Iterator first,
|
||||
Iterator last,
|
||||
char delim)
|
||||
{
|
||||
Iterator end = last;
|
||||
while(first != end) {
|
||||
last = first;
|
||||
Iterator parmnameFirst = first;
|
||||
Iterator parmnameLast = first;
|
||||
bool eqFound = false;
|
||||
for(; last != end; ++last) {
|
||||
if(*last == delim) {
|
||||
break;
|
||||
} else if(!eqFound && *last == '=') {
|
||||
eqFound = true;
|
||||
parmnameFirst = first;
|
||||
parmnameLast = last;
|
||||
}
|
||||
}
|
||||
std::string tname, tvalue;
|
||||
if(parmnameFirst == parmnameLast) {
|
||||
if(!eqFound) {
|
||||
parmnameFirst = first;
|
||||
parmnameLast = last;
|
||||
tname = util::stripIter(parmnameFirst, parmnameLast);
|
||||
}
|
||||
} else {
|
||||
first = parmnameLast+1;
|
||||
tname = util::stripIter(parmnameFirst, parmnameLast);
|
||||
tvalue = util::stripIter(first, last);
|
||||
}
|
||||
if(last != end) {
|
||||
++last;
|
||||
}
|
||||
if(!tname.empty()) {
|
||||
name.swap(tname);
|
||||
value.swap(tvalue);
|
||||
return std::make_pair(last, true);
|
||||
}
|
||||
first = last;
|
||||
}
|
||||
return std::make_pair(end, false);
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include "AuthConfigFactory.h"
|
||||
#include "AuthConfig.h"
|
||||
#include "StreamFilter.h"
|
||||
#include "MetalinkHttpEntry.h"
|
||||
#include "Option.h"
|
||||
#include "Checksum.h"
|
||||
|
||||
namespace aria2 {
|
||||
|
||||
|
@ -49,6 +52,10 @@ class HttpResponseTest : public CppUnit::TestFixture {
|
|||
CPPUNIT_TEST(testProcessRedirect);
|
||||
CPPUNIT_TEST(testRetrieveCookie);
|
||||
CPPUNIT_TEST(testSupportsPersistentConnection);
|
||||
CPPUNIT_TEST(testGetMetalinKHttpEntries);
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
CPPUNIT_TEST(testGetDigest);
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
private:
|
||||
|
||||
|
@ -81,6 +88,10 @@ public:
|
|||
void testProcessRedirect();
|
||||
void testRetrieveCookie();
|
||||
void testSupportsPersistentConnection();
|
||||
void testGetMetalinKHttpEntries();
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
void testGetDigest();
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
};
|
||||
|
||||
|
||||
|
@ -590,4 +601,85 @@ void HttpResponseTest::testSupportsPersistentConnection()
|
|||
httpHeader->clearField();
|
||||
}
|
||||
|
||||
void HttpResponseTest::testGetMetalinKHttpEntries()
|
||||
{
|
||||
HttpResponse httpResponse;
|
||||
SharedHandle<HttpHeader> httpHeader(new HttpHeader());
|
||||
httpResponse.setHttpHeader(httpHeader);
|
||||
SharedHandle<Option> option(new Option());
|
||||
|
||||
httpHeader->put("Link", "<http://uri1/>; rel=duplicate; pri=1; pref; geo=JP");
|
||||
httpHeader->put("Link", "<http://uri2/>; rel=duplicate");
|
||||
httpHeader->put("Link", "<http://uri3/>;;;;;;;;rel=duplicate;;;;;pri=2;;;;;");
|
||||
httpHeader->put("Link", "<http://uri4/>;rel=duplicate;=pri=1;pref");
|
||||
httpHeader->put("Link", "<http://describedby>; rel=describedby");
|
||||
httpHeader->put("Link", "<http://norel/>");
|
||||
httpHeader->put("Link", "<baduri>; rel=duplicate; pri=-1;");
|
||||
std::vector<MetalinkHttpEntry> result;
|
||||
httpResponse.getMetalinKHttpEntries(result, option);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)5, result.size());
|
||||
|
||||
MetalinkHttpEntry e = result[0];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http://uri1/"), e.uri);
|
||||
CPPUNIT_ASSERT_EQUAL(1, e.pri);
|
||||
CPPUNIT_ASSERT(e.pref);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("jp"), e.geo);
|
||||
|
||||
e = result[1];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http://uri4/"), e.uri);
|
||||
CPPUNIT_ASSERT_EQUAL(999999, e.pri);
|
||||
CPPUNIT_ASSERT(e.pref);
|
||||
CPPUNIT_ASSERT(e.geo.empty());
|
||||
|
||||
e = result[2];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http://uri3/"), e.uri);
|
||||
CPPUNIT_ASSERT_EQUAL(2, e.pri);
|
||||
CPPUNIT_ASSERT(!e.pref);
|
||||
CPPUNIT_ASSERT(e.geo.empty());
|
||||
|
||||
e = result[3];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("http://uri2/"), e.uri);
|
||||
CPPUNIT_ASSERT_EQUAL(999999, e.pri);
|
||||
CPPUNIT_ASSERT(!e.pref);
|
||||
CPPUNIT_ASSERT(e.geo.empty());
|
||||
|
||||
e = result[4];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("baduri"), e.uri);
|
||||
CPPUNIT_ASSERT_EQUAL(999999, e.pri);
|
||||
CPPUNIT_ASSERT(!e.pref);
|
||||
CPPUNIT_ASSERT(e.geo.empty());
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MESSAGE_DIGEST
|
||||
void HttpResponseTest::testGetDigest()
|
||||
{
|
||||
HttpResponse httpResponse;
|
||||
SharedHandle<HttpHeader> httpHeader(new HttpHeader());
|
||||
httpResponse.setHttpHeader(httpHeader);
|
||||
SharedHandle<Option> option(new Option());
|
||||
// Python binascii.hexlify(base64.b64decode(B64ED_HASH)) is handy to
|
||||
// retrieve ascii hex hash string.
|
||||
httpHeader->put("Digest", "SHA-1=82AD8itGL/oYQ5BTPFANiYnp9oE=");
|
||||
httpHeader->put("Digest", "NOT_SUPPORTED");
|
||||
httpHeader->put("Digest", "SHA-224=rQdowoLHQJTMVZ3rF7vmYOIzUXlu7F+FcMbPnA==");
|
||||
httpHeader->put("Digest", "SHA-224=6Ik6LNZ1iPy6cbmlKO4NHfvxzaiurmHilMyhGA==");
|
||||
httpHeader->put("Digest",
|
||||
"SHA-256=+D8nGudz3G/kpkVKQeDrI3xD57v0UeQmzGCZOk03nsU=,"
|
||||
"MD5=LJDK2+9ClF8Nz/K5WZd/+A==");
|
||||
std::vector<Checksum> result;
|
||||
httpResponse.getDigest(result);
|
||||
CPPUNIT_ASSERT_EQUAL((size_t)3, result.size());
|
||||
|
||||
Checksum c = result[0];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("sha-256"), c.getAlgo());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("f83f271ae773dc6fe4a6454a41e0eb237c43e7bbf451e426cc60993a4d379ec5"),
|
||||
c.getMessageDigest());
|
||||
|
||||
c = result[1];
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("sha-1"), c.getAlgo());
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("f36003f22b462ffa184390533c500d8989e9f681"),
|
||||
c.getMessageDigest());
|
||||
}
|
||||
#endif // ENABLE_MESSAGE_DIGEST
|
||||
|
||||
} // namespace aria2
|
||||
|
|
|
@ -71,6 +71,7 @@ class UtilTest:public CppUnit::TestFixture {
|
|||
CPPUNIT_TEST(testGetCidrPrefix);
|
||||
CPPUNIT_TEST(testInSameCidrBlock);
|
||||
CPPUNIT_TEST(testIsUtf8String);
|
||||
CPPUNIT_TEST(testNextParam);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
private:
|
||||
|
||||
|
@ -130,6 +131,7 @@ public:
|
|||
void testGetCidrPrefix();
|
||||
void testInSameCidrBlock();
|
||||
void testIsUtf8String();
|
||||
void testNextParam();
|
||||
};
|
||||
|
||||
|
||||
|
@ -1247,4 +1249,42 @@ void UtilTest::testIsUtf8String()
|
|||
CPPUNIT_ASSERT(!util::isUtf8(util::fromHex("00")));
|
||||
}
|
||||
|
||||
void UtilTest::testNextParam()
|
||||
{
|
||||
std::string s1 = " :a : b=c :d=b::::g::";
|
||||
std::pair<std::string::iterator, bool> r;
|
||||
std::string name, value;
|
||||
r = util::nextParam(name, value, s1.begin(), s1.end(), ':');
|
||||
CPPUNIT_ASSERT(r.second);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("a"), name);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string(), value);
|
||||
|
||||
r = util::nextParam(name, value, r.first, s1.end(), ':');
|
||||
CPPUNIT_ASSERT(r.second);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("b"), name);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("c"), value);
|
||||
|
||||
r = util::nextParam(name, value, r.first, s1.end(), ':');
|
||||
CPPUNIT_ASSERT(r.second);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("d"), name);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("b"), value);
|
||||
|
||||
r = util::nextParam(name, value, r.first, s1.end(), ':');
|
||||
CPPUNIT_ASSERT(r.second);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string("g"), name);
|
||||
CPPUNIT_ASSERT_EQUAL(std::string(), value);
|
||||
|
||||
std::string s2 = "";
|
||||
r = util::nextParam(name, value, s2.begin(), s2.end(), ':');
|
||||
CPPUNIT_ASSERT(!r.second);
|
||||
|
||||
std::string s3 = " ";
|
||||
r = util::nextParam(name, value, s3.begin(), s3.end(), ':');
|
||||
CPPUNIT_ASSERT(!r.second);
|
||||
|
||||
std::string s4 = ":::";
|
||||
r = util::nextParam(name, value, s4.begin(), s4.end(), ':');
|
||||
CPPUNIT_ASSERT(!r.second);
|
||||
}
|
||||
|
||||
} // namespace aria2
|
||||
|
|
Loading…
Reference in New Issue