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