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;