]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Better support for unknown URL schemes
authorAmos Jeffries <squid3@treenet.co.nz>
Wed, 17 Aug 2016 00:38:25 +0000 (12:38 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Wed, 17 Aug 2016 00:38:25 +0000 (12:38 +1200)
Squid already contains AnyP::PROTO_UNKNOWN support for unknown protocols
but currently does not preserve the actual string value received for them.

This adds a textual representation ('image') to the UriScheme object to
fill that gap and ensure that all URL representations (ie cache keys,
logs and outgoing messages) are generated with the scheme string as it
was received rather than implicitly via a registered protocol type.

Future work:
* add ACL support for arbitrary scheme names
* support for comparisons of unknown schemes

15 files changed:
src/HttpRequest.cc
src/HttpRequest.h
src/PeerPoolMgr.cc
src/URL.h
src/anyp/UriScheme.cc
src/anyp/UriScheme.h
src/cache_cf.cc
src/carp.cc
src/client_side.cc
src/client_side_reply.cc
src/errorpage.cc
src/format/Format.cc
src/tests/stub_HttpRequest.cc
src/tests/testUriScheme.cc
src/url.cc

index 8b926323b0cb4bf9a8c2dd42aa50c25d0fa664d1..9ef416d22d642b8560ce7ac16c14c24a547fffc3 100644 (file)
@@ -44,13 +44,13 @@ HttpRequest::HttpRequest() :
     init();
 }
 
-HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath) :
+HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aSchemeImg, const char *aUrlpath) :
     HttpMsg(hoRequest)
 {
     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()
@@ -60,10 +60,10 @@ 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);
+    url.setScheme(aProtocol, aSchemeImg);
     url.path(aUrlpath);
 }
 
@@ -180,11 +180,7 @@ HttpRequest::clone() const
     copy->pstate = pstate; // TODO: should we assert a specific state here?
     copy->body_pipe = body_pipe;
 
-    copy->url.setScheme(url.getScheme());
-    copy->url.userInfo(url.userInfo());
-    copy->url.host(url.host());
-    copy->url.port(url.port());
-    copy->url.path(url.path());
+    copy->url = url;
 
     // range handled in hdrCacheInit()
     copy->ims = ims;
index 8d9702ac58beda98f3e51b990a4eefeb06f2cf92..86444ae7839b66cda7124f2b7fe47f2b37b4c241 100644 (file)
@@ -49,11 +49,11 @@ public:
     typedef RefCount<HttpRequest> Pointer;
 
     HttpRequest();
-    HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath);
+    HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *schemeImage, const char *aUrlpath);
     ~HttpRequest();
     virtual void reset();
 
-    void initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath);
+    void initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *schemeImage, const char *aUrlpath);
 
     virtual HttpRequest *clone() const;
 
index 1733e16f7010cef444ef3fdc2cc7a60b84f8077d..9fcb4c682a49cef7f59fdfd8b98a25c821d669cc 100644 (file)
@@ -61,7 +61,7 @@ PeerPoolMgr::start()
 
     // ErrorState, getOutgoingAddress(), and other APIs may require a request.
     // We fake one. TODO: Optionally send this request to peers?
-    request = new HttpRequest(Http::METHOD_OPTIONS, AnyP::PROTO_HTTP, "*");
+    request = new HttpRequest(Http::METHOD_OPTIONS, AnyP::PROTO_HTTP, "http", "*");
     request->url.host(peer->host);
 
     checkpoint("peer initialized");
index 7217663e3a02975aff6a006d9ff5122c85ae90f3..9495a7f62d3dfbadabc5321dab857fc657792506 100644 (file)
--- a/src/URL.h
+++ b/src/URL.h
@@ -28,6 +28,20 @@ class URL
 public:
     URL() : hostIsNumeric_(false), port_(0) {*host_=0;}
     URL(AnyP::UriScheme const &aScheme);
+    URL(const URL &other) {
+        this->operator =(other);
+    }
+    URL &operator =(const URL &o) {
+        scheme_ = o.scheme_;
+        userInfo_ = o.userInfo_;
+        memcpy(host_, o.host_, sizeof(host_));
+        hostIsNumeric_ = o.hostIsNumeric_;
+        hostAddr_ = o.hostAddr_;
+        port_ = o.port_;
+        path_ = o.path_;
+        touch();
+        return *this;
+    }
 
     void clear() {
         scheme_=AnyP::PROTO_NONE;
@@ -42,7 +56,10 @@ public:
     AnyP::UriScheme const & getScheme() const {return scheme_;}
 
     /// convert the URL scheme to that given
-    void setScheme(const AnyP::ProtocolType &p) {scheme_=p; touch();}
+    void setScheme(const AnyP::ProtocolType &p, const char *str) {
+        scheme_ = AnyP::UriScheme(p, str);
+        touch();
+    }
 
     void userInfo(const SBuf &s) {userInfo_=s; touch();}
     const SBuf &userInfo() const {return userInfo_;}
@@ -131,9 +148,17 @@ private:
 inline std::ostream &
 operator <<(std::ostream &os, const URL &url)
 {
-    if (const char *sc = url.getScheme().c_str())
-        os << sc << ":";
-    os << "//" << url.authority() << url.path();
+    // none means explicit empty string for scheme.
+    if (url.getScheme() != AnyP::PROTO_NONE)
+        os << url.getScheme().image();
+    os << ":";
+
+    // no authority section on URN
+    if (url.getScheme() != AnyP::PROTO_URN)
+        os << "//" << url.authority();
+
+    // path is what it is - including absent
+    os << url.path();
     return os;
 }
 
index a802f615e12c06afacadb453e677d04b621a2847..8f1ff4972ae87c8b3616a31a7bc15f1dc8b78039 100644 (file)
 #include "squid.h"
 #include "anyp/UriScheme.h"
 
-char const *
-AnyP::UriScheme::c_str() const
+AnyP::UriScheme::UriScheme(AnyP::ProtocolType const aScheme, const char *img) :
+    theScheme_(aScheme)
 {
-    if (theScheme_ == AnyP::PROTO_UNKNOWN)
-        return "(unknown)";
-
-    static char out[BUFSIZ];
-    int p = 0;
-
-    if (theScheme_ > AnyP::PROTO_NONE && theScheme_ < AnyP::PROTO_MAX) {
-        const char *in = AnyP::ProtocolType_str[theScheme_];
-        for (; p < (BUFSIZ-1) && in[p] != '\0'; ++p)
-            out[p] = xtolower(in[p]);
+    if (img)
+        // image could be provided explicitly (case-sensitive)
+        image_ = img;
+
+    else if (theScheme_ == AnyP::PROTO_UNKNOWN)
+        // image could be actually unknown and not provided
+        image_ = "(unknown)";
+
+    else if (theScheme_ > AnyP::PROTO_NONE && theScheme_ < AnyP::PROTO_MAX) {
+        // image could be implied by a registered transfer protocol
+        // which use upper-case labels, so down-case for scheme image
+        image_ = AnyP::ProtocolType_str[theScheme_];
+        image_.toLower();
     }
-    out[p] = '\0';
-    return out;
+    // else, image is an empty string ("://example.com/")
 }
 
 unsigned short
index 4cd03af83d61b4d8f37ded4a05139f4fb53290d0..d20299f2936a2b177a9b05c7e45f731d20eda426 100644 (file)
@@ -10,6 +10,7 @@
 #define SQUID_ANYP_URISCHEME_H
 
 #include "anyp/ProtocolType.h"
+#include "sbuf/SBuf.h"
 
 #include <iosfwd>
 
@@ -23,27 +24,35 @@ class UriScheme
 {
 public:
     UriScheme() : theScheme_(AnyP::PROTO_NONE) {}
-    UriScheme(AnyP::ProtocolType const aScheme) : theScheme_(aScheme) {}
+    UriScheme(AnyP::ProtocolType const aScheme, const char *img = nullptr);
+    UriScheme(const AnyP::UriScheme &o) : theScheme_(o.theScheme_), image_(o.image_) {}
+    UriScheme(AnyP::UriScheme &&) = default;
     ~UriScheme() {}
 
-    operator AnyP::ProtocolType() const { return theScheme_; }
+    AnyP::UriScheme& operator=(const AnyP::UriScheme &o) {
+        theScheme_ = o.theScheme_;
+        image_ = o.image_;
+        return *this;
+    }
+    AnyP::UriScheme& operator=(AnyP::UriScheme &&) = default;
 
+    operator AnyP::ProtocolType() const { return theScheme_; }
+    // XXX: does not account for comparison of unknown schemes (by image)
     bool operator != (AnyP::ProtocolType const & aProtocol) const { return theScheme_ != aProtocol; }
 
     /** Get a char string representation of the scheme.
-     * Does not include the ':' or '://" terminators.
-     *
-     * An upper bound length of BUFSIZ bytes converted. Remainder will be truncated.
-     * The result of this call will remain usable only until any subsequest call
-     * and must be copied if persistence is needed.
+     * Does not include the ':' or "://" terminators.
      */
-    char const *c_str() const;
+    SBuf image() const {return image_;}
 
     unsigned short defaultPort() const;
 
 private:
     /// This is a typecode pointer into the enum/registry of protocols handled.
     AnyP::ProtocolType theScheme_;
+
+    /// the string representation
+    SBuf image_;
 };
 
 } // namespace AnyP
@@ -51,7 +60,7 @@ private:
 inline std::ostream &
 operator << (std::ostream &os, AnyP::UriScheme const &scheme)
 {
-    os << scheme.c_str();
+    os << scheme.image();
     return os;
 }
 
index 2a860be8698a17e6071262c3f23d7330affe2705..3a6a76a95e7b2be7b3ecbb05271ecaae3acfd2bc 100644 (file)
@@ -3381,7 +3381,7 @@ parsePortSpecification(const AnyP::PortCfgPointer &s, char *token)
     s->name = xstrdup(token);
     s->connection_auth_disabled = false;
 
-    const char *portType = AnyP::UriScheme(s->transport.protocol).c_str();
+    const SBuf &portType = AnyP::UriScheme(s->transport.protocol).image();
 
     if (*token == '[') {
         /* [ipv6]:port */
@@ -3780,7 +3780,7 @@ parsePortCfg(AnyP::PortCfgPointer *head, const char *optionName)
         // clone the port options from *s to *(s->next)
         s->next = s->clone();
         s->next->s.setIPv4();
-        debugs(3, 3, AnyP::UriScheme(s->transport.protocol).c_str() << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
+        debugs(3, 3, AnyP::UriScheme(s->transport.protocol).image() << "_port: clone wildcard address for split-stack: " << s->s << " and " << s->next->s);
     }
 
     while (*head != NULL)
@@ -3824,7 +3824,7 @@ dump_generic_port(StoreEntry * e, const char *n, const AnyP::PortCfgPointer &s)
 
         // TODO: compare against prefix of 'n' instead of assuming http_port
         if (s->transport.protocol != AnyP::PROTO_HTTP)
-            storeAppendPrintf(e, " protocol=%s", AnyP::UriScheme(s->transport.protocol).c_str());
+            storeAppendPrintf(e, " protocol=%s", AnyP::ProtocolType_str[s->transport.protocol]);
 
         if (s->allow_direct)
             storeAppendPrintf(e, " allow-direct");
index 0c463e56596441445a5054473af975e957710cc5..cbb97e5a57ab99b2ae12a69b8e6621018c50c4f6 100644 (file)
@@ -167,7 +167,7 @@ carpSelectParent(HttpRequest * request)
             // this code follows URI syntax pattern.
             // corner cases should use the full effective request URI
             if (tp->options.carp_key.scheme) {
-                key.append(request->url.getScheme().c_str());
+                key.append(request->url.getScheme().image());
                 if (key.length()) //if the scheme is not empty
                     key.append("://");
             }
index 1ed432c2dc897b2a07e216bb20c4a175e1470f6a..a84b0589ddd1889dcc8df42e93342a5471237b6b 100644 (file)
@@ -1221,7 +1221,8 @@ prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1
         } // else nothing to alter port-wise.
         const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen + strlen(host);
         http->uri = (char *)xcalloc(url_sz, 1);
-        snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH, AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(url));
+        const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+        snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH, SQUIDSBUFPRINT(scheme), host, SQUIDSBUFPRINT(url));
         debugs(33, 5, "ACCEL VHOST REWRITE: " << http->uri);
     } else if (conn->port->defaultsite /* && !vhost */) {
         debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << conn->port->defaultsite << " + vport=" << vport);
@@ -1233,8 +1234,9 @@ prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1
         if (vport > 0) {
             snprintf(vportStr, sizeof(vportStr),":%d",vport);
         }
-        snprintf(http->uri, url_sz, "%s://%s%s" SQUIDSBUFPH,
-                 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
+        const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+        snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s%s" SQUIDSBUFPH,
+                 SQUIDSBUFPRINT(scheme), conn->port->defaultsite, vportStr, SQUIDSBUFPRINT(url));
         debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: " << http->uri);
     } else if (vport > 0 /* && (!vhost || no Host:) */) {
         debugs(33, 5, "ACCEL VPORT REWRITE: *_port IP + vport=" << vport);
@@ -1242,9 +1244,9 @@ prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, const Http1
         const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen;
         http->uri = (char *)xcalloc(url_sz, 1);
         http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
-        snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
-                 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(),
-                 ipbuf, vport, SQUIDSBUFPRINT(url));
+        const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+        snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
+                 SQUIDSBUFPRINT(scheme), ipbuf, vport, SQUIDSBUFPRINT(url));
         debugs(33, 5, "ACCEL VPORT REWRITE: " << http->uri);
     }
 }
@@ -1262,8 +1264,9 @@ prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, const Http1
         const int url_sz = hp->requestUri().length() + 32 + Config.appendDomainLen +
                            strlen(host);
         http->uri = (char *)xcalloc(url_sz, 1);
-        snprintf(http->uri, url_sz, "%s://%s" SQUIDSBUFPH,
-                 AnyP::UriScheme(conn->transferProtocol.protocol).c_str(), host, SQUIDSBUFPRINT(hp->requestUri()));
+        const SBuf &scheme = AnyP::UriScheme(conn->transferProtocol.protocol).image();
+        snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s" SQUIDSBUFPH,
+                 SQUIDSBUFPRINT(scheme), host, SQUIDSBUFPRINT(hp->requestUri()));
         debugs(33, 5, "TRANSPARENT HOST REWRITE: " << http->uri);
     } else {
         /* Put the local socket IP address as the hostname.  */
@@ -1271,8 +1274,9 @@ prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, const Http1
         http->uri = (char *)xcalloc(url_sz, 1);
         static char ipbuf[MAX_IPSTRLEN];
         http->getConn()->clientConnection->local.toHostStr(ipbuf,MAX_IPSTRLEN);
-        snprintf(http->uri, url_sz, "%s://%s:%d" SQUIDSBUFPH,
-                 AnyP::UriScheme(http->getConn()->transferProtocol.protocol).c_str(),
+        const SBuf &scheme = AnyP::UriScheme(http->getConn()->transferProtocol.protocol).image();
+        snprintf(http->uri, url_sz, SQUIDSBUFPH "://%s:%d" SQUIDSBUFPH,
+                 SQUIDSBUFPRINT(scheme),
                  ipbuf, http->getConn()->clientConnection->local.port(), SQUIDSBUFPRINT(hp->requestUri()));
         debugs(33, 5, "TRANSPARENT REWRITE: " << http->uri);
     }
@@ -1679,7 +1683,7 @@ clientProcessRequest(ConnStateData *conn, const Http1::RequestParserPointer &hp,
             http->flags.internal = true;
         } else if (Config.onoff.global_internal_static && internalStaticCheck(request->url.path())) {
             debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (global_internal_static on)");
-            request->url.setScheme(AnyP::PROTO_HTTP);
+            request->url.setScheme(AnyP::PROTO_HTTP, "http");
             request->url.host(internalHostname());
             request->url.port(getMyPort());
             http->flags.internal = true;
@@ -3445,7 +3449,7 @@ static void
 clientHttpConnectionsOpen(void)
 {
     for (AnyP::PortCfgPointer s = HttpPortList; s != NULL; s = s->next) {
-        const char *scheme = AnyP::UriScheme(s->transport.protocol).c_str();
+        const SBuf &scheme = AnyP::UriScheme(s->transport.protocol).image();
 
         if (MAXTCPLISTENPORTS == NHttpSockets) {
             debugs(1, DBG_IMPORTANT, "WARNING: You have too many '" << scheme << "_port' lines.");
index b89bf8e9b4ad5118bc794f9a392d52d57177bdff..b5fbd88fa9edf1aad4b3ca619107d44f234d3767 100644 (file)
@@ -2259,7 +2259,7 @@ clientReplyContext::createStoreEntry(const HttpRequestMethod& m, RequestFlags re
      */
 
     if (http->request == NULL) {
-        http->request = new HttpRequest(m, AnyP::PROTO_NONE, null_string);
+        http->request = new HttpRequest(m, AnyP::PROTO_NONE, "http", null_string);
         HTTPMSGLOCK(http->request);
     }
 
index 200af2a30ed50feb6cedaadef7c6a3ae7ac59964..3c06357fb5163fbcf77afb489d995bbf3ae0fcd1 100644 (file)
@@ -946,7 +946,8 @@ ErrorState::Convert(char token, bool building_deny_info_url, bool allowRecursion
 
     case 'P':
         if (request) {
-            p = request->url.getScheme().c_str();
+            const SBuf &m = request->url.getScheme().image();
+            mb.append(m.rawContent(), m.length());
         } else if (!building_deny_info_url) {
             p = "[unknown protocol]";
         }
index 7ca4f53036ae9e32abcd974ced56c68750b54b9b..0f4139d700232292f59515db40c6afd7bd3c3be2 100644 (file)
@@ -996,7 +996,9 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_CLIENT_REQ_URLSCHEME:
             if (al->request) {
-                out = al->request->url.getScheme().c_str();
+                const SBuf s(al->request->url.getScheme().image());
+                sb.append(s.rawContent(), s.length());
+                out = sb.termedBuf();
                 quote = 1;
             }
             break;
@@ -1075,7 +1077,9 @@ Format::Format::assemble(MemBuf &mb, const AccessLogEntry::Pointer &al, int logS
 
         case LFT_SERVER_REQ_URLSCHEME:
             if (al->adapted_request) {
-                out = al->adapted_request->url.getScheme().c_str();
+                const SBuf s(al->adapted_request->url.getScheme().image());
+                sb.append(s.rawContent(), s.length());
+                out = sb.termedBuf();
                 quote = 1;
             }
             break;
index eddd4261024b4239bd2a0edc0ba5ce6bf8f3821a..69bedc59951f0f2f55af56f55ea6edac51663944 100644 (file)
 // void httpRequestPack(void *obj, Packable *p);
 
 HttpRequest::HttpRequest() : HttpMsg(hoRequest) {STUB}
-HttpRequest::HttpRequest(const HttpRequestMethod &, AnyP::ProtocolType, const char *) : HttpMsg(hoRequest) {STUB}
+HttpRequest::HttpRequest(const HttpRequestMethod &, AnyP::ProtocolType, const char *, const char *) : HttpMsg(hoRequest) {STUB}
 HttpRequest::~HttpRequest() STUB
 void HttpRequest::reset() STUB
-void HttpRequest::initHTTP(const HttpRequestMethod &, AnyP::ProtocolType, const char *) STUB
+void HttpRequest::initHTTP(const HttpRequestMethod &, AnyP::ProtocolType, const char *, const char *) STUB
 HttpRequest * HttpRequest::clone() const STUB_RETVAL(NULL)
 bool HttpRequest::maybeCacheable() STUB_RETVAL(false)
 bool HttpRequest::conditional() const STUB_RETVAL(false)
index 3cc018e7dd95c59619198fc2d626da7f049ec3b3..1b790b1a0e9bda6592f49a52ebfa6639848f041f 100644 (file)
@@ -112,9 +112,9 @@ testUriScheme::testConstructprotocol_t()
 void
 testUriScheme::testC_str()
 {
-    String lhs("wais");
+    SBuf lhs("wais");
     AnyP::UriScheme wais(AnyP::PROTO_WAIS);
-    String rhs(wais.c_str());
+    SBuf rhs(wais.image());
     CPPUNIT_ASSERT_EQUAL(lhs, rhs);
 }
 
index 55f20d88f84ab1881237404176b8e2156680050b..8cfe220eb901e049655734b5234d78cb7e8e792c 100644 (file)
@@ -18,6 +18,7 @@
 
 static HttpRequest *urlParseFinish(const HttpRequestMethod& method,
                                    const AnyP::ProtocolType protocol,
+                                   const char *const protoStr,
                                    const char *const urlpath,
                                    const char *const host,
                                    const SBuf &login,
@@ -157,6 +158,9 @@ urlParseProtocol(const char *b)
     if (strncasecmp(b, "whois", len) == 0)
         return AnyP::PROTO_WHOIS;
 
+    if (len > 0)
+        return AnyP::PROTO_UNKNOWN;
+
     return AnyP::PROTO_NONE;
 }
 
@@ -214,8 +218,8 @@ urlParse(const HttpRequestMethod& method, char *url, HttpRequest *request)
     } else if ((method == Http::METHOD_OPTIONS || method == Http::METHOD_TRACE) &&
                URL::Asterisk().cmp(url) == 0) {
         protocol = AnyP::PROTO_HTTP;
-        port = AnyP::UriScheme(protocol).defaultPort();
-        return urlParseFinish(method, protocol, url, host, SBuf(), port, request);
+        port = 80; // or the slow way ...  AnyP::UriScheme(protocol,"http").defaultPort();
+        return urlParseFinish(method, protocol, "http", url, host, SBuf(), port, request);
     } else if (!strncmp(url, "urn:", 4)) {
         return urnParse(method, url, request);
     } else {
@@ -422,7 +426,7 @@ urlParse(const HttpRequestMethod& method, char *url, HttpRequest *request)
         }
     }
 
-    return urlParseFinish(method, protocol, urlpath, host, SBuf(login), port, request);
+    return urlParseFinish(method, protocol, proto, urlpath, host, SBuf(login), port, request);
 }
 
 /**
@@ -433,6 +437,7 @@ urlParse(const HttpRequestMethod& method, char *url, HttpRequest *request)
 static HttpRequest *
 urlParseFinish(const HttpRequestMethod& method,
                const AnyP::ProtocolType protocol,
+               const char *const protoStr, // for unknown protocols
                const char *const urlpath,
                const char *const host,
                const SBuf &login,
@@ -440,9 +445,9 @@ urlParseFinish(const HttpRequestMethod& method,
                HttpRequest *request)
 {
     if (NULL == request)
-        request = new HttpRequest(method, protocol, urlpath);
+        request = new HttpRequest(method, protocol, protoStr, urlpath);
     else {
-        request->initHTTP(method, protocol, urlpath);
+        request->initHTTP(method, protocol, protoStr, urlpath);
     }
 
     request->url.host(host);
@@ -456,11 +461,11 @@ urnParse(const HttpRequestMethod& method, char *urn, HttpRequest *request)
 {
     debugs(50, 5, "urnParse: " << urn);
     if (request) {
-        request->initHTTP(method, AnyP::PROTO_URN, urn + 4);
+        request->initHTTP(method, AnyP::PROTO_URN, "urn", urn + 4);
         return request;
     }
 
-    return new HttpRequest(method, AnyP::PROTO_URN, urn + 4);
+    return new HttpRequest(method, AnyP::PROTO_URN, "urn", urn + 4);
 }
 
 void
@@ -496,7 +501,8 @@ URL::absolute() const
         // TODO: most URL will be much shorter, avoid allocating this much
         absolute_.reserveCapacity(MAX_URL);
 
-        absolute_.appendf("%s:", getScheme().c_str());
+        absolute_.append(getScheme().image());
+        absolute_.append(":",1);
         if (getScheme() != AnyP::PROTO_URN) {
             absolute_.append("//", 2);
             const bool omitUserInfo = getScheme() == AnyP::PROTO_HTTP ||
@@ -620,8 +626,9 @@ urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
     }
 
     SBuf authorityForm = req->url.authority(); // host[:port]
-    size_t urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH,
-                             req->url.getScheme().c_str(),
+    const SBuf &scheme = req->url.getScheme().image();
+    size_t urllen = snprintf(urlbuf, MAX_URL, SQUIDSBUFPH "://" SQUIDSBUFPH "%s" SQUIDSBUFPH,
+                             SQUIDSBUFPRINT(scheme),
                              SQUIDSBUFPRINT(req->url.userInfo()),
                              !req->url.userInfo().isEmpty() ? "@" : "",
                              SQUIDSBUFPRINT(authorityForm));