HTTP Compliance: reply with 504 (Gateway Timeout) if required validation failed.

RFC 2616 says that we MUST reply with 504 (Gateway Timeout) if validation fails
and cached reply has proxy-revalidate, must-revalidate or s-maxage Cache-Control
directive.

FwdState::makeConnectingError() method is added to set error status depending on
whether the request was a validation request.

Co-Advisor test cases:
    test_case/rfc2616/noSrv-hit-must-reval-s-maxage-resp
    test_case/rfc2616/noSrv-hit-must-reval-proxy-revalidate-resp
    test_case/rfc2616/noSrv-hit-must-reval-must-revalidate-resp

=== modified file 'src/forward.cc'
--- src/forward.cc	2010-09-13 02:25:09 +0000
+++ src/forward.cc	2010-09-16 06:03:33 +0000
@@ -584,41 +584,41 @@ FwdState::negotiateSSL(int fd)
     SSL *ssl = fd_table[fd].ssl;
     int ret;
 
     if ((ret = SSL_connect(ssl)) <= 0) {
         int ssl_error = SSL_get_error(ssl, ret);
 
         switch (ssl_error) {
 
         case SSL_ERROR_WANT_READ:
             commSetSelect(fd, COMM_SELECT_READ, fwdNegotiateSSLWrapper, this, 0);
             return;
 
         case SSL_ERROR_WANT_WRITE:
             commSetSelect(fd, COMM_SELECT_WRITE, fwdNegotiateSSLWrapper, this, 0);
             return;
 
         default:
             debugs(81, 1, "fwdNegotiateSSL: Error negotiating SSL connection on FD " << fd <<
                    ": " << ERR_error_string(ERR_get_error(), NULL) << " (" << ssl_error <<
                    "/" << ret << "/" << errno << ")");
-            ErrorState *anErr = errorCon(ERR_SECURE_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+            ErrorState *const anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
 #ifdef EPROTO
 
             anErr->xerrno = EPROTO;
 #else
 
             anErr->xerrno = EACCES;
 #endif
 
             fail(anErr);
 
             if (fs->_peer) {
                 peerConnectFailed(fs->_peer);
                 fs->_peer->stats.conn_open--;
             }
 
             comm_close(fd);
             return;
         }
     }
 
@@ -704,50 +704,50 @@ FwdState::connectDone(int aServerFD, con
     FwdServer *fs = servers;
     assert(server_fd == aServerFD);
 
     request->recordLookup(dns);
 
     if (Config.onoff.log_ip_on_direct && status != COMM_ERR_DNS && fs->code == HIER_DIRECT)
         updateHierarchyInfo();
 
     if (status == COMM_ERR_DNS) {
         /*
          * Only set the dont_retry flag if the DNS lookup fails on
          * a direct connection.  If DNS lookup fails when trying
          * a neighbor cache, we may want to retry another option.
          */
 
         if (NULL == fs->_peer)
             flags.dont_retry = 1;
 
         debugs(17, 4, "fwdConnectDone: Unknown host: " << request->GetHost());
 
-        ErrorState *anErr = errorCon(ERR_DNS_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+        ErrorState *const anErr = makeConnectingError(ERR_DNS_FAIL);
 
         anErr->dnsError = dns.error;
 
         fail(anErr);
 
         comm_close(server_fd);
     } else if (status != COMM_OK) {
         assert(fs);
-        ErrorState *anErr = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE, request);
+        ErrorState *const anErr = makeConnectingError(ERR_CONNECT_FAIL);
         anErr->xerrno = xerrno;
 
         fail(anErr);
 
         if (fs->_peer)
             peerConnectFailed(fs->_peer);
 
         comm_close(server_fd);
     } else {
         debugs(17, 3, "fwdConnectDone: FD " << server_fd << ": '" << entry->url() << "'" );
 
         if (fs->_peer)
             peerConnectSucceded(fs->_peer);
 
 #if USE_SSL
 
         if ((fs->_peer && fs->_peer->use_ssl) ||
                 (!fs->_peer && request->protocol == PROTO_HTTPS)) {
             initiateSSL();
             return;
@@ -1151,40 +1151,53 @@ FwdState::reforward()
 
     if (request->bodyNibbled())
         return 0;
 
     assert(fs);
 
     servers = fs->next;
 
     fwdServerFree(fs);
 
     if (servers == NULL) {
         debugs(17, 3, "fwdReforward: No forward-servers left");
         return 0;
     }
 
     s = e->getReply()->sline.status;
     debugs(17, 3, "fwdReforward: status " << s);
     return reforwardableStatus(s);
 }
 
+/**
+ * Create "503 Service Unavailable" or "504 Gateway Timeout" error depending
+ * on whether this is a validation request. RFC 2616 says that we MUST reply
+ * with "504 Gateway Timeout" if validation fails and cached reply has
+ * proxy-revalidate, must-revalidate or s-maxage Cache-Control directive.
+ */
+ErrorState *
+FwdState::makeConnectingError(const err_type type) const
+{
+    return errorCon(type, request->flags.need_validation ?
+                    HTTP_GATEWAY_TIMEOUT : HTTP_SERVICE_UNAVAILABLE, request);
+}
+
 static void
 fwdStats(StoreEntry * s)
 {
     int i;
     int j;
     storeAppendPrintf(s, "Status");
 
     for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {
         storeAppendPrintf(s, "\ttry#%d", j + 1);
     }
 
     storeAppendPrintf(s, "\n");
 
     for (i = 0; i <= (int) HTTP_INVALID_HEADER; i++) {
         if (FwdReplyCodes[0][i] == 0)
             continue;
 
         storeAppendPrintf(s, "%3d", i);
 
         for (j = 0; j <= MAX_FWD_STATS_IDX; j++) {

=== modified file 'src/forward.h'
--- src/forward.h	2010-09-12 00:05:59 +0000
+++ src/forward.h	2010-09-16 05:48:05 +0000
@@ -48,40 +48,41 @@ public:
     bool dontRetry() { return flags.dont_retry; }
 
     void dontRetry(bool val) { flags.dont_retry = val; }
 
     bool ftpPasvFailed() { return flags.ftp_pasv_failed; }
 
     void ftpPasvFailed(bool val) { flags.ftp_pasv_failed = val; }
 
     static void serversFree(FwdServer **);
 
 private:
     // hidden for safer management of self; use static fwdStart
     FwdState(int fd, StoreEntry *, HttpRequest *);
     void start(Pointer aSelf);
 
     static void logReplyStatus(int tries, http_status status);
     void updateHierarchyInfo();
     void doneWithRetries();
     void completed();
     void retryOrBail();
+    ErrorState *makeConnectingError(const err_type type) const;
     static void RegisterWithCacheManager(void);
 
 #if WIP_FWD_LOG
 
     void uninit                /**DOCS_NOSEMI*/
     static void logRotate      /**DOCS_NOSEMI*/
     void status()              /**DOCS_NOSEMI*/
 #endif
 
 public:
     StoreEntry *entry;
     HttpRequest *request;
     int server_fd;
     FwdServer *servers;
     static void abort(void*);
 
 private:
     Pointer self;
     ErrorState *err;
     int client_fd;