/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "helper.h"
#include "helper/Reply.h"
#include "http.h"
+#include "http/Stream.h"
#include "HttpHdrCc.h"
#include "HttpReply.h"
#include "HttpRequest.h"
setConn(aConn);
al = new AccessLogEntry;
al->cache.start_time = current_time;
- al->tcpClient = clientConnection = aConn->clientConnection;
- al->cache.port = aConn->port;
- al->cache.caddr = aConn->log_addr;
+ if (aConn) {
+ al->tcpClient = clientConnection = aConn->clientConnection;
+ al->cache.port = aConn->port;
+ al->cache.caddr = aConn->log_addr;
#if USE_OPENSSL
- if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) {
- if (SSL *ssl = fd_table[aConn->clientConnection->fd].ssl)
- al->cache.sslClientCert.reset(SSL_get_peer_certificate(ssl));
- }
+ if (aConn->clientConnection != NULL && aConn->clientConnection->isOpen()) {
+ if (auto ssl = fd_table[aConn->clientConnection->fd].ssl.get())
+ al->cache.sslClientCert.resetWithoutLocking(SSL_get_peer_certificate(ssl));
+ }
#endif
+ }
dlinkAdd(this, &active, &ClientActiveRequests);
}
{
assert(request);
return request->cache_control &&
- request->cache_control->onlyIfCached();
+ request->cache_control->hasOnlyIfCached();
}
/**
/* the ICP check here was erroneous
* - StoreEntry::releaseRequest was always called if entry was valid
*/
- assert(logType < LOG_TYPE_MAX);
logRequest();
int
clientBeginRequest(const HttpRequestMethod& method, char const *url, CSCB * streamcallback,
CSD * streamdetach, ClientStreamData streamdata, HttpHeader const *header,
- char *tailbuf, size_t taillen)
+ char *tailbuf, size_t taillen, const MasterXaction::Pointer &mx)
{
size_t url_sz;
ClientHttpRequest *http = new ClientHttpRequest(NULL);
http->uri = (char *)xcalloc(url_sz, 1);
strcpy(http->uri, url);
- if ((request = HttpRequest::CreateFromUrlAndMethod(http->uri, method)) == NULL) {
+ if ((request = HttpRequest::FromUrl(http->uri, mx, method)) == NULL) {
debugs(85, 5, "Invalid URL: " << http->uri);
return -1;
}
* correctness.
*/
if (header)
- request->header.update(header, NULL);
+ request->header.update(header);
http->log_uri = xstrdup(urlCanonicalClean(request));
*/
request->flags.accelerated = http->flags.accel;
- request->flags.internalClient = true;
-
/* this is an internally created
* request, not subject to acceleration
* target overrides */
// NP: we do not yet handle CONNECT tunnels well, so ignore for them
if (!Config.onoff.hostStrictVerify && http->request->method != Http::METHOD_CONNECT) {
debugs(85, 3, "SECURITY ALERT: Host header forgery detected on " << http->getConn()->clientConnection <<
- " (" << A << " does not match " << B << ") on URL: " << urlCanonical(http->request));
+ " (" << A << " does not match " << B << ") on URL: " << http->request->effectiveRequestUri());
// NP: it is tempting to use 'flags.noCache' but that is all about READing cache data.
// The problems here are about WRITE for new cache content, which means flags.cachable
debugs(85, DBG_IMPORTANT, "SECURITY ALERT: Host header forgery detected on " <<
http->getConn()->clientConnection << " (" << A << " does not match " << B << ")");
- debugs(85, DBG_IMPORTANT, "SECURITY ALERT: By user agent: " << http->request->header.getStr(HDR_USER_AGENT));
- debugs(85, DBG_IMPORTANT, "SECURITY ALERT: on URL: " << urlCanonical(http->request));
+ if (const char *ua = http->request->header.getStr(Http::HdrType::USER_AGENT))
+ debugs(85, DBG_IMPORTANT, "SECURITY ALERT: By user agent: " << ua);
+ debugs(85, DBG_IMPORTANT, "SECURITY ALERT: on URL: " << http->request->effectiveRequestUri());
// IP address validation for Host: failed. reject the connection.
clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->prev->data;
ClientRequestContext::hostHeaderVerify()
{
// Require a Host: header.
- const char *host = http->request->header.getStr(HDR_HOST);
+ const char *host = http->request->header.getStr(Http::HdrType::HOST);
if (!host) {
// TODO: dump out the HTTP/1.1 error about missing host header.
#if FOLLOW_X_FORWARDED_FOR
if (!http->request->flags.doneFollowXff() &&
Config.accessList.followXFF &&
- http->request->header.has(HDR_X_FORWARDED_FOR)) {
+ http->request->header.has(Http::HdrType::X_FORWARDED_FOR)) {
/* we always trust the direct client address for actual use */
http->request->indirect_client_addr = http->request->client_addr;
http->request->indirect_client_addr.port(0);
/* setup the XFF iterator for processing */
- http->request->x_forwarded_for_iterator = http->request->header.getList(HDR_X_FORWARDED_FOR);
+ http->request->x_forwarded_for_iterator = http->request->header.getList(Http::HdrType::X_FORWARDED_FOR);
/* begin by checking to see if we trust direct client enough to walk XFF */
acl_checklist = clientAclChecklistCreate(Config.accessList.followXFF, http);
}
/* ACCESS_ALLOWED continues here ... */
- safe_free(http->uri);
-
- http->uri = xstrdup(urlCanonical(http->request));
-
+ xfree(http->uri);
+ http->uri = SBufToCstring(http->request->effectiveRequestUri());
http->doCallouts();
}
ih->rfc931 = getConn()->clientConnection->rfc931;
#if USE_OPENSSL
if (getConn()->clientConnection->isOpen()) {
- ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl);
+ ih->ssluser = sslGetUserEmail(fd_table[getConn()->clientConnection->fd].ssl.get());
}
#endif
}
if (answer == ACCESS_ALLOWED)
redirectStart(http, clientRedirectDoneWrapper, context);
else {
- Helper::Reply nilReply;
- nilReply.result = Helper::Error;
+ Helper::Reply const nilReply(Helper::Error);
context->clientRedirectDone(nilReply);
}
}
ClientRequestContext::clientRedirectStart()
{
debugs(33, 5, HERE << "'" << http->uri << "'");
- (void)SyncNotes(*http->al, *http->request);
+ http->al->syncNotes(http->request);
if (Config.accessList.redirector) {
acl_checklist = clientAclChecklistCreate(Config.accessList.redirector, http);
acl_checklist->nonBlockingCheck(clientRedirectAccessCheckDone, this);
storeIdStart(http, clientStoreIdDoneWrapper, context);
else {
debugs(85, 3, "access denied expected ERR reply handling: " << answer);
- Helper::Reply nilReply;
- nilReply.result = Helper::Error;
+ Helper::Reply const nilReply(Helper::Error);
context->clientStoreIdDone(nilReply);
}
}
* is already pinned if it was pinned earlier due to proxy auth
*/
if (!request->flags.connectionAuth) {
- if (req_hdr->has(HDR_AUTHORIZATION) || req_hdr->has(HDR_PROXY_AUTHORIZATION)) {
+ if (req_hdr->has(Http::HdrType::AUTHORIZATION) || req_hdr->has(Http::HdrType::PROXY_AUTHORIZATION)) {
HttpHeaderPos pos = HttpHeaderInitPos;
HttpHeaderEntry *e;
int may_pin = 0;
while ((e = req_hdr->getEntry(&pos))) {
- if (e->id == HDR_AUTHORIZATION || e->id == HDR_PROXY_AUTHORIZATION) {
+ if (e->id == Http::HdrType::AUTHORIZATION || e->id == Http::HdrType::PROXY_AUTHORIZATION) {
const char *value = e->value.rawBuf();
if (strncasecmp(value, "NTLM ", 5) == 0
||
strncasecmp(value, "Negotiate ", 10) == 0
||
strncasecmp(value, "Kerberos ", 9) == 0) {
- if (e->id == HDR_AUTHORIZATION) {
+ if (e->id == Http::HdrType::AUTHORIZATION) {
request->flags.connectionAuth = true;
may_pin = 1;
} else {
HttpRequest *request = http->request;
HttpHeader *req_hdr = &request->header;
bool no_cache = false;
- const char *str;
request->imslen = -1;
- request->ims = req_hdr->getTime(HDR_IF_MODIFIED_SINCE);
+ request->ims = req_hdr->getTime(Http::HdrType::IF_MODIFIED_SINCE);
if (request->ims > 0)
request->flags.ims = true;
no_cache=true;
// RFC 2616: treat Pragma:no-cache as if it was Cache-Control:no-cache when Cache-Control is missing
- } else if (req_hdr->has(HDR_PRAGMA))
- no_cache = req_hdr->hasListMember(HDR_PRAGMA,"no-cache",',');
-
- /*
- * Work around for supporting the Reload button in IE browsers when Squid
- * is used as an accelerator or transparent proxy, by turning accelerated
- * IMS request to no-cache requests. Now knows about IE 5.5 fix (is
- * actually only fixed in SP1, but we can't tell whether we are talking to
- * SP1 or not so all 5.5 versions are treated 'normally').
- */
- if (Config.onoff.ie_refresh) {
- if (http->flags.accel && request->flags.ims) {
- if ((str = req_hdr->getStr(HDR_USER_AGENT))) {
- if (strstr(str, "MSIE 5.01") != NULL)
- no_cache=true;
- else if (strstr(str, "MSIE 5.0") != NULL)
- no_cache=true;
- else if (strstr(str, "MSIE 4.") != NULL)
- no_cache=true;
- else if (strstr(str, "MSIE 3.") != NULL)
- no_cache=true;
- }
- }
- }
+ } else if (req_hdr->has(Http::HdrType::PRAGMA))
+ no_cache = req_hdr->hasListMember(Http::HdrType::PRAGMA,"no-cache",',');
}
if (request->method == Http::METHOD_OTHER) {
* If these headers appear on any other type of request, delete them now.
*/
else {
- req_hdr->delById(HDR_RANGE);
- req_hdr->delById(HDR_REQUEST_RANGE);
+ req_hdr->delById(Http::HdrType::RANGE);
+ req_hdr->delById(Http::HdrType::REQUEST_RANGE);
request->ignoreRange("neither HEAD nor GET");
}
- if (req_hdr->has(HDR_AUTHORIZATION))
+ if (req_hdr->has(Http::HdrType::AUTHORIZATION))
request->flags.auth = true;
clientCheckPinning(http);
if (!request->url.userInfo().isEmpty())
request->flags.auth = true;
- if (req_hdr->has(HDR_VIA)) {
- String s = req_hdr->getList(HDR_VIA);
+ if (req_hdr->has(Http::HdrType::VIA)) {
+ String s = req_hdr->getList(Http::HdrType::VIA);
/*
* ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
* Note ThisCache2 has a space prepended to the hostname so we don't
#if USE_FORW_VIA_DB
- if (req_hdr->has(HDR_X_FORWARDED_FOR)) {
- String s = req_hdr->getList(HDR_X_FORWARDED_FOR);
+ if (req_hdr->has(Http::HdrType::X_FORWARDED_FOR)) {
+ String s = req_hdr->getList(Http::HdrType::X_FORWARDED_FOR);
fvdbCountForw(s.termedBuf());
s.clean();
}
// Put helper response Notes into the transaction state record (ALE) eventually
// do it early to ensure that no matter what the outcome the notes are present.
- if (http->al != NULL)
- (void)SyncNotes(*http->al, *old_request);
+ if (http->al)
+ http->al->syncNotes(old_request);
UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
// XXX: validate the URL properly *without* generating a whole new request object right here.
// XXX: the clone() should be done only AFTER we know the new URL is valid.
HttpRequest *new_request = old_request->clone();
- if (urlParse(old_request->method, const_cast<char*>(urlNote), new_request)) {
- debugs(61,2, HERE << "URL-rewriter diverts URL from " << urlCanonical(old_request) << " to " << urlCanonical(new_request));
+ if (urlParse(old_request->method, const_cast<char*>(urlNote), *new_request)) {
+ debugs(61, 2, "URL-rewriter diverts URL from " << old_request->effectiveRequestUri() << " to " << new_request->effectiveRequestUri());
// update the new request to flag the re-writing was done on it
new_request->flags.redirected = true;
}
// update the current working ClientHttpRequest fields
- safe_free(http->uri);
- http->uri = xstrdup(urlCanonical(new_request));
+ xfree(http->uri);
+ http->uri = SBufToCstring(new_request->effectiveRequestUri());
HTTPMSGUNLOCK(old_request);
http->request = new_request;
HTTPMSGLOCK(http->request);
// Put helper response Notes into the transaction state record (ALE) eventually
// do it early to ensure that no matter what the outcome the notes are present.
- if (http->al != NULL)
- (void)SyncNotes(*http->al, *old_request);
+ if (http->al)
+ http->al->syncNotes(old_request);
UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
ClientRequestContext::checkNoCacheDone(const allow_t &answer)
{
acl_checklist = NULL;
- http->request->flags.cachable = (answer == ACCESS_ALLOWED);
+ if (answer == ACCESS_DENIED) {
+ http->request->flags.noCache = true; // dont read reply from cache
+ http->request->flags.cachable = false; // dont store reply into cache
+ }
http->doCallouts();
}
bool
ClientRequestContext::sslBumpAccessCheck()
{
- // If SSL connection tunneling or bumping decision has been made, obey it.
+ if (!http->getConn()) {
+ http->al->ssl.bumpMode = Ssl::bumpEnd; // SslBump does not apply; log -
+ return false;
+ }
+
const Ssl::BumpMode bumpMode = http->getConn()->sslBumpMode;
+ if (http->request->flags.forceTunnel) {
+ debugs(85, 5, "not needed; already decided to tunnel " << http->getConn());
+ if (bumpMode != Ssl::bumpEnd)
+ http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
+ return false;
+ }
+
+ // If SSL connection tunneling or bumping decision has been made, obey it.
if (bumpMode != Ssl::bumpEnd) {
debugs(85, 5, HERE << "SslBump already decided (" << bumpMode <<
"), " << "ignoring ssl_bump for " << http->getConn());
- if (!http->getConn()->serverBump())
+
+ // We need the following "if" for transparently bumped TLS connection,
+ // because in this case we are running ssl_bump access list before
+ // the doCallouts runs. It can be removed after the bug #4340 fixed.
+ // We do not want to proceed to bumping steps:
+ // - if the TLS connection with the client is already established
+ // because we are accepting normal HTTP requests on TLS port,
+ // or because of the client-first bumping mode
+ // - When the bumping is already started
+ if (!http->getConn()->switchedToHttps() &&
+ !http->getConn()->serverBump())
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed and not already bumped
http->al->ssl.bumpMode = bumpMode; // inherited from bumped connection
return false;
return false;
}
+ if (error) {
+ debugs(85, 5, "SslBump applies. Force bump action on error " << errorTypeName(error->type));
+ http->sslBumpNeed(Ssl::bumpBump);
+ http->al->ssl.bumpMode = Ssl::bumpBump;
+ return false;
+ }
+
debugs(85, 5, HERE << "SslBump possible, checking ACL");
ACLFilledChecklist *aclChecklist = clientAclChecklistCreate(Config.accessList.ssl_bump, http);
return;
const Ssl::BumpMode bumpMode = answer == ACCESS_ALLOWED ?
- static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpNone;
+ static_cast<Ssl::BumpMode>(answer.kind) : Ssl::bumpSplice;
http->sslBumpNeed(bumpMode); // for processRequest() to bump if needed
http->al->ssl.bumpMode = bumpMode; // for logging
+ if (bumpMode == Ssl::bumpTerminate) {
+ const Comm::ConnectionPointer clientConn = http->getConn() ? http->getConn()->clientConnection : nullptr;
+ if (Comm::IsConnOpen(clientConn)) {
+ debugs(85, 3, "closing after Ssl::bumpTerminate ");
+ clientConn->close();
+ }
+ return;
+ }
+
http->doCallouts();
}
#endif
{
debugs(85, 4, request->method << ' ' << uri);
- if (request->method == Http::METHOD_CONNECT && !redirect.status) {
+ const bool untouchedConnect = request->method == Http::METHOD_CONNECT && !redirect.status;
+
#if USE_OPENSSL
- if (sslBumpNeeded()) {
- sslBumpStart();
- return;
- }
+ if (untouchedConnect && sslBumpNeeded()) {
+ assert(!request->flags.forceTunnel);
+ sslBumpStart();
+ return;
+ }
#endif
+
+ if (untouchedConnect || request->flags.forceTunnel) {
getConn()->stopReading(); // tunnels read for themselves
- tunnelStart(this, &out.size, &al->http.code, al);
+ tunnelStart(this);
return;
}
{
PROF_start(httpStart);
logType = LOG_TAG_NONE;
- debugs(85, 4, LogTags_str[logType] << " for '" << uri << "'");
+ debugs(85, 4, logType.c_str() << " for '" << uri << "'");
/* no one should have touched this */
assert(out.offset == 0);
{
assert(calloutContext);
+ auto &ale = calloutContext->http->al;
/*Save the original request for logging purposes*/
- if (!calloutContext->http->al->request) {
- calloutContext->http->al->request = request;
- HTTPMSGLOCK(calloutContext->http->al->request);
+ if (!ale->request) {
+ ale->request = request;
+ HTTPMSGLOCK(ale->request);
- NotePairs ¬es = SyncNotes(*calloutContext->http->al, *calloutContext->http->request);
// Make the previously set client connection ID available as annotation.
if (ConnStateData *csd = calloutContext->http->getConn()) {
- if (!csd->connectionTag().isEmpty())
- notes.add("clt_conn_tag", SBuf(csd->connectionTag()).c_str());
+ if (!csd->notes()->empty())
+ calloutContext->http->request->notes()->appendNewOnly(csd->notes().getRaw());
}
+ ale->syncNotes(calloutContext->http->request);
}
if (!calloutContext->error) {
if (!calloutContext->redirect_done) {
calloutContext->redirect_done = true;
- assert(calloutContext->redirect_state == REDIRECT_NONE);
if (Config.Program.redirect) {
debugs(83, 3, HERE << "Doing calloutContext->clientRedirectStart()");
if (!calloutContext->store_id_done) {
calloutContext->store_id_done = true;
- assert(calloutContext->store_id_state == REDIRECT_NONE);
if (Config.Program.store_id) {
debugs(83, 3,"Doing calloutContext->clientStoreIdStart()");
}
#if USE_OPENSSL
- // We need to check for SslBump even if the calloutContext->error is set
- // because bumping may require delaying the error until after CONNECT.
+ // Even with calloutContext->error, we call sslBumpAccessCheck() to decide
+ // whether SslBump applies to this transaction. If it applies, we will
+ // attempt to bump the client to serve the error.
if (!calloutContext->sslBumpCheckDone) {
calloutContext->sslBumpCheckDone = true;
if (calloutContext->sslBumpAccessCheck())
#endif
if (calloutContext->error) {
- const char *storeUri = request->storeId();
- StoreEntry *e= storeCreateEntry(storeUri, storeUri, request->flags, request->method);
+ // XXX: prformance regression. c_str() reallocates
+ SBuf storeUriBuf(request->storeId());
+ const char *storeUri = storeUriBuf.c_str();
+ StoreEntry *e = storeCreateEntry(storeUri, storeUri, request->flags, request->method);
#if USE_OPENSSL
if (sslBumpNeeded()) {
// We have to serve an error, so bump the client first.
sslBumpNeed(Ssl::bumpClientFirst);
// set final error but delay sending until we bump
- Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e);
+ Ssl::ServerBump *srvBump = new Ssl::ServerBump(request, e, Ssl::bumpClientFirst);
errorAppendEntry(e, calloutContext->error);
calloutContext->error = NULL;
getConn()->setServerBump(srvBump);
repContext->setReplyToStoreEntry(e, "immediate SslBump error");
errorAppendEntry(e, calloutContext->error);
calloutContext->error = NULL;
- if (calloutContext->readNextRequest)
+ if (calloutContext->readNextRequest && getConn())
getConn()->flags.readMore = true; // resume any pipeline reads.
node = (clientStreamNode *)client_stream.tail->data;
clientStreamRead(node, this, node->readBuffer);
#endif
}
-#if !_USE_INLINE_
-#include "client_side_request.cci"
-#endif
-
#if USE_ADAPTATION
/// Initiate an asynchronous adaptation transaction which will call us back.
void
switch (answer.kind) {
case Adaptation::Answer::akForward:
- handleAdaptedHeader(const_cast<HttpMsg*>(answer.message.getRaw()));
+ handleAdaptedHeader(const_cast<Http::Message*>(answer.message.getRaw()));
break;
case Adaptation::Answer::akBlock:
}
void
-ClientHttpRequest::handleAdaptedHeader(HttpMsg *msg)
+ClientHttpRequest::handleAdaptedHeader(Http::Message *msg)
{
assert(msg);
HTTPMSGLOCK(request);
// update the new message to flag whether URL re-writing was done on it
- if (strcmp(urlCanonical(request),uri) != 0)
+ if (request->effectiveRequestUri().cmp(uri) != 0)
request->flags.redirected = 1;
/*
* Store the new URI for logging
*/
xfree(uri);
- uri = xstrdup(urlCanonical(request));
+ uri = SBufToCstring(request->effectiveRequestUri());
setLogUri(this, urlCanonicalClean(request));
assert(request->method.id());
} else if (HttpReply *new_rep = dynamic_cast<HttpReply*>(msg)) {
doCallouts();
}
+void
+ClientHttpRequest::callException(const std::exception &ex)
+{
+ if (const auto clientConn = getConn() ? getConn()->clientConnection : nullptr) {
+ if (Comm::IsConnOpen(clientConn)) {
+ debugs(85, 3, "closing after exception: " << ex.what());
+ clientConn->close(); // initiate orderly top-to-bottom cleanup
+ return;
+ }
+ }
+ debugs(85, DBG_IMPORTANT, "ClientHttpRequest exception without connection. Ignoring " << ex.what());
+ // XXX: Normally, we mustStop() but we cannot do that here because it is
+ // likely to leave Http::Stream and ConnStateData with a dangling http
+ // pointer. See r13480 or XXX in Http::Stream class description.
+}
#endif
// XXX: modify and use with ClientRequestContext::clientAccessCheckDone too.