On 09.02.2017 20:19, Amos Jeffries wrote:

> Since Via is a list header we should be able to just append a new Via
> header to the header list with putStr. No need to use getList, String,
> delById to inject on the end of existing Via string.

Doing so will change the Via generation way currently used. Instead of
generating a single Via header with comma-separated value list Squid
would produce several Via header fields. Though this is equivalent from
the RFC point of view, I would prefer to leave these things "as is" for now.

> Please also take the opportunity to fix a long standing protocol
> violation bug in Via header produced by Squid:
> The version part of the header is only supposed to elide the protocol
> label if the that label would be "HTTP/" (ie. for messages received via
> HTTP and HTTPS).
>
> eg. for ICY replies:
>   Via: ICY/1.1 squid
>
> eg. for FTP gatewayed replies (or relayed requests):
>   Via: FTP/2 squid

Done.

> After those calls that require String are gone please assemble the value
> in an SBuf instead of static char* buffer.

Done.

> Also, for messages where Squid itself generated the message (ie
> Downloader or ESI) there should be no Via header added at all.

Marked as TODO.


Eduard.

Fixed appending Http::HdrType::VIA code duplication.

=== modified file 'src/HttpHeader.cc'
--- src/HttpHeader.cc	2017-01-25 22:29:03 +0000
+++ src/HttpHeader.cc	2017-02-11 19:30:36 +0000
@@ -976,60 +976,83 @@
     const char *item;
     int ilen;
     int mlen = strlen(member);
 
     assert(any_registered_header(id));
 
     header = getStrOrList(id);
     String result;
 
     while (strListGetItem(&header, separator, &item, &ilen, &pos)) {
         if (strncmp(item, member, mlen) == 0 && item[mlen] == '=') {
             result.append(item + mlen + 1, ilen - mlen - 1);
             break;
         }
     }
 
     header.clean();
     return result;
 }
 
 /* test if a field is present */
 int
 HttpHeader::has(Http::HdrType id) const
 {
     assert(any_registered_header(id));
     debugs(55, 9, this << " lookup for " << id);
     return CBIT_TEST(mask, id);
 }
 
 void
+HttpHeader::addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from)
+{
+    // TODO: do not add Via header for messages where Squid itself
+    // generated the message (i.e., Downloader or ESI) there should be no Via header added at all.
+
+    if (Config.onoff.via) {
+        SBuf buf;
+        // RFC 7230 section 5.7.1.: protocol-name is omitted when
+        // the received protocol is HTTP.
+        if (ver.protocol > AnyP::PROTO_NONE && ver.protocol < AnyP::PROTO_UNKNOWN &&
+                ver.protocol != AnyP::PROTO_HTTP && ver.protocol != AnyP::PROTO_HTTPS)
+            buf.appendf("%s/", AnyP::ProtocolType_str[ver.protocol]);
+        buf.appendf("%d.%d %s", ver.major, ver.minor, ThisCache);
+        String strVia;
+        const HttpHeader *hdr = from ? from : this;
+        hdr->getList(Http::HdrType::VIA, &strVia);
+        strListAdd(&strVia, buf.c_str(), ',');
+        delById(Http::HdrType::VIA);
+        putStr(Http::HdrType::VIA, strVia.termedBuf());
+    }
+}
+
+void
 HttpHeader::putInt(Http::HdrType id, int number)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt);  /* must be of an appropriate type */
     assert(number >= 0);
     addEntry(new HttpHeaderEntry(id, NULL, xitoa(number)));
 }
 
 void
 HttpHeader::putInt64(Http::HdrType id, int64_t number)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftInt64);    /* must be of an appropriate type */
     assert(number >= 0);
     addEntry(new HttpHeaderEntry(id, NULL, xint64toa(number)));
 }
 
 void
 HttpHeader::putTime(Http::HdrType id, time_t htime)
 {
     assert(any_registered_header(id));
     assert(Http::HeaderLookupTable.lookup(id).type == Http::HdrFieldType::ftDate_1123);    /* must be of an appropriate type */
     assert(htime >= 0);
     addEntry(new HttpHeaderEntry(id, NULL, mkrfc1123(htime)));
 }
 
 void
 HttpHeader::putStr(Http::HdrType id, const char *str)
 {
     assert(any_registered_header(id));

=== modified file 'src/HttpHeader.h'
--- src/HttpHeader.h	2017-01-01 00:12:22 +0000
+++ src/HttpHeader.h	2017-02-11 16:53:40 +0000
@@ -1,41 +1,42 @@
 /*
  * 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.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_HTTPHEADER_H
 #define SQUID_HTTPHEADER_H
 
+#include "anyp/ProtocolVersion.h"
 #include "base/LookupTable.h"
 #include "http/RegisteredHeaders.h"
 /* because we pass a spec by value */
 #include "HttpHeaderMask.h"
 #include "mem/forward.h"
 #include "sbuf/forward.h"
 #include "SquidString.h"
 
 #include <vector>
 
 /* class forward declarations */
 class HttpHdrCc;
 class HttpHdrContRange;
 class HttpHdrRange;
 class HttpHdrSc;
 class Packable;
 
 /** Possible owners of http header */
 typedef enum {
     hoNone =0,
 #if USE_HTCP
     hoHtcpReply,
 #endif
     hoRequest,
     hoReply,
 #if USE_OPENSSL
     hoErrorDetail,
 #endif
     hoEnd
 } http_hdr_owner_type;
@@ -86,60 +87,63 @@
     int parse(const char *header_start, size_t len);
     /// Parses headers stored in a buffer.
     /// \returns 1 and sets hdr_sz on success
     /// \returns 0 when needs more data
     /// \returns -1 on error
     int parse(const char *buf, size_t buf_len, bool atEnd, size_t &hdr_sz);
     void packInto(Packable * p, bool mask_sensitive_info=false) const;
     HttpHeaderEntry *getEntry(HttpHeaderPos * pos) const;
     HttpHeaderEntry *findEntry(Http::HdrType id) const;
     int delByName(const char *name);
     int delById(Http::HdrType id);
     void delAt(HttpHeaderPos pos, int &headers_deleted);
     void refreshMask();
     void addEntry(HttpHeaderEntry * e);
     void insertEntry(HttpHeaderEntry * e);
     String getList(Http::HdrType id) const;
     bool getList(Http::HdrType id, String *s) const;
     bool conflictingContentLength() const { return conflictingContentLength_; }
     String getStrOrList(Http::HdrType id) const;
     String getByName(const SBuf &name) const;
     String getByName(const char *name) const;
     String getById(Http::HdrType id) const;
     /// sets value and returns true iff a [possibly empty] field identified by id is there
     bool getByIdIfPresent(Http::HdrType id, String &result) const;
     /// sets value and returns true iff a [possibly empty] named field is there
     bool getByNameIfPresent(const SBuf &s, String &value) const;
     bool getByNameIfPresent(const char *name, int namelen, String &value) const;
     String getByNameListMember(const char *name, const char *member, const char separator) const;
     String getListMember(Http::HdrType id, const char *member, const char separator) const;
     int has(Http::HdrType id) const;
+    /// Appends "this cache" information to VIA header field.
+    /// Takes the initial VIA value from "from" parameter, if provided.
+    void addVia(const AnyP::ProtocolVersion &ver, const HttpHeader *from = 0);
     void putInt(Http::HdrType id, int number);
     void putInt64(Http::HdrType id, int64_t number);
     void putTime(Http::HdrType id, time_t htime);
     void putStr(Http::HdrType id, const char *str);
     void putAuth(const char *auth_scheme, const char *realm);
     void putCc(const HttpHdrCc * cc);
     void putContRange(const HttpHdrContRange * cr);
     void putRange(const HttpHdrRange * range);
     void putSc(HttpHdrSc *sc);
     void putWarning(const int code, const char *const text); ///< add a Warning header
     void putExt(const char *name, const char *value);
     int getInt(Http::HdrType id) const;
     int64_t getInt64(Http::HdrType id) const;
     time_t getTime(Http::HdrType id) const;
     const char *getStr(Http::HdrType id) const;
     const char *getLastStr(Http::HdrType id) const;
     HttpHdrCc *getCc() const;
     HttpHdrRange *getRange() const;
     HttpHdrSc *getSc() const;
     HttpHdrContRange *getContRange() const;
     const char *getAuth(Http::HdrType id, const char *auth_scheme) const;
     ETag getETag(Http::HdrType id) const;
     TimeOrTag getTimeOrTag(Http::HdrType id) const;
     int hasListMember(Http::HdrType id, const char *member, const char separator) const;
     int hasByNameListMember(const char *name, const char *member, const char separator) const;
     void removeHopByHopEntries();
     inline bool chunked() const; ///< whether message uses chunked Transfer-Encoding
 
     /* protected, do not use these, use interface functions instead */
     std::vector<HttpHeaderEntry *> entries;     /**< parsed fields in raw format */

=== modified file 'src/client_side_reply.cc'
--- src/client_side_reply.cc	2017-01-01 00:12:22 +0000
+++ src/client_side_reply.cc	2017-02-11 16:53:40 +0000
@@ -1567,73 +1567,62 @@
     } else if (reply->bodySize(request->method) < 0 && !maySendChunkedReply) {
         debugs(88, 3, "clientBuildReplyHeader: can't keep-alive, unknown body size" );
         request->flags.proxyKeepalive = false;
     } else if (fdUsageHigh()&& !request->flags.mustKeepalive) {
         debugs(88, 3, "clientBuildReplyHeader: Not many unused FDs, can't keep-alive");
         request->flags.proxyKeepalive = false;
     } else if (request->flags.sslBumped && !reply->persistent()) {
         // We do not really have to close, but we pretend we are a tunnel.
         debugs(88, 3, "clientBuildReplyHeader: bumped reply forces close");
         request->flags.proxyKeepalive = false;
     } else if (request->pinnedConnection() && !reply->persistent()) {
         // The peer wants to close the pinned connection
         debugs(88, 3, "pinned reply forces close");
         request->flags.proxyKeepalive = false;
     } else if (http->getConn()) {
         ConnStateData * conn = http->getConn();
         if (!Comm::IsConnOpen(conn->port->listenConn)) {
             // The listening port closed because of a reconfigure
             debugs(88, 3, "listening port closed");
             request->flags.proxyKeepalive = false;
         }
     }
 
     // Decide if we send chunked reply
     if (maySendChunkedReply && reply->bodySize(request->method) < 0) {
         debugs(88, 3, "clientBuildReplyHeader: chunked reply");
         request->flags.chunkedReply = true;
         hdr->putStr(Http::HdrType::TRANSFER_ENCODING, "chunked");
     }
 
-    /* Append VIA */
-    if (Config.onoff.via) {
-        LOCAL_ARRAY(char, bbuf, MAX_URL + 32);
-        String strVia;
-        hdr->getList(Http::HdrType::VIA, &strVia);
-        snprintf(bbuf, MAX_URL + 32, "%d.%d %s",
-                 reply->sline.version.major,
-                 reply->sline.version.minor,
-                 ThisCache);
-        strListAdd(&strVia, bbuf, ',');
-        hdr->delById(Http::HdrType::VIA);
-        hdr->putStr(Http::HdrType::VIA, strVia.termedBuf());
-    }
+    hdr->addVia(reply->sline.version);
+
     /* Signal keep-alive or close explicitly */
     hdr->putStr(Http::HdrType::CONNECTION, request->flags.proxyKeepalive ? "keep-alive" : "close");
 
 #if ADD_X_REQUEST_URI
     /*
      * Knowing the URI of the request is useful when debugging persistent
      * connections in a client; we cannot guarantee the order of http headers,
      * but X-Request-URI is likely to be the very last header to ease use from a
      * debugger [hdr->entries.count-1].
      */
     hdr->putStr(Http::HdrType::X_REQUEST_URI,
                 http->memOjbect()->url ? http->memObject()->url : http->uri);
 
 #endif
 
     /* Surrogate-Control requires Surrogate-Capability from upstream to pass on */
     if ( hdr->has(Http::HdrType::SURROGATE_CONTROL) ) {
         if (!request->header.has(Http::HdrType::SURROGATE_CAPABILITY)) {
             hdr->delById(Http::HdrType::SURROGATE_CONTROL);
         }
         /* TODO: else case: drop any controls intended specifically for our surrogate ID */
     }
 
     httpHdrMangleList(hdr, request, http->al, ROR_REPLY);
 }
 
 void
 clientReplyContext::cloneReply()
 {
     assert(reply == NULL);

=== modified file 'src/http.cc'
--- src/http.cc	2017-01-01 00:12:22 +0000
+++ src/http.cc	2017-02-11 16:53:40 +0000
@@ -1786,71 +1786,61 @@
 
     /* use our IMS header if the cached entry has Last-Modified time */
     if (request->lastmod > -1)
         hdr_out->putTime(Http::HdrType::IF_MODIFIED_SINCE, request->lastmod);
 
     // Add our own If-None-Match field if the cached entry has a strong ETag.
     // copyOneHeaderFromClientsideRequestToUpstreamRequest() adds client ones.
     if (request->etag.size() > 0) {
         hdr_out->addEntry(new HttpHeaderEntry(Http::HdrType::IF_NONE_MATCH, NULL,
                                               request->etag.termedBuf()));
     }
 
     bool we_do_ranges = decideIfWeDoRanges (request);
 
     String strConnection (hdr_in->getList(Http::HdrType::CONNECTION));
 
     while ((e = hdr_in->getEntry(&pos)))
         copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags);
 
     /* Abstraction break: We should interpret multipart/byterange responses
      * into offset-length data, and this works around our inability to do so.
      */
     if (!we_do_ranges && request->multipartRangeRequest()) {
         /* don't cache the result */
         request->flags.cachable = false;
         /* pretend it's not a range request */
         request->ignoreRange("want to request the whole object");
         request->flags.isRanged = false;
     }
 
-    /* append Via */
-    if (Config.onoff.via) {
-        String strVia;
-        strVia = hdr_in->getList(Http::HdrType::VIA);
-        snprintf(bbuf, BBUF_SZ, "%d.%d %s",
-                 request->http_ver.major,
-                 request->http_ver.minor, ThisCache);
-        strListAdd(&strVia, bbuf, ',');
-        hdr_out->putStr(Http::HdrType::VIA, strVia.termedBuf());
-        strVia.clean();
-    }
+    hdr_out->addVia(request->http_ver, hdr_in);
 
     if (request->flags.accelerated) {
         /* Append Surrogate-Capabilities */
         String strSurrogate(hdr_in->getList(Http::HdrType::SURROGATE_CAPABILITY));
 #if USE_SQUID_ESI
         snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
 #else
         snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0\"", Config.Accel.surrogate_id);
 #endif
         strListAdd(&strSurrogate, bbuf, ',');
         hdr_out->putStr(Http::HdrType::SURROGATE_CAPABILITY, strSurrogate.termedBuf());
     }
 
     /** \pre Handle X-Forwarded-For */
     if (strcmp(opt_forwarded_for, "delete") != 0) {
 
         String strFwd = hdr_in->getList(Http::HdrType::X_FORWARDED_FOR);
 
         // if we cannot double strFwd size, then it grew past 50% of the limit
         if (!strFwd.canGrowBy(strFwd.size())) {
             // There is probably a forwarding loop with Via detection disabled.
             // If we do nothing, String will assert on overflow soon.
             // TODO: Terminate all transactions with huge XFF?
             strFwd = "error";
 
             static int warnedCount = 0;
             if (warnedCount++ < 100) {
                 const SBuf url(entry ? SBuf(entry->url()) : request->effectiveRequestUri());
                 debugs(11, DBG_IMPORTANT, "Warning: likely forwarding loop with " << url);
             }

_______________________________________________
squid-dev mailing list
squid-dev@lists.squid-cache.org
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to