]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/HttpRequest.cc
transaction_initiator ACL for detecting various unusual transactions
[thirdparty/squid.git] / src / HttpRequest.cc
index b09733a1f73e421742255599320d00b9aff80f3d..9b75838d1921e2b6d5a35206f3e2a1df3d1a7de1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 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 "acl/FilledChecklist.h"
 #include "client_side.h"
 #include "dns/LookupDetails.h"
+#include "Downloader.h"
 #include "err_detail_type.h"
 #include "globals.h"
 #include "gopher.h"
 #include "http.h"
 #include "http/one/RequestParser.h"
+#include "http/Stream.h"
 #include "HttpHdrCc.h"
 #include "HttpHeaderRange.h"
 #include "HttpRequest.h"
 #include "log/Config.h"
 #include "MemBuf.h"
+#include "sbuf/StringConvert.h"
 #include "SquidConfig.h"
 #include "Store.h"
 #include "URL.h"
 #include "adaptation/icap/icap_log.h"
 #endif
 
-HttpRequest::HttpRequest() :
-    HttpMsg(hoRequest)
+HttpRequest::HttpRequest(const MasterXaction::Pointer &mx) :
+    Http::Message(hoRequest),
+    masterXaction(mx)
 {
+    assert(mx);
     init();
 }
 
-HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath) :
-    HttpMsg(hoRequest)
+HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath, const MasterXaction::Pointer &mx) :
+    Http::Message(hoRequest),
+    masterXaction(mx)
 {
+    assert(mx);
     static unsigned int id = 1;
     debugs(93,7, HERE << "constructed, this=" << this << " id=" << ++id);
     init();
-    initHTTP(aMethod, aProtocol, aUrlpath);
+    initHTTP(aMethod, aProtocol, aSchemeImg, aUrlpath);
 }
 
 HttpRequest::~HttpRequest()
@@ -57,11 +64,11 @@ HttpRequest::~HttpRequest()
 }
 
 void
-HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath)
+HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath)
 {
     method = aMethod;
-    url.setScheme(aProtocol);
-    urlpath = aUrlpath;
+    url.setScheme(aProtocol, aSchemeImg);
+    url.path(aUrlpath);
 }
 
 void
@@ -69,14 +76,9 @@ HttpRequest::init()
 {
     method = Http::METHOD_NONE;
     url.clear();
-    urlpath = NULL;
-    host[0] = '\0';
-    host_is_numeric = -1;
 #if USE_AUTH
     auth_user_request = NULL;
 #endif
-    port = 0;
-    canonical = NULL;
     memset(&flags, '\0', sizeof(flags));
     range = NULL;
     ims = -1;
@@ -92,7 +94,7 @@ HttpRequest::init()
     peer_login = NULL;      // not allocated/deallocated by this class
     peer_domain = NULL;     // not allocated/deallocated by this class
     peer_host = NULL;
-    vary_headers = NULL;
+    vary_headers = SBuf();
     myportname = null_string;
     tag = null_string;
 #if USE_AUTH
@@ -101,7 +103,7 @@ HttpRequest::init()
 #endif
     extacl_log = null_string;
     extacl_message = null_string;
-    pstate = psReadyToParseStartLine;
+    pstate = Http::Message::psReadyToParseStartLine;
 #if FOLLOW_X_FORWARDED_FOR
     indirect_client_addr.setEmpty();
 #endif /* FOLLOW_X_FORWARDED_FOR */
@@ -124,12 +126,8 @@ HttpRequest::clean()
 #if USE_AUTH
     auth_user_request = NULL;
 #endif
-    safe_free(canonical);
-
-    safe_free(vary_headers);
-
+    vary_headers.clear();
     url.clear();
-    urlpath.clean();
 
     header.clean();
 
@@ -145,7 +143,7 @@ HttpRequest::clean()
 
     myportname.clean();
 
-    notes = NULL;
+    theNotes = nullptr;
 
     tag.clean();
 #if USE_AUTH
@@ -176,7 +174,8 @@ HttpRequest::reset()
 HttpRequest *
 HttpRequest::clone() const
 {
-    HttpRequest *copy = new HttpRequest(method, url.getScheme(), urlpath.termedBuf());
+    HttpRequest *copy = new HttpRequest(masterXaction);
+    copy->method = method;
     // TODO: move common cloning clone to Msg::copyTo() or copy ctor
     copy->header.append(&header);
     copy->hdrCacheInit();
@@ -185,13 +184,7 @@ HttpRequest::clone() const
     copy->pstate = pstate; // TODO: should we assert a specific state here?
     copy->body_pipe = body_pipe;
 
-    copy->url.userInfo(url.userInfo());
-    strncpy(copy->host, host, sizeof(host)); // SQUIDHOSTNAMELEN
-    copy->host_addr = host_addr;
-
-    copy->port = port;
-    // urlPath handled in ctor
-    copy->canonical = canonical ? xstrdup(canonical) : NULL;
+    copy->url = url;
 
     // range handled in hdrCacheInit()
     copy->ims = ims;
@@ -204,7 +197,7 @@ HttpRequest::clone() const
 
     copy->lastmod = lastmod;
     copy->etag = etag;
-    copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
+    copy->vary_headers = vary_headers;
     // XXX: what to do with copy->peer_domain?
 
     copy->tag = tag;
@@ -218,7 +211,7 @@ HttpRequest::clone() const
 }
 
 bool
-HttpRequest::inheritProperties(const HttpMsg *aMsg)
+HttpRequest::inheritProperties(const Http::Message *aMsg)
 {
     const HttpRequest* aReq = dynamic_cast<const HttpRequest*>(aMsg);
     if (!aReq)
@@ -258,7 +251,11 @@ HttpRequest::inheritProperties(const HttpMsg *aMsg)
     // main property is which connection the request was received on (if any)
     clientConnectionManager = aReq->clientConnectionManager;
 
-    notes = aReq->notes;
+    downloader = aReq->downloader;
+
+    theNotes = aReq->theNotes;
+
+    sources = aReq->sources;
     return true;
 }
 
@@ -334,64 +331,43 @@ HttpRequest::parseFirstLine(const char *start, const char *end)
 
     * (char *) end = '\0';     // temp terminate URI, XXX dangerous?
 
-    HttpRequest *tmp = urlParse(method, (char *) start, this);
+    const bool ret = urlParse(method, (char *) start, *this);
 
     * (char *) end = save;
 
-    if (NULL == tmp)
-        return false;
-
-    return true;
-}
-
-bool
-HttpRequest::parseHeader(Http1::RequestParser &hp)
-{
-    // HTTP/1 message contains "zero or more header fields"
-    // zero does not need parsing
-    if (!hp.headerBlockSize())
-        return true;
-
-    // XXX: c_str() reallocates. performance regression.
-    const bool result = header.parse(hp.mimeHeader().c_str(), hp.headerBlockSize());
-
-    if (result)
-        hdrCacheInit();
-
-    return result;
+    return ret;
 }
 
 /* swaps out request using httpRequestPack */
 void
 HttpRequest::swapOut(StoreEntry * e)
 {
-    Packer p;
     assert(e);
-    packerToStoreInit(&p, e);
-    pack(&p);
-    packerClean(&p);
+    e->buffer();
+    pack(e);
+    e->flush();
 }
 
 /* packs request-line and headers, appends <crlf> terminator */
 void
-HttpRequest::pack(Packer * p)
+HttpRequest::pack(Packable * p) const
 {
     assert(p);
     /* pack request-line */
-    packerPrintf(p, SQUIDSBUFPH " " SQUIDSTRINGPH " HTTP/%d.%d\r\n",
-                 SQUIDSBUFPRINT(method.image()), SQUIDSTRINGPRINT(urlpath),
-                 http_ver.major, http_ver.minor);
+    p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
+               SQUIDSBUFPRINT(method.image()), SQUIDSBUFPRINT(url.path()),
+               http_ver.major, http_ver.minor);
     /* headers */
     header.packInto(p);
     /* trailer */
-    packerAppend(p, "\r\n", 2);
+    p->append("\r\n", 2);
 }
 
 /*
  * A wrapper for debugObj()
  */
 void
-httpRequestPack(void *obj, Packer *p)
+httpRequestPack(void *obj, Packable *p)
 {
     HttpRequest *request = static_cast<HttpRequest*>(obj);
     request->pack(p);
@@ -399,10 +375,10 @@ httpRequestPack(void *obj, Packer *p)
 
 /* returns the length of request line + headers + crlf */
 int
-HttpRequest::prefixLen()
+HttpRequest::prefixLen() const
 {
     return method.image().length() + 1 +
-           urlpath.size() + 1 +
+           url.path().length() + 1 +
            4 + 1 + 3 + 2 +
            header.len + 2;
 }
@@ -411,7 +387,7 @@ HttpRequest::prefixLen()
 void
 HttpRequest::hdrCacheInit()
 {
-    HttpMsg::hdrCacheInit();
+    Http::Message::hdrCacheInit();
 
     assert(!range);
     range = header.getRange();
@@ -497,24 +473,16 @@ HttpRequest::clearError()
     errDetail = ERR_DETAIL_NONE;
 }
 
-const char *HttpRequest::packableURI(bool full_uri) const
+void
+HttpRequest::packFirstLineInto(Packable * p, bool full_uri) const
 {
-    if (full_uri)
-        return urlCanonical((HttpRequest*)this);
-
-    if (urlpath.size())
-        return urlpath.termedBuf();
+    const SBuf tmp(full_uri ? effectiveRequestUri() : url.path());
 
-    return "/";
-}
-
-void HttpRequest::packFirstLineInto(Packer * p, bool full_uri) const
-{
     // form HTTP request-line
-    packerPrintf(p, SQUIDSBUFPH " %s HTTP/%d.%d\r\n",
-                 SQUIDSBUFPRINT(method.image()),
-                 packableURI(full_uri),
-                 http_ver.major, http_ver.minor);
+    p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
+               SQUIDSBUFPRINT(method.image()),
+               SQUIDSBUFPRINT(tmp),
+               http_ver.major, http_ver.minor);
 }
 
 /*
@@ -551,20 +519,12 @@ HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &theSize) const
  * If the request cannot be created cleanly, NULL is returned
  */
 HttpRequest *
-HttpRequest::CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method)
+HttpRequest::FromUrl(char * url, const MasterXaction::Pointer &mx, const HttpRequestMethod& method)
 {
-    return urlParse(method, url, NULL);
-}
-
-/*
- * Create a Request from a URL.
- *
- * If the request cannot be created cleanly, NULL is returned
- */
-HttpRequest *
-HttpRequest::CreateFromUrl(char * url)
-{
-    return urlParse(Http::METHOD_GET, url, NULL);
+    std::unique_ptr<HttpRequest> req(new HttpRequest(mx));
+    if (urlParse(method, url, *req))
+        return req.release();
+    return nullptr;
 }
 
 /**
@@ -587,8 +547,13 @@ HttpRequest::maybeCacheable()
         if (!method.respMaybeCacheable())
             return false;
 
-        // XXX: this would seem the correct place to detect request cache-controls
-        //      no-store, private and related which block cacheability
+        // RFC 7234 section 5.2.1.5:
+        // "cache MUST NOT store any part of either this request or any response to it"
+        //
+        // NP: refresh_pattern ignore-no-store only applies to response messages
+        //     this test is handling request message CC header.
+        if (!flags.ignoreCc && cache_control && cache_control->hasNoStore())
+            return false;
         break;
 
     case AnyP::PROTO_GOPHER:
@@ -611,8 +576,8 @@ bool
 HttpRequest::conditional() const
 {
     return flags.ims ||
-           header.has(HDR_IF_MATCH) ||
-           header.has(HDR_IF_NONE_MATCH);
+           header.has(Http::HdrType::IF_MATCH) ||
+           header.has(Http::HdrType::IF_NONE_MATCH);
 }
 
 void
@@ -671,8 +636,8 @@ bool
 HttpRequest::canHandle1xx() const
 {
     // old clients do not support 1xx unless they sent Expect: 100-continue
-    // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
-    if (http_ver <= Http::ProtocolVersion(1,0) && !header.has(HDR_EXPECT))
+    // (we reject all other Http::HdrType::EXPECT values so just check for Http::HdrType::EXPECT)
+    if (http_ver <= Http::ProtocolVersion(1,0) && !header.has(Http::HdrType::EXPECT))
         return false;
 
     // others must support 1xx control messages
@@ -687,16 +652,44 @@ HttpRequest::pinnedConnection()
     return NULL;
 }
 
-const char *
+const SBuf
 HttpRequest::storeId()
 {
     if (store_id.size() != 0) {
-        debugs(73, 3, "sent back store_id:" << store_id);
-
-        return store_id.termedBuf();
+        debugs(73, 3, "sent back store_id: " << store_id);
+        return StringToSBuf(store_id);
     }
-    debugs(73, 3, "sent back canonicalUrl:" << urlCanonical(this) );
+    debugs(73, 3, "sent back effectiveRequestUrl: " << effectiveRequestUri());
+    return effectiveRequestUri();
+}
+
+const SBuf &
+HttpRequest::effectiveRequestUri() const
+{
+    if (method.id() == Http::METHOD_CONNECT || url.getScheme() == AnyP::PROTO_AUTHORITY_FORM)
+        return url.authority(true); // host:port
+    return url.absolute();
+}
+
+NotePairs::Pointer
+HttpRequest::notes()
+{
+    if (!theNotes)
+        theNotes = new NotePairs;
+    return theNotes;
+}
 
-    return urlCanonical(this);
+void
+UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
+{
+    // Tag client connection if the helper responded with clt_conn_tag=tag.
+    const char *cltTag = "clt_conn_tag";
+    if (const char *connTag = helperNotes.findFirst(cltTag)) {
+        if (csd) {
+            csd->notes()->remove(cltTag);
+            csd->notes()->add(cltTag, connTag);
+        }
+    }
+    request.notes()->replaceOrAdd(&helperNotes);
 }