]> git.ipfire.org Git - thirdparty/squid.git/blame - src/urn.cc
Polish: Drop redundant HttpMsgPointerT template
[thirdparty/squid.git] / src / urn.cc
CommitLineData
85491f8d 1/*
6f6f0853 2 * DEBUG: section 52 URN Parsing
85491f8d 3 * AUTHOR: Kostas Anagnostakis
4 *
2b6662ba 5 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 6 * ----------------------------------------------------------
85491f8d 7 *
2b6662ba 8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
85491f8d 16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
26ac0430 21 *
85491f8d 22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26ac0430 26 *
85491f8d 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
cbdec147 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 30 *
85491f8d 31 */
32
582c2af2 33#include "squid.h"
aa839030 34#include "errorpage.h"
b6b6f466 35#include "forward.h"
582c2af2 36#include "globals.h"
e87137f1
FC
37#include "HttpReply.h"
38#include "HttpRequest.h"
9b5c4a9a 39#include "icmp/net_db.h"
e87137f1 40#include "MemBuf.h"
b6149797 41#include "mime_header.h"
f206b652 42#include "RequestFlags.h"
e87137f1
FC
43#include "SquidTime.h"
44#include "Store.h"
45#include "StoreClient.h"
8d03bdb4 46#include "tools.h"
b1bd952a 47#include "URL.h"
5eb529cb 48#include "urn.h"
85491f8d 49
add2192d 50#define URN_REQBUF_SZ 4096
51
62e76326 52class UrnState : public StoreClient
53{
54
e6ccf245 55public:
3b13a8fd 56 void created (StoreEntry *newEntry);
06096e82 57 void *operator new (size_t byteCount);
e6ccf245 58 void operator delete (void *address);
190154cf 59 void start (HttpRequest *, StoreEntry *);
30abd221 60 char *getHost (String &urlpath);
190154cf 61 void setUriResFromRequest(HttpRequest *);
62 bool RequestNeedsMenu(HttpRequest *r);
17bc3f22 63 void updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len);
30abd221 64 void createUriResRequest (String &uri);
e6ccf245 65
66 virtual ~UrnState();
62e76326 67
164f7660 68 StoreEntry *entry;
06d2839d 69 store_client *sc;
164f7660 70 StoreEntry *urlres_e;
190154cf 71 HttpRequest *request;
72 HttpRequest *urlres_r;
62e76326 73
26ac0430 74 struct {
3dd52a0b 75 bool force_menu;
3d0ac046 76 } flags;
add2192d 77 char reqbuf[URN_REQBUF_SZ];
78 int reqofs;
62e76326 79
e6ccf245 80private:
81 char *urlres;
82};
85491f8d 83
26ac0430 84typedef struct {
9ce5e3e6 85 char *url;
86 char *host;
87 int rtt;
62e76326 88
26ac0430 89 struct {
62e76326 90 int cached;
2fadd50d
HN
91 } flags;
92} url_entry;
9ce5e3e6 93
94static STCB urnHandleReply;
60745f24 95static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
9ce5e3e6 96static const char *const crlf = "\r\n";
97static QS url_entry_sort;
98
e6ccf245 99CBDATA_TYPE(UrnState);
100void *
06096e82 101UrnState::operator new (size_t byteCount)
e6ccf245 102{
62e76326 103 /* derived classes with different sizes must implement their own new */
e6ccf245 104 assert (byteCount == sizeof (UrnState));
105 CBDATA_INIT_TYPE(UrnState);
106 return cbdataAlloc(UrnState);
62e76326 107
e6ccf245 108}
109
110void
111UrnState::operator delete (void *address)
112{
1f1ae50a 113 UrnState * tmp = (UrnState *)address;
114 cbdataFree (tmp);
e6ccf245 115}
116
117UrnState::~UrnState ()
118{
119 safe_free(urlres);
120}
121
48ebcb22 122static url_entry *
60745f24 123urnFindMinRtt(url_entry * urls, const HttpRequestMethod& m, int *rtt_ret)
85491f8d 124{
23d92c64 125 int min_rtt = 0;
9ce5e3e6 126 url_entry *u = NULL;
127 url_entry *min_u = NULL;
128 int i;
1caf595b 129 int urlcnt = 0;
bf8fe701 130 debugs(52, 3, "urnFindMinRtt");
23d92c64 131 assert(urls != NULL);
62e76326 132
14942edd
FC
133 for (i = 0; NULL != urls[i].url; ++i)
134 ++urlcnt;
62e76326 135
bf8fe701 136 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 137
9ce5e3e6 138 if (1 == urlcnt) {
bf8fe701 139 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
62e76326 140 return urls;
1caf595b 141 }
62e76326 142
14942edd 143 for (i = 0; i < urlcnt; ++i) {
62e76326 144 u = &urls[i];
bf8fe701 145 debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
62e76326 146
147 if (u->rtt == 0)
148 continue;
149
150 if (u->rtt > min_rtt && min_rtt != 0)
151 continue;
152
153 min_rtt = u->rtt;
154
155 min_u = u;
23d92c64 156 }
62e76326 157
23d92c64 158 if (rtt_ret)
62e76326 159 *rtt_ret = min_rtt;
160
e0236918 161 debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
26ac0430
AJ
162 (min_u ? min_u->url : "NONE") << "' RTT " <<
163 min_rtt );
62e76326 164
9ce5e3e6 165 return min_u;
85491f8d 166}
167
e6ccf245 168char *
30abd221 169UrnState::getHost (String &urlpath)
85491f8d 170{
e6ccf245 171 char * result;
b4f2886c 172 size_t p;
62e76326 173
b4f2886c
FC
174 /** FIXME: this appears to be parsing the URL. *very* badly. */
175 /* a proper encapsulated URI/URL type needs to clear this up. */
2c1fd837 176 if ((p=urlpath.find(':')) != String::npos) {
b4f2886c 177 result=xstrndup(urlpath.rawBuf(),p-1);
fa040df2 178 } else {
b4f2886c 179 result = xstrndup(urlpath.rawBuf(),urlpath.size());
fa040df2 180 }
e6ccf245 181 return result;
182}
183
184bool
190154cf 185UrnState::RequestNeedsMenu(HttpRequest *r)
e6ccf245 186{
17bc3f22
FC
187 if (r->urlpath.size() < 5)
188 return false;
189 //now we're sure it's long enough
190 return strncasecmp(r->urlpath.rawBuf(), "menu.", 5) == 0;
e6ccf245 191}
192
193void
17bc3f22 194UrnState::updateRequestURL(HttpRequest *r, char const *newPath, const size_t newPath_len)
e6ccf245 195{
17bc3f22 196 char *new_path = xstrndup (newPath, newPath_len);
62e76326 197 r->urlpath = new_path;
198 xfree(new_path);
e6ccf245 199}
200
201void
30abd221 202UrnState::createUriResRequest (String &uri)
e6ccf245 203{
204 LOCAL_ARRAY(char, local_urlres, 4096);
205 char *host = getHost (uri);
2c1fd837 206 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSTRINGPH,
af6a12ee 207 host, SQUIDSTRINGPRINT(uri));
e6ccf245 208 safe_free (host);
209 safe_free (urlres);
210 urlres = xstrdup (local_urlres);
c21ad0f5 211 urlres_r = HttpRequest::CreateFromUrl(urlres);
e6ccf245 212}
213
214void
190154cf 215UrnState::setUriResFromRequest(HttpRequest *r)
e6ccf245 216{
217 if (RequestNeedsMenu(r)) {
17bc3f22 218 updateRequestURL(r, r->urlpath.rawBuf() + 5, r->urlpath.size() - 5 );
3dd52a0b 219 flags.force_menu = true;
e6ccf245 220 }
62e76326 221
e6ccf245 222 createUriResRequest (r->urlpath);
62e76326 223
0adbab7c 224 if (urlres_r == NULL) {
bf8fe701 225 debugs(52, 3, "urnStart: Bad uri-res URL " << urlres);
913524f0 226 ErrorState *err = new ErrorState(ERR_URN_RESOLVE, HTTP_NOT_FOUND, r);
62e76326 227 err->url = urlres;
228 urlres = NULL;
229 errorAppendEntry(entry, err);
230 return;
0adbab7c 231 }
62e76326 232
6dd9f4bd 233 HTTPMSGLOCK(urlres_r);
a9925b40 234 urlres_r->header.putStr(HDR_ACCEPT, "text/plain");
e6ccf245 235}
236
237void
190154cf 238UrnState::start(HttpRequest * r, StoreEntry * e)
e6ccf245 239{
bf8fe701 240 debugs(52, 3, "urnStart: '" << e->url() << "'" );
e6ccf245 241 entry = e;
b248c2a3
AJ
242 request = r;
243 HTTPMSGLOCK(request);
34266cde 244
3d0ac046 245 entry->lock();
e6ccf245 246 setUriResFromRequest(r);
62e76326 247
e6ccf245 248 if (urlres_r == NULL)
62e76326 249 return;
250
c2a7cefd 251 StoreEntry::getPublic (this, urlres, Http::METHOD_GET);
e6ccf245 252}
253
254void
3b13a8fd 255UrnState::created(StoreEntry *newEntry)
e6ccf245 256{
257 urlres_e = newEntry;
62e76326 258
e6ccf245 259 if (urlres_e->isNull()) {
c2a7cefd 260 urlres_e = storeCreateEntry(urlres, urlres, RequestFlags(), Http::METHOD_GET);
62e76326 261 sc = storeClientListAdd(urlres_e, this);
e83cc785 262 FwdState::fwdStart(Comm::ConnectionPointer(), urlres_e, urlres_r);
cf26e54c 263 } else {
34266cde 264
3d0ac046 265 urlres_e->lock();
62e76326 266 sc = storeClientListAdd(urlres_e, this);
85491f8d 267 }
62e76326 268
e6ccf245 269 reqofs = 0;
528b2c61 270 StoreIOBuffer tempBuffer;
e6ccf245 271 tempBuffer.offset = reqofs;
c8be6d7b 272 tempBuffer.length = URN_REQBUF_SZ;
e6ccf245 273 tempBuffer.data = reqbuf;
274 storeClientCopy(sc, urlres_e,
62e76326 275 tempBuffer,
276 urnHandleReply,
277 this);
e6ccf245 278}
279
280void
190154cf 281urnStart(HttpRequest * r, StoreEntry * e)
e6ccf245 282{
283 UrnState *anUrn = new UrnState();
284 anUrn->start (r, e);
85491f8d 285}
286
9ce5e3e6 287static int
288url_entry_sort(const void *A, const void *B)
289{
e6ccf245 290 const url_entry *u1 = (const url_entry *)A;
291 const url_entry *u2 = (const url_entry *)B;
62e76326 292
9ce5e3e6 293 if (u2->rtt == u1->rtt)
62e76326 294 return 0;
9ce5e3e6 295 else if (0 == u1->rtt)
62e76326 296 return 1;
9ce5e3e6 297 else if (0 == u2->rtt)
62e76326 298 return -1;
9ce5e3e6 299 else
62e76326 300 return u1->rtt - u2->rtt;
9ce5e3e6 301}
302
bb9edbb2
AJ
303static void
304urnHandleReplyError(UrnState *urnState, StoreEntry *urlres_e)
305{
306 urlres_e->unlock();
307 urnState->entry->unlock();
308 HTTPMSGUNLOCK(urnState->request);
309 HTTPMSGUNLOCK(urnState->urlres_r);
310 delete urnState;
311}
312
528b2c61 313/* TODO: use the clientStream support for this */
85491f8d 314static void
c8be6d7b 315urnHandleReply(void *data, StoreIOBuffer result)
85491f8d 316{
e6ccf245 317 UrnState *urnState = static_cast<UrnState *>(data);
cf26e54c 318 StoreEntry *e = urnState->entry;
319 StoreEntry *urlres_e = urnState->urlres_e;
23d92c64 320 char *s = NULL;
2334c194 321 size_t k;
cb69b4c7 322 HttpReply *rep;
9ce5e3e6 323 url_entry *urls;
00141c96 324 url_entry *u;
9ce5e3e6 325 url_entry *min_u;
032785bf 326 MemBuf *mb = NULL;
cf26e54c 327 ErrorState *err;
9ce5e3e6 328 int i;
329 int urlcnt = 0;
add2192d 330 char *buf = urnState->reqbuf;
c8be6d7b 331 StoreIOBuffer tempBuffer;
cf26e54c 332
58c0b17d 333 debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
62e76326 334
58c0b17d 335 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.length == 0 || result.flags.error) {
bb9edbb2
AJ
336 urnHandleReplyError(urnState, urlres_e);
337 return;
85491f8d 338 }
62e76326 339
add2192d 340 /* Update reqofs to point to where in the buffer we'd be */
c8be6d7b 341 urnState->reqofs += result.length;
add2192d 342
343 /* Handle reqofs being bigger than normal */
344 if (urnState->reqofs >= URN_REQBUF_SZ) {
bb9edbb2
AJ
345 urnHandleReplyError(urnState, urlres_e);
346 return;
add2192d 347 }
62e76326 348
add2192d 349 /* If we haven't received the entire object (urn), copy more */
350 if (urlres_e->store_status == STORE_PENDING &&
62e76326 351 urnState->reqofs < URN_REQBUF_SZ) {
352 tempBuffer.offset = urnState->reqofs;
353 tempBuffer.length = URN_REQBUF_SZ;
354 tempBuffer.data = urnState->reqbuf + urnState->reqofs;
355 storeClientCopy(urnState->sc, urlres_e,
356 tempBuffer,
357 urnHandleReply,
358 urnState);
359 return;
23d92c64 360 }
62e76326 361
23d92c64 362 /* we know its STORE_OK */
add2192d 363 k = headersEnd(buf, urnState->reqofs);
62e76326 364
2334c194 365 if (0 == k) {
e0236918 366 debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url() );
bb9edbb2
AJ
367 urnHandleReplyError(urnState, urlres_e);
368 return;
85491f8d 369 }
62e76326 370
2334c194 371 s = buf + k;
528b2c61 372 assert(urlres_e->getReply());
06a5ae20 373 rep = new HttpReply;
59eed7dc 374 rep->parseCharBuf(buf, k);
bf8fe701 375 debugs(52, 3, "reply exists, code=" << rep->sline.status << ".");
62e76326 376
528b2c61 377 if (rep->sline.status != HTTP_OK) {
bf8fe701 378 debugs(52, 3, "urnHandleReply: failed.");
913524f0 379 err = new ErrorState(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request);
3900307b 380 err->url = xstrdup(e->url());
62e76326 381 errorAppendEntry(e, err);
06a5ae20 382 delete rep;
bb9edbb2
AJ
383 urnHandleReplyError(urnState, urlres_e);
384 return;
85491f8d 385 }
62e76326 386
06a5ae20 387 delete rep;
62e76326 388
b6a2f15e 389 while (xisspace(*s))
14942edd 390 ++s;
62e76326 391
9ce5e3e6 392 urls = urnParseReply(s, urnState->request->method);
62e76326 393
14942edd
FC
394 for (i = 0; NULL != urls[i].url; ++i)
395 ++urlcnt;
62e76326 396
bf8fe701 397 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 398
164f7660 399 if (urls == NULL) { /* unkown URN error */
bf8fe701 400 debugs(52, 3, "urnTranslateDone: unknown URN " << e->url() );
913524f0 401 err = new ErrorState(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request);
3900307b 402 err->url = xstrdup(e->url());
62e76326 403 errorAppendEntry(e, err);
bb9edbb2
AJ
404 urnHandleReplyError(urnState, urlres_e);
405 return;
164f7660 406 }
62e76326 407
9ce5e3e6 408 min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
409 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
3900307b 410 e->buffer();
032785bf 411 mb = new MemBuf;
2fe7eff9 412 mb->init();
413 mb->Printf( "<TITLE>Select URL for %s</TITLE>\n"
414 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
415 "<H2>Select URL for %s</H2>\n"
3900307b 416 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
62e76326 417
14942edd 418 for (i = 0; i < urlcnt; ++i) {
62e76326 419 u = &urls[i];
bf8fe701 420 debugs(52, 3, "URL {" << u->url << "}");
2fe7eff9 421 mb->Printf(
422 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
62e76326 423
424 if (urls[i].rtt > 0)
2fe7eff9 425 mb->Printf(
426 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
62e76326 427 else
2fe7eff9 428 mb->Printf("<TD align=\"right\">Unknown</TD>");
62e76326 429
2fe7eff9 430 mb->Printf(
431 "<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
cf26e54c 432 }
62e76326 433
2fe7eff9 434 mb->Printf(
435 "</TABLE>"
436 "<HR noshade size=\"1px\">\n"
437 "<ADDRESS>\n"
438 "Generated by %s@%s\n"
439 "</ADDRESS>\n",
7dbca7a4 440 APP_FULLNAME, getMyHostname());
06a5ae20 441 rep = new HttpReply;
11992b6f 442 rep->setHeaders(HTTP_MOVED_TEMPORARILY, NULL, "text/html", mb->contentSize(), 0, squid_curtime);
62e76326 443
b515fc11 444 if (urnState->flags.force_menu) {
bf8fe701 445 debugs(51, 3, "urnHandleReply: forcing menu");
9ce5e3e6 446 } else if (min_u) {
a9925b40 447 rep->header.putStr(HDR_LOCATION, min_u->url);
cb69b4c7 448 }
62e76326 449
0521f8be 450 rep->body.setMb(mb);
032785bf 451 /* don't clean or delete mb; rep->body owns it now */
db237875 452 e->replaceHttpReply(rep);
528b2c61 453 e->complete();
62e76326 454
14942edd 455 for (i = 0; i < urlcnt; ++i) {
62e76326 456 safe_free(urls[i].url);
457 safe_free(urls[i].host);
9ce5e3e6 458 }
62e76326 459
9ce5e3e6 460 safe_free(urls);
96a5be3d 461 /* mb was absorbed in httpBodySet call, so we must not clean it */
06d2839d 462 storeUnregister(urnState->sc, urlres_e, urnState);
62e76326 463
bb9edbb2 464 urnHandleReplyError(urnState, urlres_e);
85491f8d 465}
466
9ce5e3e6 467static url_entry *
60745f24 468urnParseReply(const char *inbuf, const HttpRequestMethod& m)
85491f8d 469{
23d92c64 470 char *buf = xstrdup(inbuf);
471 char *token;
9ce5e3e6 472 char *url;
473 char *host;
9ce5e3e6 474 url_entry *list;
475 url_entry *old;
476 int n = 32;
477 int i = 0;
bf8fe701 478 debugs(52, 3, "urnParseReply");
e6ccf245 479 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
62e76326 480
23d92c64 481 for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
bf8fe701 482 debugs(52, 3, "urnParseReply: got '" << token << "'");
62e76326 483
484 if (i == n) {
485 old = list;
486 n <<= 2;
487 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
41d00cd3 488 memcpy(list, old, i * sizeof(*list));
62e76326 489 safe_free(old);
490 }
491
492 url = xstrdup(token);
493 host = urlHostname(url);
494
495 if (NULL == host)
496 continue;
497
9b5c4a9a
AJ
498#if USE_ICMP
499 list[i].rtt = netdbHostRtt(host);
62e76326 500
9b5c4a9a 501 if (0 == list[i].rtt) {
bf8fe701 502 debugs(52, 3, "urnParseReply: Pinging " << host);
62e76326 503 netdbPingSite(host);
504 }
9b5c4a9a
AJ
505#else
506 list[i].rtt = 0;
507#endif
62e76326 508
509 list[i].url = url;
510 list[i].host = xstrdup(host);
5bd484b5
AR
511 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
512 // ones.
62e76326 513 list[i].flags.cached = storeGetPublic(url, m) ? 1 : 0;
14942edd 514 ++i;
85491f8d 515 }
62e76326 516
bf8fe701 517 debugs(52, 3, "urnParseReply: Found " << i << " URLs");
9ce5e3e6 518 return list;
85491f8d 519}