From 8adbc01dc5975a64c55fe594d8c758c71e8183b3 Mon Sep 17 00:00:00 2001
From: myfreeer <myfreeer@users.noreply.github.com>
Date: Sun, 22 Jul 2018 19:59:02 +0800
Subject: [PATCH] option: add option to retry on http 400, 403, 406, or unknown

 --retry-on-400[=true|false]  Configure whether retry or not when
                              HTTP server returns 400 Bad Request.
                              Only effective if retry-wait > 0.

                              Possible Values: true, false
                              Default: false
                              Tags: #advanced, #http

 --retry-on-403[=true|false]  Configure whether retry or not when
                              HTTP server returns 403 Forbidden.
                              Only effective if retry-wait > 0.

                              Possible Values: true, false
                              Default: false
                              Tags: #advanced, #http

 --retry-on-406[=true|false]  Configure whether retry or not when
                              HTTP server returns 406 Not Acceptable.
                              Only effective if retry-wait > 0.

                              Possible Values: true, false
                              Default: false
                              Tags: #advanced, #http

 --retry-on-unknown[=true|false] Configure whether retry or not when
                              HTTP server returns unknown status code.
                              Only effective if retry-wait > 0.

                              Possible Values: true, false
                              Default: false
                              Tags: #advanced, #http
---
 src/HttpSkipResponseCommand.cc | 42 +++++++++++++++++++++++++++++-----
 src/OptionHandlerFactory.cc    | 40 ++++++++++++++++++++++++++++++++
 src/prefs.cc                   |  8 +++++++
 src/prefs.h                    |  8 +++++++
 src/usage_text.h               | 16 +++++++++++++
 5 files changed, 108 insertions(+), 6 deletions(-)

diff --git a/src/HttpSkipResponseCommand.cc b/src/HttpSkipResponseCommand.cc
index a722d77..de4ad6c 100644
--- a/src/HttpSkipResponseCommand.cc
+++ b/src/HttpSkipResponseCommand.cc
@@ -204,7 +204,7 @@ bool HttpSkipResponseCommand::processResponse()
   auto statusCode = httpResponse_->getStatusCode();
   if (statusCode >= 400) {
     switch (statusCode) {
-    case 401:
+    case 401: // Unauthorized
       if (getOption()->getAsBool(PREF_HTTP_AUTH_CHALLENGE) &&
           !httpResponse_->getHttpRequest()->authenticationUsed() &&
           getDownloadEngine()->getAuthConfigFactory()->activateBasicCred(
@@ -213,15 +213,41 @@ bool HttpSkipResponseCommand::processResponse()
         return prepareForRetry(0);
       }
       throw DL_ABORT_EX2(EX_AUTH_FAILED, error_code::HTTP_AUTH_FAILED);
-    case 404:
+    case 404: // Not Found
       if (getOption()->getAsInt(PREF_MAX_FILE_NOT_FOUND) == 0) {
         throw DL_ABORT_EX2(MSG_RESOURCE_NOT_FOUND,
                            error_code::RESOURCE_NOT_FOUND);
       }
       throw DL_RETRY_EX2(MSG_RESOURCE_NOT_FOUND,
                          error_code::RESOURCE_NOT_FOUND);
-    case 502:
-    case 503:
+    case 400: // Bad Request
+      if (getOption()->getAsBool(PREF_RETRY_ON_400)
+          && getOption()->getAsInt(PREF_RETRY_WAIT) > 0) {
+        throw DL_RETRY_EX2(fmt(EX_BAD_STATUS, statusCode),
+                          error_code::HTTP_PROTOCOL_ERROR);
+      }
+      break;
+    case 403: // Forbidden
+      if (getOption()->getAsBool(PREF_RETRY_ON_403)
+          && getOption()->getAsInt(PREF_RETRY_WAIT) > 0) {
+        throw DL_RETRY_EX2(fmt(EX_BAD_STATUS, statusCode),
+                          error_code::HTTP_PROTOCOL_ERROR);
+      }
+      break;
+    case 406: // Not Acceptable
+      if (getOption()->getAsBool(PREF_RETRY_ON_406)
+          && getOption()->getAsInt(PREF_RETRY_WAIT) > 0) {
+        throw DL_RETRY_EX2(fmt(EX_BAD_STATUS, statusCode),
+                          error_code::HTTP_PROTOCOL_ERROR);
+      }
+      break;
+    case 408: // Request Timeout
+    case 429: // Too Many Requests
+    case 502: // Bad Gateway
+    case 503: // Service Unavailable
+    case 507: // Insufficient Storage
+    case 520: // https://github.com/aria2/aria2/issues/1229
+    case 521: // https://github.com/aria2/aria2/issues/1229
       // Only retry if pretry-wait > 0. Hammering 'busy' server is not
       // a good idea.
       if (getOption()->getAsInt(PREF_RETRY_WAIT) > 0) {
@@ -230,12 +256,16 @@ bool HttpSkipResponseCommand::processResponse()
       }
       throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, statusCode),
                          error_code::HTTP_SERVICE_UNAVAILABLE);
-    case 504:
+    case 504: // Gateway Timeout
       // This is Gateway Timeout, so try again
       throw DL_RETRY_EX2(fmt(EX_BAD_STATUS, statusCode),
                          error_code::HTTP_SERVICE_UNAVAILABLE);
     };
-
+    if (getOption()->getAsBool(PREF_RETRY_ON_UNKNOWN)
+        && getOption()->getAsInt(PREF_RETRY_WAIT) > 0) {
+      throw DL_RETRY_EX2(fmt(EX_BAD_STATUS, statusCode),
+                        error_code::HTTP_PROTOCOL_ERROR);
+    }
     throw DL_ABORT_EX2(fmt(EX_BAD_STATUS, statusCode),
                        error_code::HTTP_PROTOCOL_ERROR);
   }
diff --git a/src/OptionHandlerFactory.cc b/src/OptionHandlerFactory.cc
index 5768f7b..decb03e 100644
--- a/src/OptionHandlerFactory.cc
+++ b/src/OptionHandlerFactory.cc
@@ -934,6 +934,46 @@ std::vector<OptionHandler*> OptionHandlerFactory::createOptionHandlers()
     op->setChangeOptionForReserved(true);
     handlers.push_back(op);
   }
+  {
+    OptionHandler* op(new BooleanOptionHandler(
+        PREF_RETRY_ON_400, TEXT_RETRY_ON_400, A2_V_FALSE, OptionHandler::OPT_ARG));
+    op->addTag(TAG_ADVANCED);
+    op->addTag(TAG_HTTP);
+    op->setInitialOption(true);
+    op->setChangeGlobalOption(true);
+    op->setChangeOptionForReserved(true);
+    handlers.push_back(op);
+  }
+  {
+    OptionHandler* op(new BooleanOptionHandler(
+        PREF_RETRY_ON_403, TEXT_RETRY_ON_403, A2_V_FALSE, OptionHandler::OPT_ARG));
+    op->addTag(TAG_ADVANCED);
+    op->addTag(TAG_HTTP);
+    op->setInitialOption(true);
+    op->setChangeGlobalOption(true);
+    op->setChangeOptionForReserved(true);
+    handlers.push_back(op);
+  }
+  {
+    OptionHandler* op(new BooleanOptionHandler(
+        PREF_RETRY_ON_406, TEXT_RETRY_ON_406, A2_V_FALSE, OptionHandler::OPT_ARG));
+    op->addTag(TAG_ADVANCED);
+    op->addTag(TAG_HTTP);
+    op->setInitialOption(true);
+    op->setChangeGlobalOption(true);
+    op->setChangeOptionForReserved(true);
+    handlers.push_back(op);
+  }
+  {
+    OptionHandler* op(new BooleanOptionHandler(
+        PREF_RETRY_ON_UNKNOWN, TEXT_RETRY_ON_UNKNOWN, A2_V_FALSE, OptionHandler::OPT_ARG));
+    op->addTag(TAG_ADVANCED);
+    op->addTag(TAG_HTTP);
+    op->setInitialOption(true);
+    op->setChangeGlobalOption(true);
+    op->setChangeOptionForReserved(true);
+    handlers.push_back(op);
+  }
   {
     OptionHandler* op(new BooleanOptionHandler(
         PREF_REUSE_URI, TEXT_REUSE_URI, A2_V_TRUE, OptionHandler::OPT_ARG));
diff --git a/src/prefs.cc b/src/prefs.cc
index 937e927..33eff91 100644
--- a/src/prefs.cc
+++ b/src/prefs.cc
@@ -327,6 +327,14 @@ PrefPtr PREF_ENABLE_ASYNC_DNS6 = makePref("enable-async-dns6");
 PrefPtr PREF_MAX_DOWNLOAD_RESULT = makePref("max-download-result");
 // value: 1*digit
 PrefPtr PREF_RETRY_WAIT = makePref("retry-wait");
+// value: true | false
+PrefPtr PREF_RETRY_ON_400 = makePref("retry-on-400");
+// value: true | false
+PrefPtr PREF_RETRY_ON_403 = makePref("retry-on-403");
+// value: true | false
+PrefPtr PREF_RETRY_ON_406 = makePref("retry-on-406");
+// value: true | false
+PrefPtr PREF_RETRY_ON_UNKNOWN = makePref("retry-on-unknown");
 // value: string
 PrefPtr PREF_ASYNC_DNS_SERVER = makePref("async-dns-server");
 // value: true | false
diff --git a/src/prefs.h b/src/prefs.h
index e1f8397..019e774 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -280,6 +280,14 @@ extern PrefPtr PREF_ENABLE_ASYNC_DNS6;
 extern PrefPtr PREF_MAX_DOWNLOAD_RESULT;
 // value: 1*digit
 extern PrefPtr PREF_RETRY_WAIT;
+// value: true | false
+extern PrefPtr PREF_RETRY_ON_400;
+// value: true | false
+extern PrefPtr PREF_RETRY_ON_403;
+// value: true | false
+extern PrefPtr PREF_RETRY_ON_406;
+// value: true | false
+extern PrefPtr PREF_RETRY_ON_UNKNOWN;
 // value: string
 extern PrefPtr PREF_ASYNC_DNS_SERVER;
 // value: true | false
diff --git a/src/usage_text.h b/src/usage_text.h
index d73b50d..75d34a0 100644
--- a/src/usage_text.h
+++ b/src/usage_text.h
@@ -64,6 +64,22 @@
   _(" --retry-wait=SEC             Set the seconds to wait between retries. \n" \
     "                              With SEC > 0, aria2 will retry download when the\n" \
     "                              HTTP server returns 503 response.")
+#define TEXT_RETRY_ON_400                                                 \
+  _(" --retry-on-400[=true|false]  Configure whether retry or not when\n" \
+    "                              HTTP server returns 400 Bad Request.\n" \
+    "                              Only effective if retry-wait > 0.")
+#define TEXT_RETRY_ON_403                                                 \
+  _(" --retry-on-403[=true|false]  Configure whether retry or not when\n" \
+    "                              HTTP server returns 403 Forbidden.\n" \
+    "                              Only effective if retry-wait > 0.")
+#define TEXT_RETRY_ON_406                                                 \
+  _(" --retry-on-406[=true|false]  Configure whether retry or not when\n" \
+    "                              HTTP server returns 406 Not Acceptable.\n" \
+    "                              Only effective if retry-wait > 0.")
+#define TEXT_RETRY_ON_UNKNOWN                                                 \
+  _(" --retry-on-unknown[=true|false] Configure whether retry or not when\n" \
+    "                              HTTP server returns unknown status code.\n" \
+    "                              Only effective if retry-wait > 0.")
 #define TEXT_TIMEOUT                                            \
   _(" -t, --timeout=SEC            Set timeout in seconds.")
 #define TEXT_MAX_TRIES                                                  \
-- 
2.18.0