]> git.ipfire.org Git - thirdparty/squid.git/blame - src/urn.cc
Add [] operator for offset-based access into String's.
[thirdparty/squid.git] / src / urn.cc
CommitLineData
85491f8d 1
2/*
cc192b50 3 * $Id: urn.cc,v 1.108 2007/12/14 23:11:48 amosjeffries Exp $
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.
24 *
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.
29 *
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
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"
985c86bc 44#include "SquidTime.h"
85491f8d 45
add2192d 46#define URN_REQBUF_SZ 4096
47
62e76326 48class UrnState : public StoreClient
49{
50
e6ccf245 51public:
3b13a8fd 52 void created (StoreEntry *newEntry);
06096e82 53 void *operator new (size_t byteCount);
e6ccf245 54 void operator delete (void *address);
190154cf 55 void start (HttpRequest *, StoreEntry *);
30abd221 56 char *getHost (String &urlpath);
190154cf 57 void setUriResFromRequest(HttpRequest *);
58 bool RequestNeedsMenu(HttpRequest *r);
59 void updateRequestURL(HttpRequest *r, char const *newPath);
30abd221 60 void createUriResRequest (String &uri);
e6ccf245 61
62 virtual ~UrnState();
62e76326 63
64
164f7660 65 StoreEntry *entry;
06d2839d 66 store_client *sc;
164f7660 67 StoreEntry *urlres_e;
190154cf 68 HttpRequest *request;
69 HttpRequest *urlres_r;
62e76326 70
71 struct
72 {
73
74unsigned int force_menu:
75 1;
76 }
77
78 flags;
add2192d 79 char reqbuf[URN_REQBUF_SZ];
80 int reqofs;
62e76326 81
e6ccf245 82private:
83 char *urlres;
84};
85491f8d 85
62e76326 86typedef struct
87{
9ce5e3e6 88 char *url;
89 char *host;
90 int rtt;
62e76326 91
92 struct
93 {
94 int cached;
95 }
96
97 flags;
98}
99
100url_entry;
9ce5e3e6 101
102static STCB urnHandleReply;
103static url_entry *urnParseReply(const char *inbuf, method_t);
104static const char *const crlf = "\r\n";
105static QS url_entry_sort;
106
e6ccf245 107CBDATA_TYPE(UrnState);
108void *
06096e82 109UrnState::operator new (size_t byteCount)
e6ccf245 110{
62e76326 111 /* derived classes with different sizes must implement their own new */
e6ccf245 112 assert (byteCount == sizeof (UrnState));
113 CBDATA_INIT_TYPE(UrnState);
114 return cbdataAlloc(UrnState);
62e76326 115
e6ccf245 116}
117
118void
119UrnState::operator delete (void *address)
120{
1f1ae50a 121 UrnState * tmp = (UrnState *)address;
122 cbdataFree (tmp);
e6ccf245 123}
124
125UrnState::~UrnState ()
126{
127 safe_free(urlres);
128}
129
48ebcb22 130static url_entry *
9ce5e3e6 131urnFindMinRtt(url_entry * urls, method_t m, int *rtt_ret)
85491f8d 132{
23d92c64 133 int min_rtt = 0;
9ce5e3e6 134 url_entry *u = NULL;
135 url_entry *min_u = NULL;
136 int i;
1caf595b 137 int urlcnt = 0;
bf8fe701 138 debugs(52, 3, "urnFindMinRtt");
23d92c64 139 assert(urls != NULL);
62e76326 140
00141c96 141 for (i = 0; NULL != urls[i].url; i++)
62e76326 142 urlcnt++;
143
bf8fe701 144 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 145
9ce5e3e6 146 if (1 == urlcnt) {
bf8fe701 147 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
62e76326 148 return urls;
1caf595b 149 }
62e76326 150
00141c96 151 for (i = 0; i < urlcnt; i++) {
62e76326 152 u = &urls[i];
bf8fe701 153 debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
62e76326 154
155 if (u->rtt == 0)
156 continue;
157
158 if (u->rtt > min_rtt && min_rtt != 0)
159 continue;
160
161 min_rtt = u->rtt;
162
163 min_u = u;
23d92c64 164 }
62e76326 165
23d92c64 166 if (rtt_ret)
62e76326 167 *rtt_ret = min_rtt;
168
bf8fe701 169 debugs(52, 1, "urnFindMinRtt: Returning '" <<
170 (min_u ? min_u->url : "NONE") << "' RTT " <<
171 min_rtt );
62e76326 172
9ce5e3e6 173 return min_u;
85491f8d 174}
175
e6ccf245 176char *
30abd221 177UrnState::getHost (String &urlpath)
85491f8d 178{
e6ccf245 179 char * result;
180 char const *t;
62e76326 181
cc192b50 182/* FIXME: this appears to be parsing the URL. *very* badly. */
183/* FIXME: a proper encapsulated URI/URL type needs to clear this up. */
184
650c4b88 185 if ((t = urlpath.pos(':')) != NULL) {
186 urlpath.set(t, '\0');
30abd221 187 result = xstrdup(urlpath.buf());
650c4b88 188 urlpath.set(t, ':');
fa040df2 189 } else {
30abd221 190 result = xstrdup(urlpath.buf());
fa040df2 191 }
62e76326 192
e6ccf245 193 return result;
194}
195
196bool
190154cf 197UrnState::RequestNeedsMenu(HttpRequest *r)
e6ccf245 198{
30abd221 199 return strncasecmp(r->urlpath.buf(), "menu.", 5) == 0;
e6ccf245 200}
201
202void
190154cf 203UrnState::updateRequestURL(HttpRequest *r, char const *newPath)
e6ccf245 204{
62e76326 205 char *new_path = xstrdup (newPath);
206 r->urlpath = new_path;
207 xfree(new_path);
e6ccf245 208}
209
210void
30abd221 211UrnState::createUriResRequest (String &uri)
e6ccf245 212{
213 LOCAL_ARRAY(char, local_urlres, 4096);
214 char *host = getHost (uri);
30abd221 215 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:%s", host, uri.buf());
e6ccf245 216 safe_free (host);
217 safe_free (urlres);
218 urlres = xstrdup (local_urlres);
c21ad0f5 219 urlres_r = HttpRequest::CreateFromUrl(urlres);
e6ccf245 220}
221
222void
190154cf 223UrnState::setUriResFromRequest(HttpRequest *r)
e6ccf245 224{
225 if (RequestNeedsMenu(r)) {
30abd221 226 updateRequestURL(r, r->urlpath.buf() + 5);
62e76326 227 flags.force_menu = 1;
e6ccf245 228 }
62e76326 229
e6ccf245 230 createUriResRequest (r->urlpath);
62e76326 231
0adbab7c 232 if (urlres_r == NULL) {
bf8fe701 233 debugs(52, 3, "urnStart: Bad uri-res URL " << urlres);
2cc81f1f 234 ErrorState *err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, r);
62e76326 235 err->url = urlres;
236 urlres = NULL;
237 errorAppendEntry(entry, err);
238 return;
0adbab7c 239 }
62e76326 240
6dd9f4bd 241 HTTPMSGLOCK(urlres_r);
a9925b40 242 urlres_r->header.putStr(HDR_ACCEPT, "text/plain");
e6ccf245 243}
244
245void
190154cf 246UrnState::start(HttpRequest * r, StoreEntry * e)
e6ccf245 247{
bf8fe701 248 debugs(52, 3, "urnStart: '" << e->url() << "'" );
e6ccf245 249 entry = e;
6dd9f4bd 250 request = HTTPMSGLOCK(r);
34266cde 251
252 entry->lock()
253
254 ;
e6ccf245 255 setUriResFromRequest(r);
62e76326 256
e6ccf245 257 if (urlres_r == NULL)
62e76326 258 return;
259
3b13a8fd 260 StoreEntry::getPublic (this, urlres, METHOD_GET);
e6ccf245 261}
262
263void
3b13a8fd 264UrnState::created(StoreEntry *newEntry)
e6ccf245 265{
266 urlres_e = newEntry;
62e76326 267
e6ccf245 268 if (urlres_e->isNull()) {
62e76326 269 urlres_e = storeCreateEntry(urlres, urlres, request_flags(), METHOD_GET);
270 sc = storeClientListAdd(urlres_e, this);
b6b6f466 271 FwdState::fwdStart(-1, urlres_e, urlres_r);
cf26e54c 272 } else {
34266cde 273
274 urlres_e->lock()
275
276 ;
62e76326 277 sc = storeClientListAdd(urlres_e, this);
85491f8d 278 }
62e76326 279
e6ccf245 280 reqofs = 0;
528b2c61 281 StoreIOBuffer tempBuffer;
e6ccf245 282 tempBuffer.offset = reqofs;
c8be6d7b 283 tempBuffer.length = URN_REQBUF_SZ;
e6ccf245 284 tempBuffer.data = reqbuf;
285 storeClientCopy(sc, urlres_e,
62e76326 286 tempBuffer,
287 urnHandleReply,
288 this);
e6ccf245 289}
290
291void
190154cf 292urnStart(HttpRequest * r, StoreEntry * e)
e6ccf245 293{
294 UrnState *anUrn = new UrnState();
295 anUrn->start (r, e);
85491f8d 296}
297
9ce5e3e6 298static int
299url_entry_sort(const void *A, const void *B)
300{
e6ccf245 301 const url_entry *u1 = (const url_entry *)A;
302 const url_entry *u2 = (const url_entry *)B;
62e76326 303
9ce5e3e6 304 if (u2->rtt == u1->rtt)
62e76326 305 return 0;
9ce5e3e6 306 else if (0 == u1->rtt)
62e76326 307 return 1;
9ce5e3e6 308 else if (0 == u2->rtt)
62e76326 309 return -1;
9ce5e3e6 310 else
62e76326 311 return u1->rtt - u2->rtt;
9ce5e3e6 312}
313
528b2c61 314/* TODO: use the clientStream support for this */
85491f8d 315static void
c8be6d7b 316urnHandleReply(void *data, StoreIOBuffer result)
85491f8d 317{
e6ccf245 318 UrnState *urnState = static_cast<UrnState *>(data);
cf26e54c 319 StoreEntry *e = urnState->entry;
320 StoreEntry *urlres_e = urnState->urlres_e;
23d92c64 321 char *s = NULL;
2334c194 322 size_t k;
cb69b4c7 323 HttpReply *rep;
9ce5e3e6 324 url_entry *urls;
00141c96 325 url_entry *u;
9ce5e3e6 326 url_entry *min_u;
032785bf 327 MemBuf *mb = NULL;
cf26e54c 328 ErrorState *err;
9ce5e3e6 329 int i;
330 int urlcnt = 0;
add2192d 331 char *buf = urnState->reqbuf;
c8be6d7b 332 StoreIOBuffer tempBuffer;
cf26e54c 333
bf8fe701 334 debugs(52, 3, "urnHandleReply: Called with size=" << (unsigned int)result.length << ".");
62e76326 335
450e0c10 336 /* Can't be lower because of the goto's */
337 HttpVersion version(1, 0);
338
b7fe0ab0 339 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED)) {
62e76326 340 goto error;
85491f8d 341 }
62e76326 342
c8be6d7b 343 if (result.length == 0) {
62e76326 344 goto error;
c8be6d7b 345 } else if (result.flags.error < 0) {
62e76326 346 goto error;
85491f8d 347 }
62e76326 348
add2192d 349 /* Update reqofs to point to where in the buffer we'd be */
c8be6d7b 350 urnState->reqofs += result.length;
add2192d 351
352 /* Handle reqofs being bigger than normal */
353 if (urnState->reqofs >= URN_REQBUF_SZ) {
62e76326 354 goto error;
add2192d 355 }
62e76326 356
add2192d 357 /* If we haven't received the entire object (urn), copy more */
358 if (urlres_e->store_status == STORE_PENDING &&
62e76326 359 urnState->reqofs < URN_REQBUF_SZ) {
360 tempBuffer.offset = urnState->reqofs;
361 tempBuffer.length = URN_REQBUF_SZ;
362 tempBuffer.data = urnState->reqbuf + urnState->reqofs;
363 storeClientCopy(urnState->sc, urlres_e,
364 tempBuffer,
365 urnHandleReply,
366 urnState);
367 return;
23d92c64 368 }
62e76326 369
23d92c64 370 /* we know its STORE_OK */
add2192d 371 k = headersEnd(buf, urnState->reqofs);
62e76326 372
2334c194 373 if (0 == k) {
bf8fe701 374 debugs(52, 1, "urnHandleReply: didn't find end-of-headers for " << e->url() );
62e76326 375 goto error;
85491f8d 376 }
62e76326 377
2334c194 378 s = buf + k;
528b2c61 379 assert(urlres_e->getReply());
06a5ae20 380 rep = new HttpReply;
59eed7dc 381 rep->parseCharBuf(buf, k);
bf8fe701 382 debugs(52, 3, "reply exists, code=" << rep->sline.status << ".");
62e76326 383
528b2c61 384 if (rep->sline.status != HTTP_OK) {
bf8fe701 385 debugs(52, 3, "urnHandleReply: failed.");
2cc81f1f 386 err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request);
3900307b 387 err->url = xstrdup(e->url());
62e76326 388 errorAppendEntry(e, err);
06a5ae20 389 delete rep;
62e76326 390 goto error;
85491f8d 391 }
62e76326 392
06a5ae20 393 delete rep;
62e76326 394
b6a2f15e 395 while (xisspace(*s))
62e76326 396 s++;
397
9ce5e3e6 398 urls = urnParseReply(s, urnState->request->method);
62e76326 399
00141c96 400 for (i = 0; NULL != urls[i].url; i++)
62e76326 401 urlcnt++;
402
bf8fe701 403 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
62e76326 404
164f7660 405 if (urls == NULL) { /* unkown URN error */
bf8fe701 406 debugs(52, 3, "urnTranslateDone: unknown URN " << e->url() );
2cc81f1f 407 err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request);
3900307b 408 err->url = xstrdup(e->url());
62e76326 409 errorAppendEntry(e, err);
410 goto error;
164f7660 411 }
62e76326 412
9ce5e3e6 413 min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
414 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
3900307b 415 e->buffer();
032785bf 416 mb = new MemBuf;
2fe7eff9 417 mb->init();
418 mb->Printf( "<TITLE>Select URL for %s</TITLE>\n"
419 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
420 "<H2>Select URL for %s</H2>\n"
3900307b 421 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
62e76326 422
9ce5e3e6 423 for (i = 0; i < urlcnt; i++) {
62e76326 424 u = &urls[i];
bf8fe701 425 debugs(52, 3, "URL {" << u->url << "}");
2fe7eff9 426 mb->Printf(
427 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
62e76326 428
429 if (urls[i].rtt > 0)
2fe7eff9 430 mb->Printf(
431 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
62e76326 432 else
2fe7eff9 433 mb->Printf("<TD align=\"right\">Unknown</TD>");
62e76326 434
2fe7eff9 435 mb->Printf(
436 "<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
cf26e54c 437 }
62e76326 438
2fe7eff9 439 mb->Printf(
440 "</TABLE>"
441 "<HR noshade size=\"1px\">\n"
442 "<ADDRESS>\n"
443 "Generated by %s@%s\n"
444 "</ADDRESS>\n",
445 full_appname_string, getMyHostname());
06a5ae20 446 rep = new HttpReply;
447 rep->setHeaders(version, HTTP_MOVED_TEMPORARILY, NULL,
448 "text/html", mb->contentSize(), 0, squid_curtime);
62e76326 449
b515fc11 450 if (urnState->flags.force_menu) {
bf8fe701 451 debugs(51, 3, "urnHandleReply: forcing menu");
9ce5e3e6 452 } else if (min_u) {
a9925b40 453 rep->header.putStr(HDR_LOCATION, min_u->url);
cb69b4c7 454 }
62e76326 455
032785bf 456 httpBodySet(&rep->body, mb);
457 /* don't clean or delete mb; rep->body owns it now */
db237875 458 e->replaceHttpReply(rep);
528b2c61 459 e->complete();
62e76326 460
9ce5e3e6 461 for (i = 0; i < urlcnt; i++) {
62e76326 462 safe_free(urls[i].url);
463 safe_free(urls[i].host);
9ce5e3e6 464 }
62e76326 465
9ce5e3e6 466 safe_free(urls);
96a5be3d 467 /* mb was absorbed in httpBodySet call, so we must not clean it */
06d2839d 468 storeUnregister(urnState->sc, urlres_e, urnState);
62e76326 469
470error:
97b5e68f 471 urlres_e->unlock();
472 urnState->entry->unlock();
6dd9f4bd 473 HTTPMSGUNLOCK(urnState->request);
474 HTTPMSGUNLOCK(urnState->urlres_r);
e6ccf245 475 delete urnState;
85491f8d 476}
477
9ce5e3e6 478static url_entry *
479urnParseReply(const char *inbuf, method_t m)
85491f8d 480{
23d92c64 481 char *buf = xstrdup(inbuf);
482 char *token;
9ce5e3e6 483 char *url;
484 char *host;
9ce5e3e6 485 int rtt;
486 url_entry *list;
487 url_entry *old;
488 int n = 32;
489 int i = 0;
bf8fe701 490 debugs(52, 3, "urnParseReply");
e6ccf245 491 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
62e76326 492
23d92c64 493 for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
bf8fe701 494 debugs(52, 3, "urnParseReply: got '" << token << "'");
62e76326 495
496 if (i == n) {
497 old = list;
498 n <<= 2;
499 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
500 xmemcpy(list, old, i * sizeof(*list));
501 safe_free(old);
502 }
503
504 url = xstrdup(token);
505 host = urlHostname(url);
506
507 if (NULL == host)
508 continue;
509
510 rtt = netdbHostRtt(host);
511
512 if (0 == rtt) {
bf8fe701 513 debugs(52, 3, "urnParseReply: Pinging " << host);
62e76326 514 netdbPingSite(host);
515 }
516
517 list[i].url = url;
518 list[i].host = xstrdup(host);
519 list[i].rtt = rtt;
520 list[i].flags.cached = storeGetPublic(url, m) ? 1 : 0;
521 i++;
85491f8d 522 }
62e76326 523
bf8fe701 524 debugs(52, 3, "urnParseReply: Found " << i << " URLs");
9ce5e3e6 525 return list;
85491f8d 526}