]> git.ipfire.org Git - thirdparty/squid.git/blame - src/urn.cc
Fix the SQUID_CC_REQUIRE_ARGUMENT autoconf function (#478)
[thirdparty/squid.git] / src / urn.cc
CommitLineData
85491f8d 1/*
f6e9a3ee 2 * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
e25c139f 3 *
bbc27441
AJ
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
85491f8d 7 */
8
bbc27441
AJ
9/* DEBUG: section 52 URN Parsing */
10
582c2af2 11#include "squid.h"
d2a6dcba 12#include "AccessLogEntry.h"
819be284 13#include "acl/FilledChecklist.h"
bda078fe 14#include "cbdata.h"
aa839030 15#include "errorpage.h"
eb13c21e 16#include "FwdState.h"
582c2af2 17#include "globals.h"
e87137f1
FC
18#include "HttpReply.h"
19#include "HttpRequest.h"
9b5c4a9a 20#include "icmp/net_db.h"
e87137f1 21#include "MemBuf.h"
b6149797 22#include "mime_header.h"
f206b652 23#include "RequestFlags.h"
e87137f1
FC
24#include "SquidTime.h"
25#include "Store.h"
26#include "StoreClient.h"
8d03bdb4 27#include "tools.h"
5eb529cb 28#include "urn.h"
85491f8d 29
f53969cc 30#define URN_REQBUF_SZ 4096
add2192d 31
62e76326 32class UrnState : public StoreClient
33{
5c2f68b7 34 CBDATA_CLASS(UrnState);
62e76326 35
e6ccf245 36public:
d2a6dcba
EB
37 explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
38
3b13a8fd 39 void created (StoreEntry *newEntry);
190154cf 40 void start (HttpRequest *, StoreEntry *);
190154cf 41 void setUriResFromRequest(HttpRequest *);
e6ccf245 42
43 virtual ~UrnState();
62e76326 44
d2a6dcba
EB
45 StoreEntry *entry = nullptr;
46 store_client *sc = nullptr;
47 StoreEntry *urlres_e = nullptr;
8a70cdbb
AJ
48 HttpRequest::Pointer request;
49 HttpRequest::Pointer urlres_r;
7e6eabbc 50 AccessLogEntry::Pointer ale; ///< details of the requesting transaction
62e76326 51
d2a6dcba
EB
52 char reqbuf[URN_REQBUF_SZ] = { '\0' };
53 int reqofs = 0;
62e76326 54
e6ccf245 55private:
819be284 56 /* StoreClient API */
d2a6dcba 57 virtual LogTags *loggingTags() { return ale ? &ale->cache.code : nullptr; }
819be284
EB
58 virtual void fillChecklist(ACLFilledChecklist &) const;
59
d2a6dcba 60 char *urlres = nullptr;
e6ccf245 61};
85491f8d 62
26ac0430 63typedef struct {
9ce5e3e6 64 char *url;
65 char *host;
66 int rtt;
62e76326 67
26ac0430 68 struct {
62e76326 69 int cached;
2fadd50d
HN
70 } flags;
71} url_entry;
9ce5e3e6 72
73static STCB urnHandleReply;
60745f24 74static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
9ce5e3e6 75static const char *const crlf = "\r\n";
9ce5e3e6 76
bda078fe 77CBDATA_CLASS_INIT(UrnState);
62e76326 78
bda078fe 79UrnState::~UrnState()
e6ccf245 80{
682d5190
EB
81 if (urlres_e) {
82 if (sc)
83 storeUnregister(sc, urlres_e, this);
84 urlres_e->unlock("~UrnState+res");
85 }
86
87 if (entry)
88 entry->unlock("~UrnState+prime");
89
e6ccf245 90 safe_free(urlres);
91}
92
48ebcb22 93static url_entry *
ced8def3 94urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
85491f8d 95{
23d92c64 96 int min_rtt = 0;
9ce5e3e6 97 url_entry *u = NULL;
98 url_entry *min_u = NULL;
99 int i;
1caf595b 100 int urlcnt = 0;
bf8fe701 101 debugs(52, 3, "urnFindMinRtt");
23d92c64 102 assert(urls != NULL);
62e76326 103
14942edd
FC
104 for (i = 0; NULL != urls[i].url; ++i)
105 ++urlcnt;
62e76326 106
bf8fe701 107 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 108
9ce5e3e6 109 if (1 == urlcnt) {
bf8fe701 110 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
62e76326 111 return urls;
1caf595b 112 }
62e76326 113
14942edd 114 for (i = 0; i < urlcnt; ++i) {
62e76326 115 u = &urls[i];
bf8fe701 116 debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
62e76326 117
118 if (u->rtt == 0)
119 continue;
120
121 if (u->rtt > min_rtt && min_rtt != 0)
122 continue;
123
124 min_rtt = u->rtt;
125
126 min_u = u;
23d92c64 127 }
62e76326 128
23d92c64 129 if (rtt_ret)
62e76326 130 *rtt_ret = min_rtt;
131
e0236918 132 debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
26ac0430
AJ
133 (min_u ? min_u->url : "NONE") << "' RTT " <<
134 min_rtt );
62e76326 135
9ce5e3e6 136 return min_u;
85491f8d 137}
138
e6ccf245 139void
51b5dcf5 140UrnState::setUriResFromRequest(HttpRequest *r)
e6ccf245 141{
6c880a16
AJ
142 const auto &query = r->url.absolute();
143 const auto host = r->url.host();
c8ab5ec6 144 // TODO: use class AnyP::Uri instead of generating a string and re-parsing
e6ccf245 145 LOCAL_ARRAY(char, local_urlres, 4096);
6c880a16 146 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH, host, SQUIDSBUFPRINT(query));
86c63190 147 safe_free(urlres);
6c880a16 148 urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
62e76326 149
8babada0
AJ
150 if (!urlres_r) {
151 debugs(52, 3, "Bad uri-res URL " << local_urlres);
7e6eabbc 152 const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
8babada0 153 err->url = xstrdup(local_urlres);
62e76326 154 errorAppendEntry(entry, err);
155 return;
0adbab7c 156 }
62e76326 157
8babada0 158 urlres = xstrdup(local_urlres);
789217a2 159 urlres_r->header.putStr(Http::HdrType::ACCEPT, "text/plain");
e6ccf245 160}
161
162void
190154cf 163UrnState::start(HttpRequest * r, StoreEntry * e)
e6ccf245 164{
bf8fe701 165 debugs(52, 3, "urnStart: '" << e->url() << "'" );
e6ccf245 166 entry = e;
b248c2a3 167 request = r;
34266cde 168
1bfe9ade 169 entry->lock("UrnState::start");
e6ccf245 170 setUriResFromRequest(r);
62e76326 171
e6ccf245 172 if (urlres_r == NULL)
62e76326 173 return;
174
c2a7cefd 175 StoreEntry::getPublic (this, urlres, Http::METHOD_GET);
e6ccf245 176}
177
178void
819be284 179UrnState::fillChecklist(ACLFilledChecklist &checklist) const
e6ccf245 180{
819be284 181 checklist.setRequest(request.getRaw());
d2a6dcba 182 checklist.al = ale;
819be284 183}
62e76326 184
819be284
EB
185void
186UrnState::created(StoreEntry *e)
187{
69565793 188 if (!e || (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false))) {
c2a7cefd 189 urlres_e = storeCreateEntry(urlres, urlres, RequestFlags(), Http::METHOD_GET);
62e76326 190 sc = storeClientListAdd(urlres_e, this);
d2a6dcba 191 FwdState::Start(Comm::ConnectionPointer(), urlres_e, urlres_r.getRaw(), ale);
819be284 192 // TODO: StoreClients must either store/lock or abandon found entries.
69565793 193 //if (e)
819be284 194 // e->abandon();
cf26e54c 195 } else {
819be284 196 urlres_e = e;
1bfe9ade 197 urlres_e->lock("UrnState::created");
62e76326 198 sc = storeClientListAdd(urlres_e, this);
85491f8d 199 }
62e76326 200
e6ccf245 201 reqofs = 0;
528b2c61 202 StoreIOBuffer tempBuffer;
e6ccf245 203 tempBuffer.offset = reqofs;
c8be6d7b 204 tempBuffer.length = URN_REQBUF_SZ;
e6ccf245 205 tempBuffer.data = reqbuf;
206 storeClientCopy(sc, urlres_e,
62e76326 207 tempBuffer,
208 urnHandleReply,
209 this);
e6ccf245 210}
211
212void
d2a6dcba 213urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
e6ccf245 214{
d2a6dcba 215 const auto anUrn = new UrnState(ale);
e6ccf245 216 anUrn->start (r, e);
85491f8d 217}
218
9ce5e3e6 219static int
220url_entry_sort(const void *A, const void *B)
221{
e6ccf245 222 const url_entry *u1 = (const url_entry *)A;
223 const url_entry *u2 = (const url_entry *)B;
62e76326 224
9ce5e3e6 225 if (u2->rtt == u1->rtt)
62e76326 226 return 0;
9ce5e3e6 227 else if (0 == u1->rtt)
62e76326 228 return 1;
9ce5e3e6 229 else if (0 == u2->rtt)
62e76326 230 return -1;
9ce5e3e6 231 else
62e76326 232 return u1->rtt - u2->rtt;
9ce5e3e6 233}
234
528b2c61 235/* TODO: use the clientStream support for this */
85491f8d 236static void
c8be6d7b 237urnHandleReply(void *data, StoreIOBuffer result)
85491f8d 238{
e6ccf245 239 UrnState *urnState = static_cast<UrnState *>(data);
cf26e54c 240 StoreEntry *e = urnState->entry;
241 StoreEntry *urlres_e = urnState->urlres_e;
23d92c64 242 char *s = NULL;
2334c194 243 size_t k;
cb69b4c7 244 HttpReply *rep;
9ce5e3e6 245 url_entry *urls;
00141c96 246 url_entry *u;
9ce5e3e6 247 url_entry *min_u;
cf26e54c 248 ErrorState *err;
9ce5e3e6 249 int i;
250 int urlcnt = 0;
add2192d 251 char *buf = urnState->reqbuf;
c8be6d7b 252 StoreIOBuffer tempBuffer;
cf26e54c 253
58c0b17d 254 debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
62e76326 255
682d5190
EB
256 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
257 delete urnState;
bb9edbb2 258 return;
85491f8d 259 }
62e76326 260
add2192d 261 /* Update reqofs to point to where in the buffer we'd be */
c8be6d7b 262 urnState->reqofs += result.length;
add2192d 263
264 /* Handle reqofs being bigger than normal */
265 if (urnState->reqofs >= URN_REQBUF_SZ) {
682d5190 266 delete urnState;
bb9edbb2 267 return;
add2192d 268 }
62e76326 269
add2192d 270 /* If we haven't received the entire object (urn), copy more */
682d5190
EB
271 if (urlres_e->store_status == STORE_PENDING) {
272 Must(result.length > 0); // zero length ought to imply STORE_OK
62e76326 273 tempBuffer.offset = urnState->reqofs;
682d5190 274 tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
62e76326 275 tempBuffer.data = urnState->reqbuf + urnState->reqofs;
276 storeClientCopy(urnState->sc, urlres_e,
277 tempBuffer,
278 urnHandleReply,
279 urnState);
280 return;
23d92c64 281 }
62e76326 282
23d92c64 283 /* we know its STORE_OK */
add2192d 284 k = headersEnd(buf, urnState->reqofs);
62e76326 285
2334c194 286 if (0 == k) {
e0236918 287 debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url() );
682d5190 288 delete urnState;
bb9edbb2 289 return;
85491f8d 290 }
62e76326 291
2334c194 292 s = buf + k;
528b2c61 293 assert(urlres_e->getReply());
06a5ae20 294 rep = new HttpReply;
59eed7dc 295 rep->parseCharBuf(buf, k);
9b769c67 296 debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
62e76326 297
9b769c67 298 if (rep->sline.status() != Http::scOkay) {
bf8fe701 299 debugs(52, 3, "urnHandleReply: failed.");
7e6eabbc 300 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
3900307b 301 err->url = xstrdup(e->url());
62e76326 302 errorAppendEntry(e, err);
06a5ae20 303 delete rep;
682d5190 304 delete urnState;
bb9edbb2 305 return;
85491f8d 306 }
62e76326 307
06a5ae20 308 delete rep;
62e76326 309
b6a2f15e 310 while (xisspace(*s))
14942edd 311 ++s;
62e76326 312
9ce5e3e6 313 urls = urnParseReply(s, urnState->request->method);
62e76326 314
892ee3d0 315 if (!urls) { /* unknown URN error */
8a70cdbb 316 debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
7e6eabbc 317 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
3900307b 318 err->url = xstrdup(e->url());
62e76326 319 errorAppendEntry(e, err);
682d5190 320 delete urnState;
bb9edbb2 321 return;
164f7660 322 }
62e76326 323
892ee3d0
AJ
324 for (i = 0; urls[i].url; ++i)
325 ++urlcnt;
326
327 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
328
9ce5e3e6 329 min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
330 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
3900307b 331 e->buffer();
7e6eabbc
CT
332 SBuf body;
333 SBuf *mb = &body; // diff reduction hack; TODO: Remove
4391cd15 334 mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
f680026f
SM
335 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
336 "<H2>Select URL for %s</H2>\n"
337 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
62e76326 338
14942edd 339 for (i = 0; i < urlcnt; ++i) {
62e76326 340 u = &urls[i];
bf8fe701 341 debugs(52, 3, "URL {" << u->url << "}");
4391cd15 342 mb->appendf(
2fe7eff9 343 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
62e76326 344
345 if (urls[i].rtt > 0)
4391cd15 346 mb->appendf(
2fe7eff9 347 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
62e76326 348 else
4391cd15 349 mb->appendf("<TD align=\"right\">Unknown</TD>");
62e76326 350
4391cd15 351 mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
cf26e54c 352 }
62e76326 353
4391cd15 354 mb->appendf(
2fe7eff9 355 "</TABLE>"
356 "<HR noshade size=\"1px\">\n"
357 "<ADDRESS>\n"
358 "Generated by %s@%s\n"
359 "</ADDRESS>\n",
7dbca7a4 360 APP_FULLNAME, getMyHostname());
06a5ae20 361 rep = new HttpReply;
7e6eabbc 362 rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
62e76326 363
6c880a16 364 if (min_u) {
789217a2 365 rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
cb69b4c7 366 }
62e76326 367
7e6eabbc 368 rep->body.set(body);
db237875 369 e->replaceHttpReply(rep);
528b2c61 370 e->complete();
62e76326 371
14942edd 372 for (i = 0; i < urlcnt; ++i) {
62e76326 373 safe_free(urls[i].url);
374 safe_free(urls[i].host);
9ce5e3e6 375 }
62e76326 376
9ce5e3e6 377 safe_free(urls);
62e76326 378
682d5190 379 delete urnState;
85491f8d 380}
381
9ce5e3e6 382static url_entry *
60745f24 383urnParseReply(const char *inbuf, const HttpRequestMethod& m)
85491f8d 384{
23d92c64 385 char *buf = xstrdup(inbuf);
386 char *token;
9ce5e3e6 387 char *host;
9ce5e3e6 388 url_entry *list;
389 url_entry *old;
390 int n = 32;
391 int i = 0;
bf8fe701 392 debugs(52, 3, "urnParseReply");
e6ccf245 393 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
62e76326 394
23d92c64 395 for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
bf8fe701 396 debugs(52, 3, "urnParseReply: got '" << token << "'");
62e76326 397
398 if (i == n) {
399 old = list;
400 n <<= 2;
401 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
41d00cd3 402 memcpy(list, old, i * sizeof(*list));
62e76326 403 safe_free(old);
404 }
405
8babada0 406 host = urlHostname(token);
62e76326 407
408 if (NULL == host)
409 continue;
410
9b5c4a9a
AJ
411#if USE_ICMP
412 list[i].rtt = netdbHostRtt(host);
62e76326 413
9b5c4a9a 414 if (0 == list[i].rtt) {
bf8fe701 415 debugs(52, 3, "urnParseReply: Pinging " << host);
62e76326 416 netdbPingSite(host);
417 }
9b5c4a9a
AJ
418#else
419 list[i].rtt = 0;
420#endif
62e76326 421
8babada0 422 list[i].url = xstrdup(token);
62e76326 423 list[i].host = xstrdup(host);
5bd484b5
AR
424 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
425 // ones.
8babada0 426 list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
14942edd 427 ++i;
85491f8d 428 }
62e76326 429
bf8fe701 430 debugs(52, 3, "urnParseReply: Found " << i << " URLs");
9ce5e3e6 431 return list;
85491f8d 432}
f53969cc 433