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