浏览代码

Implemented Metalink/HTTP in HTTP download.

Link header fields from first Metalink server is utilized as described
in rfc6249. We only set digest from Digest header field to
DownloadContext only when PieceStorage is not initialized(in other
words, before file size is known). After PieceStorage is initialized,
Digest header field is used to check the value is the same in digest
in DownloadContext.  Current implementation only handles
rel=duplicate.
Tatsuhiro Tsujikawa 14 年之前
父节点
当前提交
a533437be6
共有 2 个文件被更改,包括 81 次插入0 次删除
  1. 70 0
      src/HttpResponseCommand.cc
  2. 11 0
      src/HttpResponseCommand.h

+ 70 - 0
src/HttpResponseCommand.cc

@@ -72,6 +72,10 @@
 #include "ChunkedDecodingStreamFilter.h"
 #include "ChunkedDecodingStreamFilter.h"
 #include "uri.h"
 #include "uri.h"
 #include "SocketRecvBuffer.h"
 #include "SocketRecvBuffer.h"
+#include "MetalinkHttpEntry.h"
+#ifdef ENABLE_MESSAGE_DIGEST
+#include "Checksum.h"
+#endif // ENABLE_MESSAGE_DIGEST
 #ifdef HAVE_ZLIB
 #ifdef HAVE_ZLIB
 # include "GZipDecodingStreamFilter.h"
 # include "GZipDecodingStreamFilter.h"
 #endif // HAVE_ZLIB
 #endif // HAVE_ZLIB
@@ -189,6 +193,42 @@ bool HttpResponseCommand::executeInternal()
     getFileEntry()->poolRequest(getRequest());
     getFileEntry()->poolRequest(getRequest());
     return true;
     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 >= 300) {
     if(statusCode == 404) {
     if(statusCode == 404) {
       getRequestGroup()->increaseAndValidateFileNotFoundCount();
       getRequestGroup()->increaseAndValidateFileNotFoundCount();
@@ -241,6 +281,19 @@ bool HttpResponseCommand::executeInternal()
       return handleDefaultEncoding(httpResponse);
       return handleDefaultEncoding(httpResponse);
     }
     }
   } else {
   } 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
     // validate totalsize
     getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
     getRequestGroup()->validateTotalLength(getFileEntry()->getLength(),
                                        httpResponse->getEntityLength());
                                        httpResponse->getEntityLength());
@@ -501,4 +554,21 @@ void HttpResponseCommand::onDryRunFileFound()
   poolConnection();
   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
 } // namespace aria2

+ 11 - 0
src/HttpResponseCommand.h

@@ -45,6 +45,9 @@ class HttpDownloadCommand;
 class HttpResponse;
 class HttpResponse;
 class SocketCore;
 class SocketCore;
 class StreamFilter;
 class StreamFilter;
+#ifdef ENABLE_MESSAGE_DIGEST
+class Checksum;
+#endif // ENABLE_MESSAGE_DIGEST
 
 
 // HttpResponseCommand receives HTTP response header from remote
 // HttpResponseCommand receives HTTP response header from remote
 // server.  Because network I/O is non-blocking, execute() returns
 // server.  Because network I/O is non-blocking, execute() returns
@@ -74,6 +77,14 @@ private:
   void poolConnection();
   void poolConnection();
 
 
   void onDryRunFileFound();
   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:
 protected:
   bool executeInternal();
   bool executeInternal();