[squid-dev] [PATCH] Refactor path strings into class URL
Amos Jeffries
squid3 at treenet.co.nz
Sun Jun 28 11:21:54 UTC 2015
This takes another step towards bug 1961 closure by shuffling the
HttpRequest::urlpath member into class URL.
These changes appear to work nicely. But I have not been able to test
all code paths that have logic change so a second pair of eyes on it
would be appreciated.
Amos
-------------- next part --------------
=== modified file 'src/HttpRequest.cc'
--- src/HttpRequest.cc 2015-06-09 06:14:43 +0000
+++ src/HttpRequest.cc 2015-06-28 06:32:00 +0000
@@ -44,49 +44,48 @@
HttpRequest::HttpRequest(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath) :
HttpMsg(hoRequest)
{
static unsigned int id = 1;
debugs(93,7, HERE << "constructed, this=" << this << " id=" << ++id);
init();
initHTTP(aMethod, aProtocol, aUrlpath);
}
HttpRequest::~HttpRequest()
{
clean();
debugs(93,7, HERE << "destructed, this=" << this);
}
void
HttpRequest::initHTTP(const HttpRequestMethod& aMethod, AnyP::ProtocolType aProtocol, const char *aUrlpath)
{
method = aMethod;
url.setScheme(aProtocol);
- urlpath = aUrlpath;
+ url.path(aUrlpath);
}
void
HttpRequest::init()
{
method = Http::METHOD_NONE;
url.clear();
- urlpath = NULL;
#if USE_AUTH
auth_user_request = NULL;
#endif
canonical = NULL;
memset(&flags, '\0', sizeof(flags));
range = NULL;
ims = -1;
imslen = 0;
lastmod = -1;
client_addr.setEmpty();
my_addr.setEmpty();
body_pipe = NULL;
// hier
dnsWait = -1;
errType = ERR_NONE;
errDetail = ERR_DETAIL_NONE;
peer_login = NULL; // not allocated/deallocated by this class
peer_domain = NULL; // not allocated/deallocated by this class
peer_host = NULL;
vary_headers = NULL;
@@ -109,41 +108,40 @@
icapHistory_ = NULL;
#endif
rangeOffsetLimit = -2; //a value of -2 means not checked yet
forcedBodyContinuation = false;
}
void
HttpRequest::clean()
{
// we used to assert that the pipe is NULL, but now the request only
// points to a pipe that is owned and initiated by another object.
body_pipe = NULL;
#if USE_AUTH
auth_user_request = NULL;
#endif
safe_free(canonical);
safe_free(vary_headers);
url.clear();
- urlpath.clean();
header.clean();
if (cache_control) {
delete cache_control;
cache_control = NULL;
}
if (range) {
delete range;
range = NULL;
}
myportname.clean();
notes = NULL;
tag.clean();
#if USE_AUTH
extacl_user.clean();
@@ -156,52 +154,55 @@
etag.clean();
#if USE_ADAPTATION
adaptHistory_ = NULL;
#endif
#if ICAP_CLIENT
icapHistory_ = NULL;
#endif
}
void
HttpRequest::reset()
{
clean();
init();
}
HttpRequest *
HttpRequest::clone() const
{
- HttpRequest *copy = new HttpRequest(method, url.getScheme(), urlpath.termedBuf());
+ HttpRequest *copy = new HttpRequest();
+ copy->method = method;
// TODO: move common cloning clone to Msg::copyTo() or copy ctor
copy->header.append(&header);
copy->hdrCacheInit();
copy->hdr_sz = hdr_sz;
copy->http_ver = http_ver;
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());
// urlPath handled in ctor
copy->canonical = canonical ? xstrdup(canonical) : NULL;
// range handled in hdrCacheInit()
copy->ims = ims;
copy->imslen = imslen;
copy->hier = hier; // Is it safe to copy? Should we?
copy->errType = errType;
// XXX: what to do with copy->peer_login?
copy->lastmod = lastmod;
copy->etag = etag;
copy->vary_headers = vary_headers ? xstrdup(vary_headers) : NULL;
// XXX: what to do with copy->peer_domain?
copy->tag = tag;
copy->extacl_log = extacl_log;
@@ -355,65 +356,65 @@
hdrCacheInit();
return result;
}
/* swaps out request using httpRequestPack */
void
HttpRequest::swapOut(StoreEntry * e)
{
assert(e);
e->buffer();
pack(e);
}
/* packs request-line and headers, appends <crlf> terminator */
void
HttpRequest::pack(Packable * p)
{
assert(p);
/* pack request-line */
- p->appendf(SQUIDSBUFPH " " SQUIDSTRINGPH " HTTP/%d.%d\r\n",
- SQUIDSBUFPRINT(method.image()), SQUIDSTRINGPRINT(urlpath),
+ 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 */
p->append("\r\n", 2);
}
/*
* A wrapper for debugObj()
*/
void
httpRequestPack(void *obj, Packable *p)
{
HttpRequest *request = static_cast<HttpRequest*>(obj);
request->pack(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;
}
/* sync this routine when you update HttpRequest struct */
void
HttpRequest::hdrCacheInit()
{
HttpMsg::hdrCacheInit();
assert(!range);
range = header.getRange();
}
#if ICAP_CLIENT
Adaptation::Icap::History::Pointer
HttpRequest::icapHistory() const
{
if (!icapHistory_) {
if (Log::TheConfig.hasIcapToken || IcapLogfileStatus == LOG_ENABLE) {
@@ -474,57 +475,53 @@
{
if (errType || errDetail)
debugs(11, 5, HERE << "old error details: " << errType << '/' << errDetail);
debugs(11, 5, HERE << "current error details: " << aType << '/' << aDetail);
// checking type and detail separately may cause inconsistency, but
// may result in more details available if they only become available later
if (!errType)
errType = aType;
if (!errDetail)
errDetail = aDetail;
}
void
HttpRequest::clearError()
{
debugs(11, 7, HERE << "old error details: " << errType << '/' << errDetail);
errType = ERR_NONE;
errDetail = ERR_DETAIL_NONE;
}
-const char *HttpRequest::packableURI(bool full_uri) const
+void
+HttpRequest::packFirstLineInto(Packable * p, bool full_uri) const
{
+ SBuf tmp;
if (full_uri)
- return urlCanonical((HttpRequest*)this);
-
- if (urlpath.size())
- return urlpath.termedBuf();
+ tmp = urlCanonical((HttpRequest*)this);
+ else
+ tmp = url.path();
- return "/";
-}
-
-void HttpRequest::packFirstLineInto(Packable * p, bool full_uri) const
-{
// form HTTP request-line
- p->appendf(SQUIDSBUFPH " %s HTTP/%d.%d\r\n",
+ p->appendf(SQUIDSBUFPH " " SQUIDSBUFPH " HTTP/%d.%d\r\n",
SQUIDSBUFPRINT(method.image()),
- packableURI(full_uri),
+ SQUIDSBUFPRINT(tmp),
http_ver.major, http_ver.minor);
}
/*
* Indicate whether or not we would expect an entity-body
* along with this request
*/
bool
HttpRequest::expectingBody(const HttpRequestMethod &, int64_t &theSize) const
{
bool expectBody = false;
/*
* Note: Checks for message validity is in clientIsContentLengthValid().
* this just checks if a entity-body is expected based on HTTP message syntax
*/
if (header.chunked()) {
expectBody = true;
theSize = -1;
} else if (content_length >= 0) {
=== modified file 'src/HttpRequest.h'
--- src/HttpRequest.h 2015-06-09 06:14:43 +0000
+++ src/HttpRequest.h 2015-06-15 03:50:46 +0000
@@ -99,42 +99,40 @@
void init();
public:
HttpRequestMethod method;
URL url; ///< the request URI
private:
#if USE_ADAPTATION
mutable Adaptation::History::Pointer adaptHistory_; ///< per-HTTP transaction info
#endif
#if ICAP_CLIENT
mutable Adaptation::Icap::History::Pointer icapHistory_; ///< per-HTTP transaction info
#endif
public:
#if USE_AUTH
Auth::UserRequest::Pointer auth_user_request;
#endif
- String urlpath;
-
char *canonical;
/**
* If defined, store_id_program mapped the request URL to this ID.
* Store uses this ID (and not the URL) to find and store entries,
* avoiding caching duplicate entries when different URLs point to
* "essentially the same" cachable resource.
*/
String store_id;
RequestFlags flags;
HttpHdrRange *range;
time_t ims;
int imslen;
Ip::Address client_addr;
@@ -179,70 +177,68 @@
String x_forwarded_for_iterator; /* XXX a list of IP addresses */
#endif /* FOLLOW_X_FORWARDED_FOR */
/// A strong etag of the cached entry. Used for refreshing that entry.
String etag;
/// whether we have responded with HTTP 100 or FTP 150 already
bool forcedBodyContinuation;
public:
bool multipartRangeRequest() const;
bool parseFirstLine(const char *start, const char *end);
bool parseHeader(Http1::RequestParser &hp); // TODO move this function to the parser
virtual bool expectingBody(const HttpRequestMethod& unused, int64_t&) const;
bool bodyNibbled() const; // the request has a [partially] consumed body
- int prefixLen();
+ int prefixLen() const;
void swapOut(StoreEntry * e);
void pack(Packable * p);
static void httpRequestPack(void *obj, Packable *p);
static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method);
static HttpRequest * CreateFromUrl(char * url);
ConnStateData *pinnedConnection();
/**
* Returns the current StoreID for the request as a nul-terminated char*.
* Always returns the current id for the request
* (either the request canonical url or modified ID by the helper).
* Does not return NULL.
*/
const char *storeId();
/**
* The client connection manager, if known;
* Used for any response actions needed directly to the client.
* ie 1xx forwarding or connection pinning state changes
*/
CbcPointer<ConnStateData> clientConnectionManager;
/// forgets about the cached Range header (for a reason)
void ignoreRange(const char *reason);
int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */
private:
- const char *packableURI(bool full_uri) const;
-
mutable int64_t rangeOffsetLimit; /* caches the result of getRangeOffsetLimit */
protected:
virtual void packFirstLineInto(Packable * p, bool full_uri) const;
virtual bool sanityCheckStartLine(const char *buf, const size_t hdr_len, Http::StatusCode *error);
virtual void hdrCacheInit();
virtual bool inheritProperties(const HttpMsg *aMsg);
};
#endif /* SQUID_HTTPREQUEST_H */
=== modified file 'src/URL.h'
--- src/URL.h 2015-06-09 06:14:43 +0000
+++ src/URL.h 2015-06-28 06:35:45 +0000
@@ -1,75 +1,84 @@
/*
* Copyright (C) 1996-2015 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_SRC_URL_H
#define SQUID_SRC_URL_H
#include "anyp/UriScheme.h"
#include "ip/Address.h"
#include "rfc2181.h"
#include "SBuf.h"
+#include <iosfwd>
+
/**
* The URL class represents a Uniform Resource Location
*
* Governed by RFC 3986
*/
class URL
{
MEMPROXY_CLASS(URL);
public:
URL() : scheme_(), hostIsNumeric_(false), port_(0) {*host_=0;}
URL(AnyP::UriScheme const &aScheme) : scheme_(aScheme), hostIsNumeric_(false), port_(0) {*host_=0;}
void clear() {
scheme_=AnyP::PROTO_NONE;
hostIsNumeric_ = false;
*host_ = 0;
hostAddr_.setEmpty();
port_ = 0;
touch();
}
void touch(); ///< clear the cached URI display forms
AnyP::UriScheme const & getScheme() const {return scheme_;}
/// convert the URL scheme to that given
void setScheme(const AnyP::ProtocolType &p) {scheme_=p; touch();}
void userInfo(const SBuf &s) {userInfo_=s; touch();}
const SBuf &userInfo() const {return userInfo_;}
void host(const char *src);
const char *host(void) const {return host_;}
int hostIsNumeric(void) const {return hostIsNumeric_;}
Ip::Address const & hostIP(void) const {return hostAddr_;}
void port(unsigned short p) {port_=p; touch();}
unsigned short port() const {return port_;}
+ void path(const char *p) {path_=p; touch();}
+ void path(const SBuf &p) {path_=p; touch();}
+ const SBuf &path() const;
+
+ /// the static '/' default URL-path
+ static const SBuf &SlashPath();
+
/// the static '*' pseudo-URL
static const SBuf &Asterisk();
/**
* The authority-form URI for currently stored values.
*
* As defined by RFC 7230 section 5.3.3 this form omits the
* userinfo@ field from RFC 3986 defined authority segment.
*
* \param requirePort when true the port will be included, otherwise
* port will be elided when it is the default for
* the current scheme.
*/
SBuf &authority(bool requirePort = false) const;
private:
/**
\par
* The scheme of this URL. This has the 'type code' smell about it.
* In future we may want to make the methods that dispatch based on
@@ -84,45 +93,58 @@
* class for each manner of treating the scheme : a Hierarchical URL, a
* non-hierarchical URL etc.
\par
* Deferring the decision, its a type code for now. RBC 20060507.
\par
* In order to make taking any of these routes easy, scheme is private
* and immutable, only settable at construction time,
*/
AnyP::UriScheme scheme_;
SBuf userInfo_; // aka 'URL-login'
// XXX: uses char[] instead of SBUf to reduce performance regressions
// from c_str() since most code using this is not yet using SBuf
char host_[SQUIDHOSTNAMELEN]; ///< string representation of the URI authority name or IP
bool hostIsNumeric_; ///< whether the authority 'host' is a raw-IP
Ip::Address hostAddr_; ///< binary representation of the URI authority if it is a raw-IP
unsigned short port_; ///< URL port
+ // XXX: for now includes query-string.
+ SBuf path_; ///< URL path segment
+
// pre-assembled URL forms
mutable SBuf authorityHttp_; ///< RFC 7230 section 5.3.3 authority, maybe without default-port
mutable SBuf authorityWithPort_; ///< RFC 7230 section 5.3.3 authority with explicit port
+ mutable SBuf canonical_; ///< full absolute-URI
};
+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();
+ return os;
+}
+
class HttpRequest;
class HttpRequestMethod;
AnyP::ProtocolType urlParseProtocol(const char *, const char *e = NULL);
void urlInitialize(void);
HttpRequest *urlParse(const HttpRequestMethod&, char *, HttpRequest *request = NULL);
const char *urlCanonical(HttpRequest *);
char *urlCanonicalClean(const HttpRequest *);
const char *urlCanonicalFakeHttps(const HttpRequest * request);
bool urlIsRelative(const char *);
char *urlMakeAbsolute(const HttpRequest *, const char *);
char *urlRInternal(const char *host, unsigned short port, const char *dir, const char *name);
char *urlInternal(const char *dir, const char *name);
/**
* matchDomainName() compares a hostname (usually extracted from traffic)
* with a domainname (usually from an ACL) according to the following rules:
*
* HOST | DOMAIN | MATCH?
* -------------|-------------|------
=== modified file 'src/acl/UrlPath.cc'
--- src/acl/UrlPath.cc 2015-01-13 07:25:36 +0000
+++ src/acl/UrlPath.cc 2015-06-09 11:52:48 +0000
@@ -1,38 +1,39 @@
/*
* Copyright (C) 1996-2015 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.
*/
/* DEBUG: section 28 Access Control */
#include "squid.h"
#include "acl/Checklist.h"
#include "acl/RegexData.h"
#include "acl/UrlPath.h"
#include "HttpRequest.h"
#include "rfc1738.h"
int
ACLUrlPathStrategy::match (ACLData<char const *> * &data, ACLFilledChecklist *checklist, ACLFlags &)
{
- if (!checklist->request->urlpath.size())
+ if (checklist->request->url.path().isEmpty())
return -1;
- char *esc_buf = xstrdup(checklist->request->urlpath.termedBuf());
+ SBuf tmp = checklist->request->url.path();
+ char *esc_buf = xstrndup(tmp.rawContent(), tmp.length());
rfc1738_unescape(esc_buf);
int result = data->match(esc_buf);
- safe_free(esc_buf);
+ xfree(esc_buf);
return result;
}
ACLUrlPathStrategy *
ACLUrlPathStrategy::Instance()
{
return &Instance_;
}
ACLUrlPathStrategy ACLUrlPathStrategy::Instance_;
=== modified file 'src/adaptation/Service.cc'
--- src/adaptation/Service.cc 2015-01-13 07:25:36 +0000
+++ src/adaptation/Service.cc 2015-06-14 19:02:01 +0000
@@ -32,41 +32,41 @@
return probed() && !up();
}
bool
Adaptation::Service::wants(const ServiceFilter &filter) const
{
if (cfg().method != filter.method)
return false;
if (cfg().point != filter.point)
return false;
// sending a message to a broken service is likely to cause errors
if (cfg().bypass && broken())
return false;
if (up()) {
// Sending a message to a service that does not want it is useless.
// note that we cannot check wantsUrl for service that is not "up"
// note that even essential services are skipped on unwanted URLs!
- return wantsUrl(filter.request->urlpath);
+ return wantsUrl(filter.request->url.path());
}
// The service is down and is either not bypassable or not probed due
// to the bypass && broken() test above. Thus, we want to use it!
return true;
}
Adaptation::Services &
Adaptation::AllServices()
{
static Services *TheServices = new Services;
return *TheServices;
}
Adaptation::ServicePointer
Adaptation::FindService(const Service::Id& key)
{
typedef Services::iterator SI;
for (SI i = AllServices().begin(); i != AllServices().end(); ++i) {
if ((*i)->cfg().key == key)
=== modified file 'src/adaptation/Service.h'
--- src/adaptation/Service.h 2015-01-13 07:25:36 +0000
+++ src/adaptation/Service.h 2015-06-14 19:14:27 +0000
@@ -28,41 +28,41 @@
// specific adaptation mechanisms extend this class
class Service: public RefCountable
{
public:
typedef RefCount<Service> Pointer;
typedef String Id;
public:
explicit Service(const ServiceConfigPointer &aConfig);
virtual ~Service();
virtual bool probed() const = 0; // see comments above
virtual bool broken() const;
virtual bool up() const = 0; // see comments above
virtual Initiate *makeXactLauncher(HttpMsg *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp) = 0;
bool wants(const ServiceFilter &filter) const;
// the methods below can only be called on an up() service
- virtual bool wantsUrl(const String &urlPath) const = 0;
+ virtual bool wantsUrl(const SBuf &urlPath) const = 0;
// called by transactions to report service failure
virtual void noteFailure() = 0;
const ServiceConfig &cfg() const { return *theConfig; }
virtual void finalize(); // called after creation
/// called when removed from the config; the service will be
/// auto-destroyed when the last refcounting user leaves
virtual void detach() = 0;
/// whether detached() was called
virtual bool detached() const = 0;
protected:
ServiceConfig &writeableCfg() { return *theConfig; }
private:
ServiceConfigPointer theConfig;
};
=== modified file 'src/adaptation/ecap/ServiceRep.cc'
--- src/adaptation/ecap/ServiceRep.cc 2015-01-13 07:25:36 +0000
+++ src/adaptation/ecap/ServiceRep.cc 2015-06-14 19:24:48 +0000
@@ -220,44 +220,46 @@
return false; // we cannot handle the problem; the caller may escalate
// make up() false, preventing new adaptation requests and enabling bypass
theService.reset();
debugs(93, level, "WARNING: " << kind << " eCAP service is " <<
"down after initialization failure: " << cfg().uri);
return true; // tell the caller to ignore the problem because we handled it
}
bool Adaptation::Ecap::ServiceRep::probed() const
{
return true; // we "probe" the adapter in finalize().
}
bool Adaptation::Ecap::ServiceRep::up() const
{
return theService != NULL;
}
-bool Adaptation::Ecap::ServiceRep::wantsUrl(const String &urlPath) const
+bool Adaptation::Ecap::ServiceRep::wantsUrl(const SBuf &urlPath) const
{
Must(up());
- return theService->wantsUrl(urlPath.termedBuf());
+ SBuf nonConstUrlPath = urlPath;
+ // c_str() reallocates and terminates for libecap API
+ return theService->wantsUrl(nonConstUrlPath.c_str());
}
Adaptation::Initiate *
Adaptation::Ecap::ServiceRep::makeXactLauncher(HttpMsg *virgin,
HttpRequest *cause, AccessLogEntry::Pointer &alp)
{
Must(up());
// register now because (a) we need EventLoop::Running and (b) we do not
// want to add more main loop overheads unless an async service is used.
static AsyncEngine *TheEngine = NULL;
if (AsyncServices.size() && !TheEngine && EventLoop::Running) {
TheEngine = new Engine;
EventLoop::Running->registerEngine(TheEngine);
debugs(93, 3, "asyncs: " << AsyncServices.size() << ' ' << TheEngine);
}
XactionRep *rep = new XactionRep(virgin, cause, alp, Pointer(this));
XactionRep::AdapterXaction x(theService->makeXaction(rep));
rep->master(x);
=== modified file 'src/adaptation/ecap/ServiceRep.h'
--- src/adaptation/ecap/ServiceRep.h 2015-01-13 07:25:36 +0000
+++ src/adaptation/ecap/ServiceRep.h 2015-06-14 19:07:28 +0000
@@ -21,41 +21,41 @@
namespace Ecap
{
/* The eCAP service representative maintains information about a single eCAP
service that Squid communicates with. One eCAP module may register many
eCAP services. */
class ServiceRep : public Adaptation::Service
{
public:
explicit ServiceRep(const ServiceConfigPointer &aConfig);
virtual ~ServiceRep();
typedef libecap::shared_ptr<libecap::adapter::Service> AdapterService;
/* Adaptation::Service API */
virtual void finalize();
virtual bool probed() const;
virtual bool up() const;
virtual Adaptation::Initiate *makeXactLauncher(HttpMsg *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp);
- virtual bool wantsUrl(const String &urlPath) const;
+ virtual bool wantsUrl(const SBuf &urlPath) const;
virtual void noteFailure();
virtual const char *status() const;
virtual void detach();
virtual bool detached() const;
protected:
void tryConfigureAndStart();
bool handleFinalizeFailure(const char *error);
private:
AdapterService theService; // the actual adaptation service we represent
bool isDetached;
};
/// register loaded eCAP module service
void RegisterAdapterService(const ServiceRep::AdapterService& adapterService);
/// unregister loaded eCAP module service by service uri
void UnregisterAdapterService(const String& serviceUri);
/// returns loaded eCAP module service by service uri
=== modified file 'src/adaptation/icap/ModXact.cc'
--- src/adaptation/icap/ModXact.cc 2015-06-01 21:41:37 +0000
+++ src/adaptation/icap/ModXact.cc 2015-06-28 06:37:43 +0000
@@ -1372,43 +1372,41 @@
Adaptation::History::Pointer ah = request->adaptHistory(false);
if (ah != NULL) {
String name, value;
if (ah->getXxRecord(name, value)) {
buf.appendf(SQUIDSTRINGPH ": " SQUIDSTRINGPH "\r\n", SQUIDSTRINGPRINT(name), SQUIDSTRINGPRINT(value));
}
}
}
buf.append("Encapsulated: ", 14);
MemBuf httpBuf;
httpBuf.init();
// build HTTP request header, if any
ICAP::Method m = s.method;
// to simplify, we could assume that request is always available
- String urlPath;
if (request) {
- urlPath = request->urlpath;
if (ICAP::methodRespmod == m)
encapsulateHead(buf, "req-hdr", httpBuf, request);
else if (ICAP::methodReqmod == m)
encapsulateHead(buf, "req-hdr", httpBuf, virgin.header);
}
if (ICAP::methodRespmod == m)
if (const HttpMsg *prime = virgin.header)
encapsulateHead(buf, "res-hdr", httpBuf, prime);
if (!virginBody.expected())
buf.appendf("null-body=%d", (int) httpBuf.contentSize());
else if (ICAP::methodReqmod == m)
buf.appendf("req-body=%d", (int) httpBuf.contentSize());
else
buf.appendf("res-body=%d", (int) httpBuf.contentSize());
buf.append(ICAP::crlf, 2); // terminate Encapsulated line
if (preview.enabled()) {
@@ -1566,44 +1564,44 @@
// pack polished HTTP header
packHead(httpBuf, headClone.getRaw());
// headClone unlocks and, hence, deletes the message we packed
}
void Adaptation::Icap::ModXact::packHead(MemBuf &httpBuf, const HttpMsg *head)
{
head->packInto(&httpBuf, true);
}
// decides whether to offer a preview and calculates its size
void Adaptation::Icap::ModXact::decideOnPreview()
{
if (!TheConfig.preview_enable) {
debugs(93, 5, HERE << "preview disabled by squid.conf");
return;
}
- const String urlPath = virginRequest().urlpath;
+ const SBuf urlPath(virginRequest().url.path());
size_t wantedSize;
if (!service().wantsPreview(urlPath, wantedSize)) {
- debugs(93, 5, HERE << "should not offer preview for " << urlPath);
+ debugs(93, 5, "should not offer preview for " << urlPath);
return;
}
// we decided to do preview, now compute its size
// cannot preview more than we can backup
size_t ad = min(wantedSize, TheBackupLimit);
if (!virginBody.expected())
ad = 0;
else if (virginBody.knownSize())
ad = min(static_cast<uint64_t>(ad), virginBody.size()); // not more than we have
debugs(93, 5, HERE << "should offer " << ad << "-byte preview " <<
"(service wanted " << wantedSize << ")");
preview.enable(ad);
Must(preview.enabled());
}
=== modified file 'src/adaptation/icap/Options.cc'
--- src/adaptation/icap/Options.cc 2015-01-13 07:25:36 +0000
+++ src/adaptation/icap/Options.cc 2015-06-14 19:06:59 +0000
@@ -26,52 +26,53 @@
{
theTransfers.preview.name = "Transfer-Preview";
theTransfers.preview.kind = xferPreview;
theTransfers.ignore.name = "Transfer-Ignore";
theTransfers.ignore.kind = xferIgnore;
theTransfers.complete.name = "Transfer-Complete";
theTransfers.complete.kind = xferComplete;
// Section 4.10.2 of RFC 3507 says that default is no Preview
// TODO: provide a squid.conf option to overwrite the default
theTransfers.byDefault = &theTransfers.complete;
}
Adaptation::Icap::Options::~Options()
{
}
// future optimization note: this method is called by ICAP ACL code at least
// twice for each HTTP message to see if the message should be ignored. For any
// non-ignored HTTP message, ICAP calls to check whether a preview is needed.
-Adaptation::Icap::Options::TransferKind Adaptation::Icap::Options::transferKind(const String &urlPath) const
+Adaptation::Icap::Options::TransferKind
+Adaptation::Icap::Options::transferKind(const SBuf &urlPath) const
{
if (theTransfers.preview.matches(urlPath))
return xferPreview;
if (theTransfers.complete.matches(urlPath))
return xferComplete;
if (theTransfers.ignore.matches(urlPath))
return xferIgnore;
- debugs(93,7, HERE << "url " << urlPath << " matches no extensions; " <<
+ debugs(93,7, "url " << urlPath << " matches no extensions; " <<
"using default: " << theTransfers.byDefault->name);
return theTransfers.byDefault->kind;
}
bool Adaptation::Icap::Options::valid() const
{
return !error;
}
bool Adaptation::Icap::Options::fresh() const
{
return squid_curtime <= expire();
}
int Adaptation::Icap::Options::ttl() const
{
Must(valid());
return theTTL >= 0 ? theTTL : TheConfig.default_options_ttl;
}
@@ -167,60 +168,58 @@
list.report(5, "Adaptation::Icap::Options::cfgTransferList: ");
}
/* Adaptation::Icap::Options::TransferList */
Adaptation::Icap::Options::TransferList::TransferList(): extensions(NULL), name(NULL),
kind(xferNone)
{
};
Adaptation::Icap::Options::TransferList::~TransferList()
{
wordlistDestroy(&extensions);
};
void Adaptation::Icap::Options::TransferList::add(const char *extension)
{
wordlistAdd(&extensions, extension);
};
-bool Adaptation::Icap::Options::TransferList::matches(const String &urlPath) const
+bool Adaptation::Icap::Options::TransferList::matches(const SBuf &urlPath) const
{
- const int urlLen = urlPath.size();
+ const SBuf::size_type urlLen = urlPath.length();
for (wordlist *e = extensions; e; e = e->next) {
// optimize: store extension lengths
- const int eLen = strlen(e->key);
+ const size_t eLen = strlen(e->key);
// assume URL contains at least '/' before the extension
if (eLen < urlLen) {
- const int eOff = urlLen - eLen;
+ const size_t eOff = urlLen - eLen;
// RFC 3507 examples imply that extensions come without leading '.'
- if (urlPath[eOff-1] == '.' &&
- strcmp(urlPath.termedBuf() + eOff, e->key) == 0) {
- debugs(93,7, HERE << "url " << urlPath << " matches " <<
- name << " extension " << e->key);
+ if (urlPath[eOff-1] == '.' && urlPath.substr(eOff).cmp(e->key, eLen) == 0) {
+ debugs(93,7, "url " << urlPath << " matches " << name << " extension " << e->key);
return true;
}
}
}
- debugs(93,8, HERE << "url " << urlPath << " matches no " << name << " extensions");
+ debugs(93,8, "url " << urlPath << " matches no " << name << " extensions");
return false;
}
void Adaptation::Icap::Options::TransferList::parse(const String &buf, bool &foundStar)
{
foundStar = false;
const char *item;
const char *pos = NULL;
int ilen;
while (strListGetItem(&buf, ',', &item, &ilen, &pos)) {
if (ilen == 1 && *item == '*')
foundStar = true;
else {
const char *tmp = xstrndup(item, ilen+1);
add(tmp);
xfree(tmp);
}
}
}
=== modified file 'src/adaptation/icap/Options.h'
--- src/adaptation/icap/Options.h 2015-01-13 07:25:36 +0000
+++ src/adaptation/icap/Options.h 2015-06-14 19:16:30 +0000
@@ -25,67 +25,67 @@
class Options
{
public:
typedef void GetCallback(void *data, Options *options);
static void Get(ServiceRep::Pointer &service, GetCallback *cb, void *data);
public:
Options();
~Options();
void configure(const HttpReply *reply);
bool valid() const;
bool fresh() const;
int ttl() const;
time_t expire() const;
time_t timestamp() const { return theTimestamp; };
typedef enum { xferNone, xferPreview, xferIgnore, xferComplete } TransferKind;
- TransferKind transferKind(const String &urlPath) const;
+ TransferKind transferKind(const SBuf &urlPath) const;
public:
const char *error; // human-readable information; set iff !valid()
// ICAP server MUST supply this info
std::vector<ICAP::Method> methods;
String istag;
// ICAP server MAY supply this info. If not, Squid supplies defaults.
String service;
String serviceId;
int max_connections;
bool allow204;
bool allow206;
int preview;
protected:
// Transfer-* extension list representation
// maintains wordlist and does parsing/matching
class TransferList
{
public:
TransferList();
~TransferList();
- bool matches(const String &urlPath) const;
+ bool matches(const SBuf &urlPath) const;
void parse(const String &buf, bool &foundStar);
void add(const char *extension);
void report(int level, const char *prefix) const;
public:
wordlist *extensions; // TODO: optimize with a hash of some sort
const char *name; // header name, mostly for debugging
TransferKind kind; // to simplify caller's life
};
// varios Transfer-* lists
struct Transfers {
TransferList preview;
TransferList ignore;
TransferList complete;
TransferList *byDefault; // Transfer-X that has '*'
} theTransfers;
int theTTL;
=== modified file 'src/adaptation/icap/ServiceRep.cc'
--- src/adaptation/icap/ServiceRep.cc 2015-05-26 17:25:04 +0000
+++ src/adaptation/icap/ServiceRep.cc 2015-06-14 19:10:43 +0000
@@ -304,47 +304,47 @@
}
bool Adaptation::Icap::ServiceRep::availableForNew() const
{
Must(up());
int available = availableConnections();
if (available < 0)
return true;
else
return (available - theAllWaiters > 0);
}
bool Adaptation::Icap::ServiceRep::availableForOld() const
{
Must(up());
int available = availableConnections();
return (available != 0); // it is -1 (no limit) or has available slots
}
-bool Adaptation::Icap::ServiceRep::wantsUrl(const String &urlPath) const
+bool Adaptation::Icap::ServiceRep::wantsUrl(const SBuf &urlPath) const
{
Must(hasOptions());
return theOptions->transferKind(urlPath) != Adaptation::Icap::Options::xferIgnore;
}
-bool Adaptation::Icap::ServiceRep::wantsPreview(const String &urlPath, size_t &wantedSize) const
+bool Adaptation::Icap::ServiceRep::wantsPreview(const SBuf &urlPath, size_t &wantedSize) const
{
Must(hasOptions());
if (theOptions->preview < 0)
return false;
if (theOptions->transferKind(urlPath) != Adaptation::Icap::Options::xferPreview)
return false;
wantedSize = theOptions->preview;
return true;
}
bool Adaptation::Icap::ServiceRep::allows204() const
{
Must(hasOptions());
return true; // in the future, we may have ACLs to prevent 204s
}
=== modified file 'src/adaptation/icap/ServiceRep.h'
--- src/adaptation/icap/ServiceRep.h 2015-05-05 09:09:27 +0000
+++ src/adaptation/icap/ServiceRep.h 2015-06-14 19:10:55 +0000
@@ -64,42 +64,42 @@
public:
typedef RefCount<ServiceRep> Pointer;
public:
explicit ServiceRep(const ServiceConfigPointer &aConfig);
virtual ~ServiceRep();
virtual void finalize();
virtual bool probed() const; // see comments above
virtual bool up() const; // see comments above
bool availableForNew() const; ///< a new transaction may start communicating with the service
bool availableForOld() const; ///< a transaction notified about connection slot availability may start communicating with the service
virtual Initiate *makeXactLauncher(HttpMsg *virginHeader, HttpRequest *virginCause, AccessLogEntry::Pointer &alp);
void callWhenAvailable(AsyncCall::Pointer &cb, bool priority = false);
void callWhenReady(AsyncCall::Pointer &cb);
// the methods below can only be called on an up() service
- bool wantsUrl(const String &urlPath) const;
- bool wantsPreview(const String &urlPath, size_t &wantedSize) const;
+ bool wantsUrl(const SBuf &urlPath) const;
+ bool wantsPreview(const SBuf &urlPath, size_t &wantedSize) const;
bool allows204() const;
bool allows206() const;
Comm::ConnectionPointer getConnection(bool isRetriable, bool &isReused);
void putConnection(const Comm::ConnectionPointer &conn, bool isReusable, bool sendReset, const char *comment);
void noteConnectionUse(const Comm::ConnectionPointer &conn);
void noteConnectionFailed(const char *comment);
void noteFailure(); // called by transactions to report service failure
void noteNewWaiter() {theAllWaiters++;} ///< New xaction waiting for service to be up or available
void noteGoneWaiter(); ///< An xaction is not waiting any more for service to be available
bool existWaiters() const {return (theAllWaiters > 0);} ///< if there are xactions waiting for the service to be available
//AsyncJob virtual methods
virtual bool doneAll() const { return Adaptation::Initiator::doneAll() && false;}
virtual void callException(const std::exception &e);
virtual void detach();
virtual bool detached() const;
=== modified file 'src/carp.cc'
--- src/carp.cc 2015-06-09 06:14:43 +0000
+++ src/carp.cc 2015-06-28 06:43:45 +0000
@@ -161,50 +161,48 @@
/* select CachePeer */
for (k = 0; k < n_carp_peers; ++k) {
SBuf key;
tp = carp_peers[k];
if (tp->options.carp_key.set) {
//this code follows urlCanonical's pattern.
// corner cases should use the canonical URL
if (tp->options.carp_key.scheme) {
key.append(request->url.getScheme().c_str());
if (key.length()) //if the scheme is not empty
key.append("://");
}
if (tp->options.carp_key.host) {
key.append(request->url.host());
}
if (tp->options.carp_key.port) {
key.appendf(":%u", request->url.port());
}
if (tp->options.carp_key.path) {
- String::size_type pos;
- if ((pos=request->urlpath.find('?'))!=String::npos)
- key.append(SBuf(request->urlpath.substr(0,pos)));
- else
- key.append(SBuf(request->urlpath));
+ // XXX: fix when path and query are separate
+ key.append(request->url.path().substr(0,request->url.path().find('?'))); // 0..N
}
if (tp->options.carp_key.params) {
- String::size_type pos;
- if ((pos=request->urlpath.find('?'))!=String::npos)
- key.append(SBuf(request->urlpath.substr(pos,request->urlpath.size())));
+ // XXX: fix when path and query are separate
+ SBuf::size_type pos;
+ if ((pos=request->url.path().find('?')) != SBuf::npos)
+ key.append(request->url.path().substr(pos)); // N..npos
}
}
// if the url-based key is empty, e.g. because the user is
// asking to balance on the path but the request doesn't supply any,
// then fall back to canonical URL
if (key.isEmpty())
key=SBuf(urlCanonical(request));
for (const char *c = key.rawContent(), *e=key.rawContent()+key.length(); c < e; ++c)
user_hash += ROTATE_LEFT(user_hash, 19) + *c;
combined_hash = (user_hash ^ tp->carp.hash);
combined_hash += combined_hash * 0x62531965;
combined_hash = ROTATE_LEFT(combined_hash, 21);
score = combined_hash * tp->carp.load_multiplier;
debugs(39, 3, "carpSelectParent: key=" << key << " name=" << tp->name << " combined_hash=" << combined_hash <<
" score=" << std::setprecision(0) << score);
if ((score > high_score) && peerHTTPOkay(tp, request)) {
p = tp;
=== modified file 'src/client_side.cc'
--- src/client_side.cc 2015-06-09 06:14:43 +0000
+++ src/client_side.cc 2015-06-18 09:50:39 +0000
@@ -2186,83 +2186,80 @@
", mime-header-size=" << hp->headerBlockSize() <<
", mime header block:\n" << hp->mimeHeader() << "\n----------");
/* Ok, all headers are received */
ClientHttpRequest *http = new ClientHttpRequest(csd);
http->req_sz = hp->messageHeaderSize();
ClientSocketContext *result = new ClientSocketContext(csd->clientConnection, http);
StoreIOBuffer tempBuffer;
tempBuffer.data = result->reqbuf;
tempBuffer.length = HTTP_REQBUF_SZ;
ClientStreamData newServer = new clientReplyContext(http);
ClientStreamData newClient = result;
clientStreamInit(&http->client_stream, clientGetMoreData, clientReplyDetach,
clientReplyStatus, newServer, clientSocketRecipient,
clientSocketDetach, newClient, tempBuffer);
/* set url */
- // XXX: c_str() does re-allocate but here replaces explicit malloc/free.
- // when internalCheck() accepts SBuf removing this will be a net gain for performance.
- SBuf tmp(hp->requestUri());
- const char *url = tmp.c_str();
-
debugs(33,5, "Prepare absolute URL from " <<
(csd->transparent()?"intercept":(csd->port->flags.accelSurrogate ? "accel":"")));
/* Rewrite the URL in transparent or accelerator mode */
/* NP: there are several cases to traverse here:
* - standard mode (forward proxy)
* - transparent mode (TPROXY)
* - transparent mode with failures
* - intercept mode (NAT)
* - intercept mode with failures
* - accelerator mode (reverse proxy)
- * - internal URL
+ * - internal relative-URL
* - mixed combos of the above with internal URL
* - remote interception with PROXY protocol
* - remote reverse-proxy with PROXY protocol
*/
if (csd->transparent()) {
/* intercept or transparent mode, properly working with no failures */
prepareTransparentURL(csd, http, hp);
- } else if (internalCheck(url)) {
+ } else if (internalCheck(hp->requestUri())) { // NP: only matches relative-URI
/* internal URL mode */
/* prepend our name & port */
- http->uri = xstrdup(internalLocalUri(NULL, url));
+ http->uri = xstrdup(internalLocalUri(NULL, hp->requestUri()));
// We just re-wrote the URL. Must replace the Host: header.
// But have not parsed there yet!! flag for local-only handling.
http->flags.internal = true;
} else if (csd->port->flags.accelSurrogate || csd->switchedToHttps()) {
/* accelerator mode */
prepareAcceleratedURL(csd, http, hp);
}
if (!http->uri) {
/* No special rewrites have been applied above, use the
* requested url. may be rewritten later, so make extra room */
int url_sz = hp->requestUri().length() + Config.appendDomainLen + 5;
http->uri = (char *)xcalloc(url_sz, 1);
- strcpy(http->uri, url);
+ // XXX: c_str() does re-allocate but here replaces explicit malloc/free.
+ SBuf tmp(hp->requestUri());
+ strcpy(http->uri, tmp.c_str());
}
result->flags.parsed_ok = 1;
return result;
}
bool
ConnStateData::In::maybeMakeSpaceAvailable()
{
if (buf.spaceSize() < 2) {
const SBuf::size_type haveCapacity = buf.length() + buf.spaceSize();
if (haveCapacity >= Config.maxRequestBufferSize) {
debugs(33, 4, "request buffer full: client_request_buffer_max_size=" << Config.maxRequestBufferSize);
return false;
}
if (haveCapacity == 0) {
// haveCapacity is based on the SBuf visible window of the MemBlob buffer, which may fill up.
// at which point bump the buffer back to default. This allocates a new MemBlob with any un-parsed bytes.
buf.reserveCapacity(CLIENT_REQ_BUF_SZ);
} else {
@@ -2546,45 +2543,45 @@
/** \par
* If transparent or interception mode is working clone the transparent and interception flags
* from the port settings to the request.
*/
if (http->clientConnection != NULL) {
request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
request->flags.interceptTproxy = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
static const bool proxyProtocolPort = (conn->port != NULL) ? conn->port->flags.proxySurrogate : false;
if (request->flags.interceptTproxy && !proxyProtocolPort) {
if (Config.accessList.spoof_client_ip) {
ACLFilledChecklist *checklist = clientAclChecklistCreate(Config.accessList.spoof_client_ip, http);
request->flags.spoofClientIp = (checklist->fastCheck() == ACCESS_ALLOWED);
delete checklist;
} else
request->flags.spoofClientIp = true;
} else
request->flags.spoofClientIp = false;
}
- if (internalCheck(request->urlpath.termedBuf())) {
+ if (internalCheck(request->url.path())) {
if (internalHostnameIs(request->url.host()) && request->url.port() == getMyPort()) {
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true));
http->flags.internal = true;
- } else if (Config.onoff.global_internal_static && internalStaticCheck(request->urlpath.termedBuf())) {
+ } 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->SetHost(internalHostname());
request->url.port(getMyPort());
http->flags.internal = true;
} else
debugs(33, 2, "internal URL found: " << request->url.getScheme() << "://" << request->url.authority(true) << " (not this proxy)");
}
request->flags.internal = http->flags.internal;
setLogUri (http, urlCanonicalClean(request.getRaw()));
request->client_addr = conn->clientConnection->remote; // XXX: remove reuest->client_addr member.
#if FOLLOW_X_FORWARDED_FOR
// indirect client gets stored here because it is an HTTP header result (from X-Forwarded-For:)
// not a details about teh TCP connection itself
request->indirect_client_addr = conn->clientConnection->remote;
#endif /* FOLLOW_X_FORWARDED_FOR */
request->my_addr = conn->clientConnection->local;
request->myportname = conn->port->name;
=== modified file 'src/clients/FtpGateway.cc'
--- src/clients/FtpGateway.cc 2015-06-22 11:52:31 +0000
+++ src/clients/FtpGateway.cc 2015-06-28 06:51:22 +0000
@@ -1064,127 +1064,125 @@
if (password[0])
return 1;
/* Setup default FTP password settings */
/* this has to be done last so that we can have a no-password case above. */
if (!password[0]) {
if (strcmp(user, "anonymous") == 0 && !flags.tried_auth_anonymous) {
xstrncpy(password, Config.Ftp.anon_user, MAX_URL);
flags.tried_auth_anonymous=1;
return 1;
} else if (!flags.tried_auth_nopass) {
xstrncpy(password, null_string, MAX_URL);
flags.tried_auth_nopass=1;
return 1;
}
}
return 0; /* different username */
}
-static String str_type_eq;
void
Ftp::Gateway::checkUrlpath()
{
- int l;
- size_t t;
+ static SBuf str_type_eq("type=");
+ auto t = request->url.path().rfind(';');
- if (str_type_eq.size()==0) //hack. String doesn't support global-static
- str_type_eq="type=";
-
- if ((t = request->urlpath.rfind(';')) != String::npos) {
- if (request->urlpath.substr(t+1,t+1+str_type_eq.size())==str_type_eq) {
- typecode = (char)xtoupper(request->urlpath[t+str_type_eq.size()+1]);
- request->urlpath.cut(t);
+ if (t != SBuf::npos) {
+ auto filenameEnd = t-1;
+ if (request->url.path().substr(++t).cmp(str_type_eq, str_type_eq.length()) == 0) {
+ t += str_type_eq.length();
+ typecode = (char)xtoupper(request->url.path()[t]);
+ request->url.path(request->url.path().substr(0,filenameEnd));
}
}
- l = request->urlpath.size();
+ int l = request->url.path().length();
/* check for null path */
if (!l) {
flags.isdir = 1;
flags.root_dir = 1;
flags.need_base_href = 1; /* Work around broken browsers */
- } else if (!request->urlpath.cmp("/%2f/")) {
+ } else if (!request->url.path().cmp("/%2f/")) {
/* UNIX root directory */
flags.isdir = 1;
flags.root_dir = 1;
- } else if ((l >= 1) && (request->urlpath[l - 1] == '/')) {
+ } else if ((l >= 1) && (request->url.path()[l-1] == '/')) {
/* Directory URL, ending in / */
flags.isdir = 1;
if (l == 1)
flags.root_dir = 1;
} else {
flags.dir_slash = 1;
}
}
void
Ftp::Gateway::buildTitleUrl()
{
title_url = "ftp://";
if (strcmp(user, "anonymous")) {
title_url.append(user);
title_url.append("@");
}
SBuf authority = request->url.authority(request->url.getScheme() != AnyP::PROTO_FTP);
title_url.append(authority.rawContent(), authority.length());
- title_url.append(request->urlpath);
+ title_url.append(request->url.path().rawContent(), request->url.path().length());
base_href = "ftp://";
if (strcmp(user, "anonymous") != 0) {
base_href.append(rfc1738_escape_part(user));
if (password_url) {
base_href.append(":");
base_href.append(rfc1738_escape_part(password));
}
base_href.append("@");
}
base_href.append(authority.rawContent(), authority.length());
- base_href.append(request->urlpath);
+ base_href.append(request->url.path().rawContent(), request->url.path().length());
base_href.append("/");
}
void
Ftp::Gateway::start()
{
if (!checkAuth(&request->header)) {
/* create appropriate reply */
HttpReply *reply = ftpAuthRequired(request, ftpRealm());
entry->replaceHttpReply(reply);
serverComplete();
return;
}
checkUrlpath();
buildTitleUrl();
- debugs(9, 5, HERE << "FD " << ctrl.conn->fd << " : host=" << request->url.host() <<
- ", path=" << request->urlpath << ", user=" << user << ", passwd=" << password);
+ debugs(9, 5, "FD " << ctrl.conn->fd << " : host=" << request->url.host() <<
+ ", path=" << request->url.path() << ", user=" << user << ", passwd=" << password);
state = BEGIN;
Ftp::Client::start();
}
/* ====================================================================== */
void
Ftp::Gateway::handleControlReply()
{
Ftp::Client::handleControlReply();
if (ctrl.message == NULL)
return; // didn't get complete reply yet
/* Copy the message except for the last line to cwd_message to be
* printed in error messages.
*/
for (wordlist *w = ctrl.message; w && w->next; w = w->next) {
cwd_message.append('\n');
cwd_message.append(w->key);
}
@@ -1348,99 +1346,98 @@
ftpState->writeCommand(cbuf);
ftpState->state = Ftp::Client::SENT_PASS;
}
static void
ftpReadPass(Ftp::Gateway * ftpState)
{
int code = ftpState->ctrl.replycode;
debugs(9, 3, HERE << "code=" << code);
if (code == 230) {
ftpSendType(ftpState);
} else {
ftpState->loginFailed();
}
}
static void
ftpSendType(Ftp::Gateway * ftpState)
{
- const char *t;
- const char *filename;
- char mode;
-
/* check the server control channel is still available */
if (!ftpState || !ftpState->haveControlChannel("ftpSendType"))
return;
/*
* Ref section 3.2.2 of RFC 1738
*/
- mode = ftpState->typecode;
+ char mode = ftpState->typecode;
switch (mode) {
case 'D':
mode = 'A';
break;
case 'A':
case 'I':
break;
default:
if (ftpState->flags.isdir) {
mode = 'A';
} else {
- t = ftpState->request->urlpath.rpos('/');
- filename = t ? t + 1 : ftpState->request->urlpath.termedBuf();
- mode = mimeGetTransferMode(filename);
+ auto t = ftpState->request->url.path().rfind('/');
+ // XXX: performance regression, c_str() may reallocate
+ SBuf filename = ftpState->request->url.path().substr(t != SBuf::npos ? t + 1 : 0);
+ mode = mimeGetTransferMode(filename.c_str());
}
break;
}
if (mode == 'I')
ftpState->flags.binary = 1;
else
ftpState->flags.binary = 0;
snprintf(cbuf, CTRL_BUFLEN, "TYPE %c\r\n", mode);
ftpState->writeCommand(cbuf);
ftpState->state = Ftp::Client::SENT_TYPE;
}
static void
ftpReadType(Ftp::Gateway * ftpState)
{
int code = ftpState->ctrl.replycode;
char *path;
char *d, *p;
debugs(9, 3, HERE << "code=" << code);
if (code == 200) {
- p = path = xstrdup(ftpState->request->urlpath.termedBuf());
+ // XXX: performance regression, c_str() may reallocate
+ SBuf tmp = ftpState->request->url.path();
+ p = path = xstrndup(tmp.c_str(),tmp.length());
if (*p == '/')
++p;
while (*p) {
d = p;
p += strcspn(p, "/");
if (*p) {
*p = '\0';
++p;
}
rfc1738_unescape(d);
if (*d)
wordlistAdd(&ftpState->pathcomps, d);
}
xfree(path);
@@ -2351,41 +2348,43 @@
ftpReadQuit(Ftp::Gateway * ftpState)
{
ftpState->serverComplete();
}
static void
ftpTrySlashHack(Ftp::Gateway * ftpState)
{
char *path;
ftpState->flags.try_slash_hack = 1;
/* Free old paths */
debugs(9, 3, HERE);
if (ftpState->pathcomps)
wordlistDestroy(&ftpState->pathcomps);
safe_free(ftpState->filepath);
/* Build the new path (urlpath begins with /) */
- path = xstrdup(ftpState->request->urlpath.termedBuf());
+ // XXX: performance regression. c_str() may reallocate, then xstrdup repeats.
+ SBuf tmp = ftpState->request->url.path();
+ path = xstrdup(tmp.c_str());
rfc1738_unescape(path);
ftpState->filepath = path;
/* And off we go */
ftpGetFile(ftpState);
}
/**
* Forget hack status. Next error is shown to the user
*/
void
Ftp::Gateway::unhack()
{
debugs(9, 3, HERE);
if (old_request != NULL) {
safe_free(old_request);
safe_free(old_reply);
@@ -2402,51 +2401,51 @@
debugs(9, 3, HERE);
if (old_request == NULL) {
old_request = ctrl.last_command;
ctrl.last_command = NULL;
old_reply = ctrl.last_reply;
ctrl.last_reply = NULL;
if (pathcomps == NULL && filepath != NULL)
old_filepath = xstrdup(filepath);
}
/* Jump to the "hack" state */
nextState(this);
}
static void
ftpFail(Ftp::Gateway *ftpState)
{
- debugs(9, 6, HERE << "flags(" <<
+ const bool slashHack = ftpState->request->url.path().caseCmp("/%2f", 4)==0;
+ debugs(9, 6, "flags(" <<
(ftpState->flags.isdir?"IS_DIR,":"") <<
(ftpState->flags.try_slash_hack?"TRY_SLASH_HACK":"") << "), " <<
"mdtm=" << ftpState->mdtm << ", size=" << ftpState->theSize <<
- "slashhack=" << (ftpState->request->urlpath.caseCmp("/%2f", 4)==0? "T":"F") );
+ "slashhack=" << (slashHack? "T":"F") );
/* Try the / hack to support "Netscape" FTP URL's for retreiving files */
if (!ftpState->flags.isdir && /* Not a directory */
- !ftpState->flags.try_slash_hack && /* Not in slash hack */
- ftpState->mdtm <= 0 && ftpState->theSize < 0 && /* Not known as a file */
- ftpState->request->urlpath.caseCmp("/%2f", 4) != 0) { /* No slash encoded */
+ !ftpState->flags.try_slash_hack && !slashHack && /* Not doing slash hack */
+ ftpState->mdtm <= 0 && ftpState->theSize < 0) { /* Not known as a file */
switch (ftpState->state) {
case Ftp::Client::SENT_CWD:
case Ftp::Client::SENT_RETR:
/* Try the / hack */
ftpState->hackShortcut(ftpTrySlashHack);
return;
default:
break;
}
}
ftpState->failed(ERR_NONE, 0);
/* failed() closes ctrl.conn and frees this */
}
Http::StatusCode
@@ -2515,80 +2514,81 @@
err.ftp.request = xstrdup(ftpState->ctrl.last_command);
if (ftpState->old_reply)
err.ftp.reply = xstrdup(ftpState->old_reply);
else if (ftpState->ctrl.last_reply)
err.ftp.reply = xstrdup(ftpState->ctrl.last_reply);
else
err.ftp.reply = xstrdup("");
// TODO: interpret as FTP-specific error code
err.detailError(code);
ftpState->entry->replaceHttpReply( err.BuildHttpReply() );
ftpSendQuit(ftpState);
}
void
Ftp::Gateway::appendSuccessHeader()
{
- const char *mime_type = NULL;
- const char *mime_enc = NULL;
- String urlpath = request->urlpath;
- const char *filename = NULL;
- const char *t = NULL;
-
debugs(9, 3, HERE);
if (flags.http_header_sent)
return;
HttpReply *reply = new HttpReply;
flags.http_header_sent = 1;
assert(entry->isEmpty());
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
entry->buffer(); /* released when done processing current data payload */
- filename = (t = urlpath.rpos('/')) ? t + 1 : urlpath.termedBuf();
+ SBuf urlPath = request->url.path();
+ auto t = urlPath.rfind('/');
+ SBuf filename = urlPath.substr(t != SBuf::npos ? t : 0);
+
+ const char *mime_type = NULL;
+ const char *mime_enc = NULL;
if (flags.isdir) {
mime_type = "text/html";
} else {
switch (typecode) {
case 'I':
mime_type = "application/octet-stream";
- mime_enc = mimeGetContentEncoding(filename);
+ // XXX: performance regression, c_str() may reallocate
+ mime_enc = mimeGetContentEncoding(filename.c_str());
break;
case 'A':
mime_type = "text/plain";
break;
default:
- mime_type = mimeGetContentType(filename);
- mime_enc = mimeGetContentEncoding(filename);
+ // XXX: performance regression, c_str() may reallocate
+ mime_type = mimeGetContentType(filename.c_str());
+ mime_enc = mimeGetContentEncoding(filename.c_str());
break;
}
}
/* set standard stuff */
if (0 == getCurrentOffset()) {
/* Full reply */
reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, theSize, mdtm, -2);
} else if (theSize < getCurrentOffset()) {
/*
* DPW 2007-05-04
* offset should not be larger than theSize. We should
* not be seeing this condition any more because we'll only
* send REST if we know the theSize and if it is less than theSize.
*/
debugs(0,DBG_CRITICAL,HERE << "Whoops! " <<
" current offset=" << getCurrentOffset() <<
", but theSize=" << theSize <<
". assuming full content response");
@@ -2629,52 +2629,52 @@
} else {
e->release();
}
}
HttpReply *
Ftp::Gateway::ftpAuthRequired(HttpRequest * request, const char *realm)
{
ErrorState err(ERR_CACHE_ACCESS_DENIED, Http::scUnauthorized, request);
HttpReply *newrep = err.BuildHttpReply();
#if HAVE_AUTH_MODULE_BASIC
/* add Authenticate header */
newrep->header.putAuth("Basic", realm);
#endif
return newrep;
}
const char *
Ftp::UrlWith2f(HttpRequest * request)
{
- String newbuf = "%2f";
+ SBuf newbuf("%2f");
if (request->url.getScheme() != AnyP::PROTO_FTP)
return NULL;
- if ( request->urlpath[0]=='/' ) {
- newbuf.append(request->urlpath);
- request->urlpath.absorb(newbuf);
+ if (request->url.path()[0] == '/') {
+ newbuf.append(request->url.path());
+ request->url.path(newbuf);
safe_free(request->canonical);
- } else if ( !strncmp(request->urlpath.termedBuf(), "%2f", 3) ) {
- newbuf.append(request->urlpath.substr(1,request->urlpath.size()));
- request->urlpath.absorb(newbuf);
+ } else if (!request->url.path().cmp(newbuf, 3)) {
+ newbuf.append(request->url.path().substr(1));
+ request->url.path(newbuf);
safe_free(request->canonical);
}
return urlCanonical(request);
}
void
Ftp::Gateway::printfReplyBody(const char *fmt, ...)
{
va_list args;
va_start (args, fmt);
static char buf[4096];
buf[0] = '\0';
vsnprintf(buf, 4096, fmt, args);
writeReplyBody(buf, strlen(buf));
va_end(args);
}
/**
* Call this when there is data from the origin server
=== modified file 'src/errorpage.cc'
--- src/errorpage.cc 2015-06-09 06:14:43 +0000
+++ src/errorpage.cc 2015-06-28 07:04:07 +0000
@@ -718,52 +718,44 @@
#if USE_AUTH
if (auth_user_request.getRaw() && auth_user_request->denyMessage())
str.appendf("Auth ErrMsg: %s\r\n", auth_user_request->denyMessage());
#endif
if (dnsError.size() > 0)
str.appendf("DNS ErrMsg: %s\r\n", dnsError.termedBuf());
/* - TimeStamp */
str.appendf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
/* - IP stuff */
str.appendf("ClientIP: %s\r\n", src_addr.toStr(ntoabuf,MAX_IPSTRLEN));
if (request && request->hier.host[0] != '\0') {
str.appendf("ServerIP: %s\r\n", request->hier.host);
}
str.append("\r\n", 2);
/* - HTTP stuff */
str.append("HTTP Request:\r\n", 15);
-
- if (NULL != request) {
- String urlpath_or_slash;
-
- if (request->urlpath.size() != 0)
- urlpath_or_slash = request->urlpath;
- else
- urlpath_or_slash = "/";
-
- str.appendf(SQUIDSBUFPH " " SQUIDSTRINGPH " %s/%d.%d\n",
+ if (request) {
+ str.appendf(SQUIDSBUFPH " " SQUIDSBUFPH " %s/%d.%d\n",
SQUIDSBUFPRINT(request->method.image()),
- SQUIDSTRINGPRINT(urlpath_or_slash),
+ SQUIDSBUFPRINT(request->url.path()),
AnyP::ProtocolType_str[request->http_ver.protocol],
request->http_ver.major, request->http_ver.minor);
request->header.packInto(&str);
}
str.append("\r\n", 2);
/* - FTP stuff */
if (ftp.request) {
str.appendf("FTP Request: %s\r\n", ftp.request);
str.appendf("FTP Reply: %s\r\n", (ftp.reply? ftp.reply:"[none]"));
str.append("FTP Msg: ", 9);
wordlistCat(ftp.server_msg, &str);
str.append("\r\n", 2);
}
str.append("\r\n", 2);
mb->appendf("&body=%s", rfc1738_escape_part(str.buf));
str.clean();
return 0;
@@ -936,57 +928,51 @@
case 'p':
if (request) {
mb.appendf("%u", request->url.port());
} else if (!building_deny_info_url) {
p = "[unknown port]";
}
break;
case 'P':
if (request) {
p = request->url.getScheme().c_str();
} else if (!building_deny_info_url) {
p = "[unknown protocol]";
}
break;
case 'R':
if (building_deny_info_url) {
if (request != NULL) {
- p = (request->urlpath.size() != 0 ? request->urlpath.termedBuf() : "/");
+ SBuf tmp = request->url.path();
+ p = tmp.c_str();
no_urlescape = 1;
} else
p = "[no request]";
break;
}
- if (NULL != request) {
- String urlpath_or_slash;
-
- if (request->urlpath.size() != 0)
- urlpath_or_slash = request->urlpath;
- else
- urlpath_or_slash = "/";
-
- mb.appendf(SQUIDSBUFPH " " SQUIDSTRINGPH " %s/%d.%d\n",
+ if (request != NULL) {
+ mb.appendf(SQUIDSBUFPH " " SQUIDSBUFPH " %s/%d.%d\n",
SQUIDSBUFPRINT(request->method.image()),
- SQUIDSTRINGPRINT(urlpath_or_slash),
+ SQUIDSBUFPRINT(request->url.path()),
AnyP::ProtocolType_str[request->http_ver.protocol],
request->http_ver.major, request->http_ver.minor);
request->header.packInto(&mb, true); //hide authorization data
} else if (request_hdrs) {
p = request_hdrs;
} else {
p = "[no request]";
}
break;
case 's':
/* for backward compat we make %s show the full URL. Drop this in some future release. */
if (building_deny_info_url) {
p = request ? urlCanonical(request) : url;
debugs(0, DBG_CRITICAL, "WARNING: deny_info now accepts coded tags. Use %u to get the full URL instead of %s");
} else
p = visible_appname_string;
break;
case 'S':
=== modified file 'src/external_acl.cc'
--- src/external_acl.cc 2015-06-09 06:14:43 +0000
+++ src/external_acl.cc 2015-06-28 07:03:42 +0000
@@ -961,42 +961,44 @@
str = buf;
break;
case Format::LFT_CLIENT_REQ_URI:
str = urlCanonical(request);
break;
case Format::LFT_CLIENT_REQ_URLDOMAIN:
str = request->url.host();
break;
case Format::LFT_CLIENT_REQ_URLSCHEME:
str = request->url.getScheme().c_str();
break;
case Format::LFT_CLIENT_REQ_URLPORT:
snprintf(buf, sizeof(buf), "%u", request->url.port());
str = buf;
break;
- case Format::LFT_CLIENT_REQ_URLPATH:
- str = request->urlpath.termedBuf();
+ case Format::LFT_CLIENT_REQ_URLPATH: {
+ SBuf tmp = request->url.path();
+ str = tmp.c_str();
+ }
break;
case Format::LFT_CLIENT_REQ_METHOD: {
const SBuf &s = request->method.image();
sb.append(s.rawContent(), s.length());
}
str = sb.termedBuf();
break;
case Format::LFT_ADAPTED_REQUEST_HEADER:
if (format->header_id == -1)
sb = request->header.getByName(format->header);
else
sb = request->header.getStrOrList(format->header_id);
str = sb.termedBuf();
break;
case Format::LFT_ADAPTED_REQUEST_HEADER_ELEM:
if (format->header_id == -1)
sb = request->header.getByNameListMember(format->header, format->member, format->separator);
=== modified file 'src/format/Format.cc'
--- src/format/Format.cc 2015-06-09 06:14:43 +0000
+++ src/format/Format.cc 2015-06-09 12:13:49 +0000
@@ -954,41 +954,42 @@
}
break;
case LFT_CLIENT_REQ_URLDOMAIN:
if (al->request) {
out = al->request->url.host();
quote = 1;
}
break;
case LFT_CLIENT_REQ_URLPORT:
if (al->request) {
outint = al->request->url.port();
doint = 1;
}
break;
case LFT_REQUEST_URLPATH_OLD_31:
case LFT_CLIENT_REQ_URLPATH:
if (al->request) {
- out = al->request->urlpath.termedBuf();
+ SBuf s = al->request->url.path();
+ out = s.c_str();
quote = 1;
}
break;
case LFT_CLIENT_REQ_VERSION:
if (al->request) {
snprintf(tmp, sizeof(tmp), "%d.%d", (int) al->request->http_ver.major, (int) al->request->http_ver.minor);
out = tmp;
}
break;
case LFT_REQUEST_METHOD:
if (al->_private.method_str) // ICP, HTCP method code
out = al->_private.method_str;
else {
const SBuf &s = al->http.method.image();
sb.append(s.rawContent(), s.length());
out = sb.termedBuf();
quote = 1;
}
@@ -1027,41 +1028,42 @@
quote = 1;
}
break;
case LFT_SERVER_REQ_URLDOMAIN:
if (al->adapted_request) {
out = al->adapted_request->url.host();
quote = 1;
}
break;
case LFT_SERVER_REQ_URLPORT:
if (al->adapted_request) {
outint = al->adapted_request->url.port();
doint = 1;
}
break;
case LFT_SERVER_REQ_URLPATH:
if (al->adapted_request) {
- out = al->adapted_request->urlpath.termedBuf();
+ SBuf s = al->adapted_request->url.path();
+ out = s.c_str();
quote = 1;
}
break;
case LFT_SERVER_REQ_VERSION:
if (al->adapted_request) {
snprintf(tmp, sizeof(tmp), "%d.%d",
(int) al->adapted_request->http_ver.major,
(int) al->adapted_request->http_ver.minor);
out = tmp;
}
break;
case LFT_CLIENT_REQUEST_SIZE_TOTAL:
outoff = al->http.clientRequestSz.messageTotal();
dooff = 1;
break;
case LFT_CLIENT_REQUEST_SIZE_HEADERS:
outoff = al->http.clientRequestSz.header;
=== modified file 'src/gopher.cc'
--- src/gopher.cc 2015-06-09 06:14:43 +0000
+++ src/gopher.cc 2015-06-14 22:04:38 +0000
@@ -4,40 +4,41 @@
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
/* DEBUG: section 10 Gopher */
#include "squid.h"
#include "comm.h"
#include "comm/Read.h"
#include "comm/Write.h"
#include "errorpage.h"
#include "fd.h"
#include "FwdState.h"
#include "globals.h"
#include "html_quote.h"
#include "HttpReply.h"
#include "HttpRequest.h"
#include "MemBuf.h"
#include "mime.h"
+#include "parser/Tokenizer.h"
#include "rfc1738.h"
#include "SquidConfig.h"
#include "SquidTime.h"
#include "StatCounters.h"
#include "Store.h"
#include "tools.h"
#if USE_DELAY_POOLS
#include "DelayPools.h"
#include "MemObject.h"
#endif
/* gopher type code from rfc. Anawat. */
#define GOPHER_FILE '0'
#define GOPHER_DIRECTORY '1'
#define GOPHER_CSO '2'
#define GOPHER_ERROR '3'
#define GOPHER_MACBINHEX '4'
#define GOPHER_DOSBIN '5'
#define GOPHER_UUENCODED '6'
@@ -236,57 +237,61 @@
}
assert(entry->isEmpty());
EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
HttpReply *reply = new HttpReply;
entry->buffer();
reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2);
if (mime_enc)
reply->header.putStr(HDR_CONTENT_ENCODING, mime_enc);
entry->replaceHttpReply(reply);
}
/**
* Parse a gopher request into components. By Anawat.
*/
static void
gopher_request_parse(const HttpRequest * req, char *type_id, char *request)
{
- const char *path = req->urlpath.termedBuf();
+ ::Parser::Tokenizer tok(req->url.path());
if (request)
- request[0] = '\0';
+ *request = 0;
- if (path && (*path == '/'))
- ++path;
+ tok.skip('/'); // ignore failures? path could be ab-empty
- if (!path || !*path) {
+ if (tok.atEnd()) {
*type_id = GOPHER_DIRECTORY;
return;
}
- *type_id = path[0];
+ static const CharacterSet anyByte("UTF-8",0x00, 0xFF);
+
+ SBuf typeId;
+ (void)tok.prefix(typeId, anyByte, 1); // never fails since !atEnd()
+ *type_id = typeId[0];
if (request) {
- xstrncpy(request, path + 1, MAX_URL);
+ SBuf path = tok.remaining();
+ xstrncpy(request, path.c_str(), MAX_URL);
/* convert %xx to char */
rfc1738_unescape(request);
}
}
/**
* Parse the request to determine whether it is cachable.
*
* \param req Request data.
* \retval 0 Not cachable.
* \retval 1 Cachable.
*/
int
gopherCachable(const HttpRequest * req)
{
int cachable = 1;
char type_id;
/* parse to see type */
gopher_request_parse(req,
&type_id,
=== modified file 'src/http.cc'
--- src/http.cc 2015-06-19 07:13:57 +0000
+++ src/http.cc 2015-06-19 07:20:11 +0000
@@ -1292,62 +1292,61 @@
if (!flags.headers_parsed && !eof) {
debugs(11, 9, "needs more at " << inBuf.length());
flags.do_next_read = true;
/** \retval false If we have not finished parsing the headers and may get more data.
* Schedules more reads to retrieve the missing data.
*/
maybeReadVirginBody(); // schedules all kinds of reads; TODO: rename
return false;
}
/** If we are done with parsing, check for errors */
err_type error = ERR_NONE;
if (flags.headers_parsed) { // parsed headers, possibly with errors
// check for header parsing errors
if (HttpReply *vrep = virginReply()) {
const Http::StatusCode s = vrep->sline.status();
const AnyP::ProtocolVersion &v = vrep->sline.version;
if (s == Http::scInvalidHeader && v != Http::ProtocolVersion(0,9)) {
- debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Bad header encountered from " << entry->url() << " AKA " << request->url.host() << request->urlpath.termedBuf());
+ debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Bad header encountered from " << entry->url() << " AKA " << request->url);
error = ERR_INVALID_RESP;
} else if (s == Http::scHeaderTooLarge) {
fwd->dontRetry(true);
error = ERR_TOO_BIG;
} else {
return true; // done parsing, got reply, and no error
}
} else {
// parsed headers but got no reply
- debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No reply at all for " << entry->url() << " AKA " << request->url.host() << request->urlpath.termedBuf());
+ debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: No reply at all for " << entry->url() << " AKA " << request->url);
error = ERR_INVALID_RESP;
}
} else {
assert(eof);
if (inBuf.length()) {
error = ERR_INVALID_RESP;
- debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Headers did not parse at all for " << entry->url() << " AKA " << request->url.host() << request->urlpath.termedBuf());
+ debugs(11, DBG_IMPORTANT, "WARNING: HTTP: Invalid Response: Headers did not parse at all for " << entry->url() << " AKA " << request->url);
} else {
error = ERR_ZERO_SIZE_OBJECT;
- debugs(11, (request->flags.accelerated?DBG_IMPORTANT:2), "WARNING: HTTP: Invalid Response: No object data received for " <<
- entry->url() << " AKA " << request->url.host() << request->urlpath.termedBuf());
+ debugs(11, (request->flags.accelerated?DBG_IMPORTANT:2), "WARNING: HTTP: Invalid Response: No object data received for " << entry->url() << " AKA " << request->url);
}
}
assert(error != ERR_NONE);
entry->reset();
fwd->fail(new ErrorState(error, Http::scBadGateway, fwd->request));
flags.do_next_read = false;
serverConnection->close();
return false; // quit on error
}
/** truncate what we read if we read too much so that writeReplyBody()
writes no more than what we should have read */
void
HttpStateData::truncateVirginBody()
{
assert(flags.headers_parsed);
HttpReply *vrep = virginReply();
int64_t clen = -1;
@@ -2150,45 +2149,48 @@
return result;
}
/* build request prefix and append it to a given MemBuf;
* return the length of the prefix */
mb_size_t
HttpStateData::buildRequestPrefix(MemBuf * mb)
{
const int offset = mb->size;
/* Uses a local httpver variable to print the HTTP label
* since the HttpRequest may have an older version label.
* XXX: This could create protocol bugs as the headers sent and
* flow control should all be based on the HttpRequest version
* not the one we are sending. Needs checking.
*/
const AnyP::ProtocolVersion httpver = Http::ProtocolVersion();
const char * url;
if (_peer && !_peer->options.originserver)
url = urlCanonical(request);
- else
- url = request->urlpath.termedBuf();
+ else {
+ // XXX: performance regression, c_str() reallocates
+ SBuf tmp = request->url.path();
+ url = tmp.c_str();
+ }
mb->appendf(SQUIDSBUFPH " %s %s/%d.%d\r\n",
SQUIDSBUFPRINT(request->method.image()),
- url && *url ? url : "/",
+ url,
AnyP::ProtocolType_str[httpver.protocol],
httpver.major,httpver.minor);
/* build and pack headers */
{
HttpHeader hdr(hoRequest);
httpBuildRequestHeader(request, entry, fwd->al, &hdr, flags);
if (request->flags.pinned && request->flags.connectionAuth)
request->flags.authSent = true;
else if (hdr.has(HDR_AUTHORIZATION))
request->flags.authSent = true;
hdr.packInto(mb);
hdr.clean();
}
/* append header terminator */
mb->append(crlf, 2);
return mb->size - offset;
}
=== modified file 'src/icmp/net_db.cc'
--- src/icmp/net_db.cc 2015-06-09 06:14:43 +0000
+++ src/icmp/net_db.cc 2015-06-19 11:45:11 +0000
@@ -1272,41 +1272,42 @@
}
assert(0 == i);
s->flush();
memFree(buf, MEM_4K_BUF);
#else
reply->setHeaders(Http::scBadRequest, "Bad Request", NULL, -1, squid_curtime, -2);
s->replaceHttpReply(reply);
storeAppendPrintf(s, "NETDB support not compiled into this Squid cache.\n");
#endif
s->complete();
}
void
netdbExchangeStart(void *data)
{
#if USE_ICMP
CachePeer *p = (CachePeer *)data;
- char *uri = internalRemoteUri(p->host, p->http_port, "/squid-internal-dynamic/", "netdb");
+ static const SBuf netDB("netdb");
+ char *uri = internalRemoteUri(p->host, p->http_port, "/squid-internal-dynamic/", netDB);
debugs(38, 3, "netdbExchangeStart: Requesting '" << uri << "'");
assert(NULL != uri);
HttpRequest *req = HttpRequest::CreateFromUrl(uri);
if (req == NULL) {
debugs(38, DBG_IMPORTANT, "netdbExchangeStart: Bad URI " << uri);
return;
}
netdbExchangeState *ex = new netdbExchangeState(p, req);
ex->e = storeCreateEntry(uri, uri, RequestFlags(), Http::METHOD_GET);
assert(NULL != ex->e);
StoreIOBuffer tempBuffer;
tempBuffer.length = ex->buf_sz;
tempBuffer.data = ex->buf;
ex->sc = storeClientListAdd(ex->e, ex);
storeClientCopy(ex->sc, ex->e, tempBuffer,
=== modified file 'src/internal.cc'
--- src/internal.cc 2015-06-09 06:14:43 +0000
+++ src/internal.cc 2015-06-28 07:19:47 +0000
@@ -15,134 +15,140 @@
#include "HttpReply.h"
#include "HttpRequest.h"
#include "icmp/net_db.h"
#include "MemBuf.h"
#include "SquidConfig.h"
#include "SquidTime.h"
#include "Store.h"
#include "tools.h"
#include "URL.h"
#include "util.h"
#include "wordlist.h"
/* called when we "miss" on an internal object;
* generate known dynamic objects,
* return Http::scNotFound for others
*/
void
internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest * request, StoreEntry * entry)
{
ErrorState *err;
- const char *upath = request->urlpath.termedBuf();
- debugs(76, 3, HERE << clientConn << " requesting '" << upath << "'");
+ const SBuf upath = request->url.path();
+ debugs(76, 3, clientConn << " requesting '" << upath << "'");
- if (0 == strcmp(upath, "/squid-internal-dynamic/netdb")) {
+ static const SBuf netdbUri("/squid-internal-dynamic/netdb");
+ static const SBuf storeDigestUri("/squid-internal-periodic/store_digest");
+ static const SBuf mgrPfx("/squid-internal-mgr/");
+
+ if (upath.cmp(netdbUri) == 0) {
netdbBinaryExchange(entry);
- } else if (0 == strcmp(upath, "/squid-internal-periodic/store_digest")) {
+ } else if (upath.cmp(storeDigestUri) == 0) {
#if USE_CACHE_DIGESTS
const char *msgbuf = "This cache is currently building its digest.\n";
#else
const char *msgbuf = "This cache does not support Cache Digests.\n";
#endif
HttpReply *reply = new HttpReply;
reply->setHeaders(Http::scNotFound, "Not Found", "text/plain", strlen(msgbuf), squid_curtime, -2);
entry->replaceHttpReply(reply);
entry->append(msgbuf, strlen(msgbuf));
entry->complete();
- } else if (0 == strncmp(upath, "/squid-internal-mgr/", 20)) {
- debugs(17, 2, "calling CacheManager due to URL-path /squid-internal-mgr/");
+ } else if (upath.cmp(mgrPfx, mgrPfx.length()) == 0) {
+ debugs(17, 2, "calling CacheManager due to URL-path " << mgrPfx);
CacheManager::GetInstance()->Start(clientConn, request, entry);
} else {
debugObj(76, 1, "internalStart: unknown request:\n",
request, (ObjPackMethod) & httpRequestPack);
err = new ErrorState(ERR_INVALID_REQ, Http::scNotFound, request);
errorAppendEntry(entry, err);
}
}
-int
-internalCheck(const char *urlpath)
+bool
+internalCheck(const SBuf &urlPath)
{
- return (0 == strncmp(urlpath, "/squid-internal-", 16));
+ static const SBuf InternalPfx("/squid-internal-");
+ return urlPath.cmp(InternalPfx, InternalPfx.length()) == 0;
}
-int
-internalStaticCheck(const char *urlpath)
+bool
+internalStaticCheck(const SBuf &urlPath)
{
- return (0 == strncmp(urlpath, "/squid-internal-static", 22));
+ static const SBuf InternalStaticPfx("/squid-internal-static");
+ return urlPath.cmp(InternalStaticPfx, InternalStaticPfx.length()) == 0;
}
/*
* makes internal url with a given host and port (remote internal url)
*/
char *
-internalRemoteUri(const char *host, unsigned short port, const char *dir, const char *name)
+internalRemoteUri(const char *host, unsigned short port, const char *dir, const SBuf &name)
{
static char lc_host[SQUIDHOSTNAMELEN];
- assert(host && name);
+ assert(host && !name.isEmpty());
/* convert host name to lower case */
xstrncpy(lc_host, host, SQUIDHOSTNAMELEN);
Tolower(lc_host);
/* check for an IP address and format appropriately if found */
Ip::Address test = lc_host;
if ( !test.isAnyAddr() ) {
test.toHostStr(lc_host,SQUIDHOSTNAMELEN);
}
/*
* append the domain in order to mirror the requests with appended
* domains
*/
/* For IPv6 addresses also check for a colon */
if (Config.appendDomain && !strchr(lc_host, '.') && !strchr(lc_host, ':'))
strncat(lc_host, Config.appendDomain, SQUIDHOSTNAMELEN -
strlen(lc_host) - 1);
/* build URI */
URL tmp(AnyP::PROTO_HTTP);
tmp.host(lc_host);
if (port)
tmp.port(port);
static MemBuf mb;
mb.reset();
mb.appendf("http://" SQUIDSBUFPH, SQUIDSBUFPRINT(tmp.authority()));
if (dir)
mb.append(dir, strlen(dir));
- mb.append(name, strlen(name));
+ mb.append(name.rawContent(), name.length());
/* return a pointer to a local static buffer */
return mb.buf;
}
/*
* makes internal url with local host and port
*/
char *
-internalLocalUri(const char *dir, const char *name)
+internalLocalUri(const char *dir, const SBuf &name)
{
return internalRemoteUri(getMyHostname(),
getMyPort(), dir, name);
}
const char *
internalHostname(void)
{
LOCAL_ARRAY(char, host, SQUIDHOSTNAMELEN + 1);
xstrncpy(host, getMyHostname(), SQUIDHOSTNAMELEN);
/* For IPv6 addresses also check for a colon */
if (Config.appendDomain && !strchr(host, '.') && !strchr(host, ':'))
strncat(host, Config.appendDomain, SQUIDHOSTNAMELEN -
strlen(host) - 1);
Tolower(host);
return host;
}
=== modified file 'src/internal.h'
--- src/internal.h 2015-01-13 07:25:36 +0000
+++ src/internal.h 2015-06-19 11:44:31 +0000
@@ -1,30 +1,32 @@
/*
* Copyright (C) 1996-2015 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.
*/
/*
* DEBUG: section 76 Internal Squid Object handling
* AUTHOR: Duane, Alex, Henrik
*/
#ifndef SQUID_INTERNAL_H_
#define SQUID_INTERNAL_H_
#include "comm/forward.h"
+
class HttpRequest;
+class SBuf;
class StoreEntry;
void internalStart(const Comm::ConnectionPointer &clientConn, HttpRequest *, StoreEntry *);
-int internalCheck(const char *urlpath);
-int internalStaticCheck(const char *urlpath);
-char *internalLocalUri(const char *dir, const char *name);
-char *internalRemoteUri(const char *, unsigned short, const char *, const char *);
+bool internalCheck(const SBuf &urlPath);
+bool internalStaticCheck(const SBuf &urlPath);
+char *internalLocalUri(const char *dir, const SBuf &name);
+char *internalRemoteUri(const char *, unsigned short, const char *, const SBuf &);
const char *internalHostname(void);
int internalHostnameIs(const char *);
#endif /* SQUID_INTERNAL_H_ */
=== modified file 'src/mime.cc'
--- src/mime.cc 2015-06-18 15:11:24 +0000
+++ src/mime.cc 2015-06-18 16:36:22 +0000
@@ -101,89 +101,89 @@
else if (!strcmp(m->content_encoding, dash_str))
(void) 0;
else {
/* Assume we matched /\.\w$/ and cut off the last extension */
if ((t = strrchr(name, '.'))) {
*t = '\0';
} else {
/* What? A encoding without a extension? */
m = NULL;
}
}
} while (t);
xfree(name);
return m;
}
MimeIcon::MimeIcon(const char *aName) :
icon_(aName)
{
- url_ = xstrdup(internalLocalUri("/squid-internal-static/icons/", icon_.c_str()));
+ url_ = xstrdup(internalLocalUri("/squid-internal-static/icons/", icon_));
}
MimeIcon::~MimeIcon()
{
xfree(url_);
}
void
MimeIcon::setName(char const *aString)
{
xfree(url_);
icon_ = aString;
- url_ = xstrdup(internalLocalUri("/squid-internal-static/icons/", icon_.c_str()));
+ url_ = xstrdup(internalLocalUri("/squid-internal-static/icons/", icon_));
}
SBuf
MimeIcon::getName() const
{
return icon_;
}
const SBuf
mimeGetIcon(const char *fn)
{
MimeEntry *m = mimeGetEntry(fn, 1);
if (!m || !m->theIcon.getName().cmp(dash_str))
return SBuf();
return m->theIcon.getName();
}
const char *
mimeGetIconURL(const char *fn)
{
SBuf icon(mimeGetIcon(fn));
if (icon.isEmpty())
return null_string;
if (Config.icons.use_short_names) {
static SBuf mb;
mb.clear();
mb.append("/squid-internal-static/icons/");
mb.append(icon);
return mb.c_str();
} else {
- return internalLocalUri("/squid-internal-static/icons/", icon.c_str());
+ return internalLocalUri("/squid-internal-static/icons/", icon);
}
}
const char *
mimeGetContentType(const char *fn)
{
MimeEntry *m = mimeGetEntry(fn, 1);
if (m == NULL)
return NULL;
if (!strcmp(m->content_type, dash_str))
return NULL;
return m->content_type;
}
const char *
mimeGetContentEncoding(const char *fn)
{
=== modified file 'src/peer_digest.cc'
--- src/peer_digest.cc 2015-02-04 03:22:38 +0000
+++ src/peer_digest.cc 2015-06-19 11:45:55 +0000
@@ -313,41 +313,41 @@
/* ask store for a digest */
static void
peerDigestRequest(PeerDigest * pd)
{
CachePeer *p = pd->peer;
StoreEntry *e, *old_e;
char *url = NULL;
const cache_key *key;
HttpRequest *req;
StoreIOBuffer tempBuffer;
pd->req_result = NULL;
pd->flags.requested = true;
/* compute future request components */
if (p->digest_url)
url = xstrdup(p->digest_url);
else
- url = xstrdup(internalRemoteUri(p->host, p->http_port, "/squid-internal-periodic/", StoreDigestFileName));
+ url = xstrdup(internalRemoteUri(p->host, p->http_port, "/squid-internal-periodic/", SBuf(StoreDigestFileName)));
req = HttpRequest::CreateFromUrl(url);
assert(req);
key = storeKeyPublicByRequest(req);
debugs(72, 2, "peerDigestRequest: " << url << " key: " << storeKeyText(key));
/* add custom headers */
assert(!req->header.len);
req->header.putStr(HDR_ACCEPT, StoreDigestMimeStr);
req->header.putStr(HDR_ACCEPT, "text/html");
if (p->login &&
p->login[0] != '*' &&
strcmp(p->login, "PASS") != 0 &&
strcmp(p->login, "PASSTHRU") != 0 &&
=== modified file 'src/tests/testHttpRequest.cc'
--- src/tests/testHttpRequest.cc 2015-06-09 06:14:43 +0000
+++ src/tests/testHttpRequest.cc 2015-06-23 11:16:56 +0000
@@ -32,138 +32,138 @@
{
Mem::Init();
httpHeaderInitModule();
}
/*
* Test creating an HttpRequest object from a Url and method
*/
void
testHttpRequest::testCreateFromUrlAndMethod()
{
/* vanilla url */
unsigned short expected_port;
char * url = xstrdup("http://foo:90/bar");
HttpRequest *aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_GET);
expected_port = 90;
HttpRequest *nullRequest = NULL;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/bar"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://foo:90/bar"), String(url));
xfree(url);
/* vanilla url, different method */
url = xstrdup("http://foo/bar");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_PUT);
expected_port = 80;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_PUT);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/bar"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://foo/bar"), String(url));
xfree(url);
/* a connect url with non-CONNECT data */
url = xstrdup(":foo/bar");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_CONNECT);
xfree(url);
CPPUNIT_ASSERT_EQUAL(nullRequest, aRequest);
/* a CONNECT url with CONNECT data */
url = xstrdup("foo:45");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_CONNECT);
expected_port = 45;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_CONNECT);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String(""), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf(), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_NONE, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("foo:45"), String(url));
xfree(url);
}
/*
* Test creating an HttpRequest object from a Url alone.
*/
void
testHttpRequest::testCreateFromUrl()
{
/* vanilla url */
unsigned short expected_port;
char * url = xstrdup("http://foo:90/bar");
HttpRequest *aRequest = HttpRequest::CreateFromUrl(url);
expected_port = 90;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("foo"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/bar"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/bar"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://foo:90/bar"), String(url));
xfree(url);
}
/*
* Test BUG: URL '2000:800:45' opens host 2000 port 800 !!
*/
void
testHttpRequest::testIPv6HostColonBug()
{
unsigned short expected_port;
char * url = NULL;
HttpRequest *aRequest = NULL;
/* valid IPv6 address without port */
url = xstrdup("http://[2000:800::45]/foo");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_GET);
expected_port = 80;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/foo"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://[2000:800::45]/foo"), String(url));
xfree(url);
/* valid IPv6 address with port */
url = xstrdup("http://[2000:800::45]:90/foo");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_GET);
expected_port = 90;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/foo"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://[2000:800::45]:90/foo"), String(url));
xfree(url);
/* IPv6 address as invalid (bug trigger) */
url = xstrdup("http://2000:800::45/foo");
aRequest = HttpRequest::CreateFromUrlAndMethod(url, Http::METHOD_GET);
expected_port = 80;
CPPUNIT_ASSERT_EQUAL(expected_port, aRequest->url.port());
CPPUNIT_ASSERT(aRequest->method == Http::METHOD_GET);
CPPUNIT_ASSERT_EQUAL(String("[2000:800::45]"), String(aRequest->url.host()));
- CPPUNIT_ASSERT_EQUAL(String("/foo"), aRequest->urlpath);
+ CPPUNIT_ASSERT_EQUAL(SBuf("/foo"), aRequest->url.path());
CPPUNIT_ASSERT_EQUAL(AnyP::PROTO_HTTP, static_cast<AnyP::ProtocolType>(aRequest->url.getScheme()));
CPPUNIT_ASSERT_EQUAL(String("http://2000:800::45/foo"), String(url));
xfree(url);
}
void
testHttpRequest::testSanityCheckStartLine()
{
MemBuf input;
PrivateHttpRequest engine;
Http::StatusCode error = Http::scNone;
size_t hdr_len;
input.init();
// a valid request line
input.append("GET / HTTP/1.1\n\n", 16);
hdr_len = headersEnd(input.content(), input.contentSize());
CPPUNIT_ASSERT(engine.doSanityCheckStartLine(input.content(), hdr_len, &error) );
CPPUNIT_ASSERT_EQUAL(error, Http::scNone);
input.reset();
=== modified file 'src/url.cc'
--- src/url.cc 2015-06-09 06:14:43 +0000
+++ src/url.cc 2015-06-15 10:46:18 +0000
@@ -27,56 +27,75 @@
static const char valid_hostname_chars_u[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-._"
"[:]"
;
static const char valid_hostname_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789-."
"[:]"
;
const SBuf &
URL::Asterisk()
{
static SBuf star("*");
return star;
}
+const SBuf &
+URL::SlashPath()
+{
+ static SBuf slash("/");
+ return slash;
+}
+
void
URL::host(const char *src)
{
hostAddr_.setEmpty();
hostAddr_ = src;
if (hostAddr_.isAnyAddr()) {
xstrncpy(host_, src, sizeof(host_));
hostIsNumeric_ = false;
} else {
hostAddr_.toHostStr(host_, sizeof(host_));
debugs(23, 3, "given IP: " << hostAddr_);
hostIsNumeric_ = 1;
}
touch();
}
+const SBuf &
+URL::path() const
+{
+ // RFC 3986 section 3.3 says path can be empty (path-abempty).
+ // RFC 7230 sections 2.7.3, 5.3.1, 5.7.2 - says path cannot be empty, default to "/"
+ // at least when sending and using. We must still accept path-abempty as input.
+ if (path_.isEmpty() && (scheme_ == AnyP::PROTO_HTTP || scheme_ == AnyP::PROTO_HTTPS))
+ return SlashPath();
+
+ return path_;
+}
+
void
urlInitialize(void)
{
debugs(23, 5, "urlInitialize: Initializing...");
/* this ensures that the number of protocol strings is the same as
* the enum slots allocated because the last enum is always 'MAX'.
*/
assert(strcmp(AnyP::ProtocolType_str[AnyP::PROTO_MAX], "MAX") == 0);
/*
* These test that our matchDomainName() function works the
* way we expect it to.
*/
assert(0 == matchDomainName("foo.com", "foo.com"));
assert(0 == matchDomainName(".foo.com", "foo.com"));
assert(0 == matchDomainName("foo.com", ".foo.com"));
assert(0 == matchDomainName(".foo.com", ".foo.com"));
assert(0 == matchDomainName("x.foo.com", ".foo.com"));
assert(0 != matchDomainName("x.foo.com", "foo.com"));
assert(0 != matchDomainName("foo.com", "x.foo.com"));
assert(0 != matchDomainName("bar.com", "foo.com"));
@@ -469,96 +488,96 @@
authorityHttp_ = authorityWithPort_;
// authorityForm_ only has :port if it is non-default
authorityWithPort_.appendf(":%u",port());
if (port() != getScheme().defaultPort())
authorityHttp_ = authorityWithPort_;
}
return requirePort ? authorityWithPort_ : authorityHttp_;
}
const char *
urlCanonical(HttpRequest * request)
{
LOCAL_ARRAY(char, urlbuf, MAX_URL);
if (request->canonical)
return request->canonical;
if (request->url.getScheme() == AnyP::PROTO_URN) {
- snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
- SQUIDSTRINGPRINT(request->urlpath));
+ snprintf(urlbuf, MAX_URL, "urn:" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(request->url.path()));
} else {
SBuf authorityForm;
switch (request->method.id()) {
case Http::METHOD_CONNECT:
authorityForm = request->url.authority(true); // host:port
snprintf(urlbuf, MAX_URL, SQUIDSBUFPH, SQUIDSBUFPRINT(authorityForm));
break;
default: {
authorityForm = request->url.authority(); // host[:port]
- snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH SQUIDSTRINGPH,
+ snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH SQUIDSBUFPH,
request->url.getScheme().c_str(),
SQUIDSBUFPRINT(request->url.userInfo()),
!request->url.userInfo().isEmpty() ? "@" : "",
SQUIDSBUFPRINT(authorityForm),
- SQUIDSTRINGPRINT(request->urlpath));
+ SQUIDSBUFPRINT(request->url.path()));
}
}
}
return (request->canonical = xstrdup(urlbuf));
}
/** \todo AYJ: Performance: This is an *almost* duplicate of urlCanonical. But elides the query-string.
* After copying it on in the first place! Would be less code to merge the two with a flag parameter.
* and never copy the query-string part in the first place
*/
char *
urlCanonicalClean(const HttpRequest * request)
{
LOCAL_ARRAY(char, buf, MAX_URL);
char *t;
if (request->url.getScheme() == AnyP::PROTO_URN) {
- snprintf(buf, MAX_URL, "urn:" SQUIDSTRINGPH,
- SQUIDSTRINGPRINT(request->urlpath));
+ snprintf(buf, MAX_URL, "urn:" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(request->url.path()));
} else {
SBuf authorityForm;
switch (request->method.id()) {
case Http::METHOD_CONNECT:
authorityForm = request->url.authority(true); // host:port
snprintf(buf, MAX_URL, SQUIDSBUFPH, SQUIDSBUFPRINT(authorityForm));
break;
default: {
authorityForm = request->url.authority(); // host[:port]
- snprintf(buf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH SQUIDSTRINGPH,
+ snprintf(buf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH SQUIDSBUFPH,
request->url.getScheme().c_str(),
SQUIDSBUFPRINT(request->url.userInfo()),
!request->url.userInfo().isEmpty() ? "@" : "",
SQUIDSBUFPRINT(authorityForm),
- SQUIDSTRINGPRINT(request->urlpath));
+ SQUIDSBUFPRINT(request->url.path()));
// strip arguments AFTER a question-mark
if (Config.onoff.strip_query_terms)
if ((t = strchr(buf, '?')))
*(++t) = '\0';
}
} // switch
}
if (stringHasCntl(buf))
xstrncpy(buf, rfc1738_escape_unescaped(buf), MAX_URL);
return buf;
}
/**
* Yet another alternative to urlCanonical.
* This one adds the https:// parts to Http::METHOD_CONNECT URL
* for use in error page outputs.
* Luckily we can leverage the others instead of duplicating.
@@ -611,70 +630,75 @@
* It is assumed that you have already ensured that the URL is relative.
*
* If NULL is returned it is an indication that the method in use in the
* request does not distinguish between relative and absolute and you should
* use the url unchanged.
*
* If non-NULL is returned, it is up to the caller to free the resulting
* memory using safe_free().
*/
char *
urlMakeAbsolute(const HttpRequest * req, const char *relUrl)
{
if (req->method.id() == Http::METHOD_CONNECT) {
return (NULL);
}
char *urlbuf = (char *)xmalloc(MAX_URL * sizeof(char));
if (req->url.getScheme() == AnyP::PROTO_URN) {
- snprintf(urlbuf, MAX_URL, "urn:" SQUIDSTRINGPH,
- SQUIDSTRINGPRINT(req->urlpath));
+ snprintf(urlbuf, MAX_URL, "urn:" SQUIDSBUFPH,
+ SQUIDSBUFPRINT(req->url.path()));
return (urlbuf);
}
SBuf authorityForm = req->url.authority(); // host[:port]
size_t urllen = snprintf(urlbuf, MAX_URL, "%s://" SQUIDSBUFPH "%s" SQUIDSBUFPH,
req->url.getScheme().c_str(),
SQUIDSBUFPRINT(req->url.userInfo()),
!req->url.userInfo().isEmpty() ? "@" : "",
SQUIDSBUFPRINT(authorityForm));
+ // if the first char is '/' assume its a relative path
+ // XXX: this breaks on scheme-relative URLs,
+ // but we should not see those outside ESI, and rarely there.
if (relUrl[0] == '/') {
strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
} else {
- const char *path = req->urlpath.termedBuf();
- const char *last_slash = strrchr(path, '/');
+ SBuf path = req->url.path();
+ SBuf::size_type lastSlashPos = path.rfind('/');
- if (last_slash == NULL) {
+ if (lastSlashPos == SBuf::npos) {
+ // replace the whole path with the given bit(s)
urlbuf[urllen] = '/';
++urllen;
strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
} else {
- ++last_slash;
- size_t pathlen = last_slash - path;
- if (pathlen > MAX_URL - urllen - 1) {
- pathlen = MAX_URL - urllen - 1;
+ // replace only the last (file?) segment with the given bit(s)
+ ++lastSlashPos;
+ if (lastSlashPos > MAX_URL - urllen - 1) {
+ // XXX: crops bits in the middle of the combined URL.
+ lastSlashPos = MAX_URL - urllen - 1;
}
- strncpy(&urlbuf[urllen], path, pathlen);
- urllen += pathlen;
+ strncpy(&urlbuf[urllen], path.rawContent(), lastSlashPos);
+ urllen += lastSlashPos;
if (urllen + 1 < MAX_URL) {
strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
}
}
}
return (urlbuf);
}
int
matchDomainName(const char *h, const char *d, bool honorWildcards)
{
int dl;
int hl;
while ('.' == *h)
++h;
hl = strlen(h);
@@ -753,41 +777,41 @@
urlCheckRequest(const HttpRequest * r)
{
int rc = 0;
/* protocol "independent" methods
*
* actually these methods are specific to HTTP:
* they are methods we recieve on our HTTP port,
* and if we had a FTP listener would not be relevant
* there.
*
* So, we should delegate them to HTTP. The problem is that we
* do not have a default protocol from the client side of HTTP.
*/
if (r->method == Http::METHOD_CONNECT)
return 1;
// we support OPTIONS and TRACE directed at us (with a 501 reply, for now)
// we also support forwarding OPTIONS and TRACE, except for the *-URI ones
if (r->method == Http::METHOD_OPTIONS || r->method == Http::METHOD_TRACE)
- return (r->header.getInt64(HDR_MAX_FORWARDS) == 0 || URL::Asterisk().cmp(r->urlpath.rawBuf(), r->urlpath.size()) != 0);
+ return (r->header.getInt64(HDR_MAX_FORWARDS) == 0 || URL::Asterisk().cmp(r->url.path()) != 0);
if (r->method == Http::METHOD_PURGE)
return 1;
/* does method match the protocol? */
switch (r->url.getScheme()) {
case AnyP::PROTO_URN:
case AnyP::PROTO_HTTP:
case AnyP::PROTO_CACHE_OBJECT:
rc = 1;
break;
case AnyP::PROTO_FTP:
if (r->method == Http::METHOD_PUT)
rc = 1;
=== modified file 'src/urn.cc'
--- src/urn.cc 2015-05-26 18:12:08 +0000
+++ src/urn.cc 2015-06-15 11:06:49 +0000
@@ -18,45 +18,43 @@
#include "icmp/net_db.h"
#include "MemBuf.h"
#include "mime_header.h"
#include "RequestFlags.h"
#include "SquidTime.h"
#include "Store.h"
#include "StoreClient.h"
#include "tools.h"
#include "URL.h"
#include "urn.h"
#define URN_REQBUF_SZ 4096
class UrnState : public StoreClient
{
CBDATA_CLASS(UrnState);
public:
void created (StoreEntry *newEntry);
void start (HttpRequest *, StoreEntry *);
- char *getHost (String &urlpath);
+ char *getHost(SBuf &urlpath);
void setUriResFromRequest(HttpRequest *);
bool RequestNeedsMenu(HttpRequest *r);
- void updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len);
- void createUriResRequest (String &uri);
virtual ~UrnState();
StoreEntry *entry;
store_client *sc;
StoreEntry *urlres_e;
HttpRequest::Pointer request;
HttpRequest::Pointer urlres_r;
struct {
bool force_menu;
} flags;
char reqbuf[URN_REQBUF_SZ];
int reqofs;
private:
char *urlres;
};
typedef struct {
@@ -111,94 +109,80 @@
if (u->rtt > min_rtt && min_rtt != 0)
continue;
min_rtt = u->rtt;
min_u = u;
}
if (rtt_ret)
*rtt_ret = min_rtt;
debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
(min_u ? min_u->url : "NONE") << "' RTT " <<
min_rtt );
return min_u;
}
char *
-UrnState::getHost (String &urlpath)
+UrnState::getHost(SBuf &urlpath)
{
char * result;
size_t p;
/** FIXME: this appears to be parsing the URL. *very* badly. */
/* a proper encapsulated URI/URL type needs to clear this up. */
- if ((p=urlpath.find(':')) != String::npos) {
- result=xstrndup(urlpath.rawBuf(),p-1);
+ if ((p=urlpath.find(':')) != SBuf::npos) {
+ result=xstrndup(urlpath.rawContent(),p-1);
} else {
- result = xstrndup(urlpath.rawBuf(),urlpath.size());
+ result = xstrndup(urlpath.rawContent(),urlpath.length());
}
return result;
}
bool
UrnState::RequestNeedsMenu(HttpRequest *r)
{
- if (r->urlpath.size() < 5)
+ if (r->url.path().length() < 5)
return false;
//now we're sure it's long enough
- return strncasecmp(r->urlpath.rawBuf(), "menu.", 5) == 0;
+ return r->url.path().caseCmp("menu.", 5) == 0;
}
void
-UrnState::updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len)
+UrnState::setUriResFromRequest(HttpRequest *r)
{
- char *new_path = xstrndup (newPath, newPath_len);
- r->urlpath = new_path;
- xfree(new_path);
-}
+ if (RequestNeedsMenu(r)) {
+ r->url.path(r->url.path().substr(5)); // strip prefix "menu."
+ flags.force_menu = true;
+ }
-void
-UrnState::createUriResRequest (String &uri)
-{
+ SBuf uri = r->url.path();
LOCAL_ARRAY(char, local_urlres, 4096);
- char *host = getHost (uri);
- snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSTRINGPH,
- host, SQUIDSTRINGPRINT(uri));
+ char *host = getHost(uri);
+ snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSBUFPH, host, SQUIDSBUFPRINT(uri));
safe_free(host);
safe_free(urlres);
urlres = xstrdup(local_urlres);
urlres_r = HttpRequest::CreateFromUrl(urlres);
-}
-
-void
-UrnState::setUriResFromRequest(HttpRequest *r)
-{
- if (RequestNeedsMenu(r)) {
- updateRequestURL(r, r->urlpath.rawBuf() + 5, r->urlpath.size() - 5 );
- flags.force_menu = true;
- }
-
- createUriResRequest (r->urlpath);
if (urlres_r == NULL) {
debugs(52, 3, "urnStart: Bad uri-res URL " << urlres);
ErrorState *err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r);
err->url = urlres;
urlres = NULL;
errorAppendEntry(entry, err);
return;
}
urlres_r->header.putStr(HDR_ACCEPT, "text/plain");
}
void
UrnState::start(HttpRequest * r, StoreEntry * e)
{
debugs(52, 3, "urnStart: '" << e->url() << "'" );
entry = e;
request = r;
=== modified file 'src/whois.cc'
--- src/whois.cc 2015-01-13 07:25:36 +0000
+++ src/whois.cc 2015-06-15 11:09:45 +0000
@@ -39,57 +39,54 @@
bool dataWritten;
};
CBDATA_CLASS_INIT(WhoisState);
static CLCB whoisClose;
static CTCB whoisTimeout;
static IOCB whoisReadReply;
/* PUBLIC */
static void
whoisWriteComplete(const Comm::ConnectionPointer &, char *buf, size_t, Comm::Flag, int, void *)
{
xfree(buf);
}
void
whoisStart(FwdState * fwd)
{
- char *buf;
- size_t l;
WhoisState *p = new WhoisState;
p->request = fwd->request;
p->entry = fwd->entry;
p->fwd = fwd;
p->dataWritten = false;
p->entry->lock("whoisStart");
comm_add_close_handler(fwd->serverConnection()->fd, whoisClose, p);
- l = p->request->urlpath.size() + 3;
+ size_t l = p->request->url.path().length() + 3;
+ char *buf = (char *)xmalloc(l);
- buf = (char *)xmalloc(l);
-
- String str_print=p->request->urlpath.substr(1,p->request->urlpath.size());
- snprintf(buf, l, SQUIDSTRINGPH"\r\n", SQUIDSTRINGPRINT(str_print));
+ SBuf str_print = p->request->url.path().substr(1);
+ snprintf(buf, l, SQUIDSBUFPH "\r\n", SQUIDSBUFPRINT(str_print));
AsyncCall::Pointer writeCall = commCbCall(5,5, "whoisWriteComplete",
CommIoCbPtrFun(whoisWriteComplete, p));
Comm::Write(fwd->serverConnection(), buf, strlen(buf), writeCall, NULL);
AsyncCall::Pointer readCall = commCbCall(5,4, "whoisReadReply",
CommIoCbPtrFun(whoisReadReply, p));
comm_read(fwd->serverConnection(), p->buf, BUFSIZ, readCall);
AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "whoisTimeout",
CommTimeoutCbPtrFun(whoisTimeout, p));
commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
}
/* PRIVATE */
static void
whoisTimeout(const CommTimeoutCbParams &io)
{
WhoisState *p = static_cast<WhoisState *>(io.data);
debugs(75, 3, HERE << io.conn << ", URL " << p->entry->url());
io.conn->close();
More information about the squid-dev
mailing list