]> git.ipfire.org Git - thirdparty/squid.git/blame - src/urn.cc
Document the 'carp' cache_peer option
[thirdparty/squid.git] / src / urn.cc
CommitLineData
85491f8d 1
2/*
528b2c61 3 * $Id: urn.cc,v 1.81 2003/01/23 00:37:29 robertc 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"
c8be6d7b 37#include "StoreClient.h"
e6ccf245 38#include "Store.h"
528b2c61 39#include "HttpReply.h"
40#include "HttpRequest.h"
85491f8d 41
add2192d 42#define URN_REQBUF_SZ 4096
43
e6ccf245 44class UrnState : public StoreClient {
45public:
3b13a8fd 46 void created (StoreEntry *newEntry);
06096e82 47 void *operator new (size_t byteCount);
e6ccf245 48 void operator delete (void *address);
49 void start (request_t *, StoreEntry *);
50 char *getHost (String &urlpath);
51 void setUriResFromRequest(request_t *);
52 bool RequestNeedsMenu(request_t *r);
53 void updateRequestURL(request_t *r, char const *newPath);
54 void createUriResRequest (String &uri);
55
56 virtual ~UrnState();
57
58
164f7660 59 StoreEntry *entry;
06d2839d 60 store_client *sc;
164f7660 61 StoreEntry *urlres_e;
62 request_t *request;
40548fef 63 request_t *urlres_r;
b515fc11 64 struct {
1810dde6 65 unsigned int force_menu:1;
b515fc11 66 } flags;
add2192d 67 char reqbuf[URN_REQBUF_SZ];
68 int reqofs;
e6ccf245 69private:
70 char *urlres;
71};
85491f8d 72
9ce5e3e6 73typedef struct {
74 char *url;
75 char *host;
76 int rtt;
77 struct {
78 int cached;
79 } flags;
80} url_entry;
81
82static STCB urnHandleReply;
83static url_entry *urnParseReply(const char *inbuf, method_t);
84static const char *const crlf = "\r\n";
85static QS url_entry_sort;
86
e6ccf245 87CBDATA_TYPE(UrnState);
88void *
06096e82 89UrnState::operator new (size_t byteCount)
e6ccf245 90{
91 /* derived classes with different sizes must implement their own new */
92 assert (byteCount == sizeof (UrnState));
93 CBDATA_INIT_TYPE(UrnState);
94 return cbdataAlloc(UrnState);
95
96}
97
98void
99UrnState::operator delete (void *address)
100{
101 cbdataFree ((UrnState *)address);
102}
103
104UrnState::~UrnState ()
105{
106 safe_free(urlres);
107}
108
48ebcb22 109static url_entry *
9ce5e3e6 110urnFindMinRtt(url_entry * urls, method_t m, int *rtt_ret)
85491f8d 111{
23d92c64 112 int min_rtt = 0;
9ce5e3e6 113 url_entry *u = NULL;
114 url_entry *min_u = NULL;
115 int i;
1caf595b 116 int urlcnt = 0;
6f6f0853 117 debug(52, 3) ("urnFindMinRtt\n");
23d92c64 118 assert(urls != NULL);
00141c96 119 for (i = 0; NULL != urls[i].url; i++)
1caf595b 120 urlcnt++;
94934dbe 121 debug(53, 3) ("urnFindMinRtt: Counted %d URLs\n", i);
9ce5e3e6 122 if (1 == urlcnt) {
123 debug(52, 3) ("urnFindMinRtt: Only one URL - return it!\n");
124 return urls;
1caf595b 125 }
00141c96 126 for (i = 0; i < urlcnt; i++) {
127 u = &urls[i];
9ce5e3e6 128 debug(52, 3) ("urnFindMinRtt: %s rtt=%d\n", u->host, u->rtt);
129 if (u->rtt == 0)
23d92c64 130 continue;
9ce5e3e6 131 if (u->rtt > min_rtt && min_rtt != 0)
164f7660 132 continue;
9ce5e3e6 133 min_rtt = u->rtt;
134 min_u = u;
23d92c64 135 }
136 if (rtt_ret)
137 *rtt_ret = min_rtt;
6f6f0853 138 debug(52, 1) ("urnFindMinRtt: Returning '%s' RTT %d\n",
9ce5e3e6 139 min_u ? min_u->url : "NONE",
23d92c64 140 min_rtt);
9ce5e3e6 141 return min_u;
85491f8d 142}
143
e6ccf245 144char *
145UrnState::getHost (String &urlpath)
85491f8d 146{
e6ccf245 147 char * result;
148 char const *t;
149 if ((t = strChr(urlpath, ':')) != NULL) {
150 strSet(urlpath, t, '\0');
528b2c61 151 result = xstrdup(urlpath.buf());
e6ccf245 152 strSet(urlpath, t, ':');
fa040df2 153 } else {
528b2c61 154 result = xstrdup(urlpath.buf());
fa040df2 155 }
e6ccf245 156 return result;
157}
158
159bool
160UrnState::RequestNeedsMenu(request_t *r)
161{
528b2c61 162 return strncasecmp(r->urlpath.buf(), "menu.", 5) == 0;
e6ccf245 163}
164
165void
166UrnState::updateRequestURL(request_t *r, char const *newPath)
167{
168 char *new_path = xstrdup (newPath);
528b2c61 169 r->urlpath = new_path;
e6ccf245 170 xfree(new_path);
171}
172
173void
174UrnState::createUriResRequest (String &uri)
175{
176 LOCAL_ARRAY(char, local_urlres, 4096);
177 char *host = getHost (uri);
528b2c61 178 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?urn:%s", host, uri.buf());
e6ccf245 179 safe_free (host);
180 safe_free (urlres);
181 urlres = xstrdup (local_urlres);
23d92c64 182 urlres_r = urlParse(METHOD_GET, urlres);
e6ccf245 183}
184
185void
186UrnState::setUriResFromRequest(request_t *r)
187{
188 if (RequestNeedsMenu(r)) {
528b2c61 189 updateRequestURL(r, r->urlpath.buf() + 5);
e6ccf245 190 flags.force_menu = 1;
191 }
192
193 createUriResRequest (r->urlpath);
0adbab7c 194 if (urlres_r == NULL) {
195 debug(52, 3) ("urnStart: Bad uri-res URL %s\n", urlres);
e6ccf245 196 ErrorState *err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND);
197 err->url = urlres;
198 urlres = NULL;
199 errorAppendEntry(entry, err);
0adbab7c 200 return;
201 }
e6ccf245 202 requestLink(urlres_r);
2246b732 203 httpHeaderPutStr(&urlres_r->header, HDR_ACCEPT, "text/plain");
e6ccf245 204}
205
206void
207UrnState::start(request_t * r, StoreEntry * e)
208{
209 debug(52, 3) ("urnStart: '%s'\n", storeUrl(e));
210 entry = e;
211 request = requestLink(r);
212 storeLockObject(entry);
213 setUriResFromRequest(r);
214 if (urlres_r == NULL)
215 return;
3b13a8fd 216 StoreEntry::getPublic (this, urlres, METHOD_GET);
e6ccf245 217}
218
219void
3b13a8fd 220UrnState::created(StoreEntry *newEntry)
e6ccf245 221{
222 urlres_e = newEntry;
223 if (urlres_e->isNull()) {
e429f975 224 urlres_e = storeCreateEntry(urlres, urlres, request_flags(), METHOD_GET);
e6ccf245 225 sc = storeClientListAdd(urlres_e, this);
7e3ce7b9 226 fwdStart(-1, urlres_e, urlres_r);
cf26e54c 227 } else {
164f7660 228 storeLockObject(urlres_e);
e6ccf245 229 sc = storeClientListAdd(urlres_e, this);
85491f8d 230 }
e6ccf245 231 reqofs = 0;
528b2c61 232 StoreIOBuffer tempBuffer;
e6ccf245 233 tempBuffer.offset = reqofs;
c8be6d7b 234 tempBuffer.length = URN_REQBUF_SZ;
e6ccf245 235 tempBuffer.data = reqbuf;
236 storeClientCopy(sc, urlres_e,
c8be6d7b 237 tempBuffer,
cf26e54c 238 urnHandleReply,
e6ccf245 239 this);
240}
241
242void
243urnStart(request_t * r, StoreEntry * e)
244{
245 UrnState *anUrn = new UrnState();
246 anUrn->start (r, e);
85491f8d 247}
248
9ce5e3e6 249static int
250url_entry_sort(const void *A, const void *B)
251{
e6ccf245 252 const url_entry *u1 = (const url_entry *)A;
253 const url_entry *u2 = (const url_entry *)B;
9ce5e3e6 254 if (u2->rtt == u1->rtt)
255 return 0;
256 else if (0 == u1->rtt)
257 return 1;
258 else if (0 == u2->rtt)
00141c96 259 return -1;
9ce5e3e6 260 else
00141c96 261 return u1->rtt - u2->rtt;
9ce5e3e6 262}
263
528b2c61 264/* TODO: use the clientStream support for this */
85491f8d 265static void
c8be6d7b 266urnHandleReply(void *data, StoreIOBuffer result)
85491f8d 267{
e6ccf245 268 UrnState *urnState = static_cast<UrnState *>(data);
cf26e54c 269 StoreEntry *e = urnState->entry;
270 StoreEntry *urlres_e = urnState->urlres_e;
23d92c64 271 char *s = NULL;
2334c194 272 size_t k;
cb69b4c7 273 HttpReply *rep;
9ce5e3e6 274 url_entry *urls;
00141c96 275 url_entry *u;
9ce5e3e6 276 url_entry *min_u;
02922e76 277 MemBuf mb;
cf26e54c 278 ErrorState *err;
9ce5e3e6 279 int i;
280 int urlcnt = 0;
ccf44862 281 http_version_t version;
add2192d 282 char *buf = urnState->reqbuf;
c8be6d7b 283 StoreIOBuffer tempBuffer;
cf26e54c 284
c4b7a5a9 285 debug(52, 3) ("urnHandleReply: Called with size=%u.\n", (unsigned int)result.length);
b7fe0ab0 286 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED)) {
add2192d 287 goto error;
85491f8d 288 }
c8be6d7b 289 if (result.length == 0) {
add2192d 290 goto error;
c8be6d7b 291 } else if (result.flags.error < 0) {
add2192d 292 goto error;
85491f8d 293 }
add2192d 294 /* Update reqofs to point to where in the buffer we'd be */
c8be6d7b 295 urnState->reqofs += result.length;
add2192d 296
297 /* Handle reqofs being bigger than normal */
298 if (urnState->reqofs >= URN_REQBUF_SZ) {
fa80a8ef 299 goto error;
add2192d 300 }
add2192d 301 /* If we haven't received the entire object (urn), copy more */
302 if (urlres_e->store_status == STORE_PENDING &&
fa80a8ef 303 urnState->reqofs < URN_REQBUF_SZ) {
c8be6d7b 304 tempBuffer.offset = urnState->reqofs;
305 tempBuffer.length = URN_REQBUF_SZ;
306 tempBuffer.data = urnState->reqbuf + urnState->reqofs;
06d2839d 307 storeClientCopy(urnState->sc, urlres_e,
c8be6d7b 308 tempBuffer,
23d92c64 309 urnHandleReply,
cf26e54c 310 urnState);
85491f8d 311 return;
23d92c64 312 }
313 /* we know its STORE_OK */
add2192d 314 k = headersEnd(buf, urnState->reqofs);
2334c194 315 if (0 == k) {
6f6f0853 316 debug(52, 1) ("urnHandleReply: didn't find end-of-headers for %s\n",
cf26e54c 317 storeUrl(e));
add2192d 318 goto error;
85491f8d 319 }
2334c194 320 s = buf + k;
528b2c61 321 assert(urlres_e->getReply());
322 rep = httpReplyCreate ();
323 httpReplyParse(rep, buf, k);
324 debug(52, 3) ("reply exists, code=%d.\n",
325 rep->sline.status);
326 if (rep->sline.status != HTTP_OK) {
6f6f0853 327 debug(52, 3) ("urnHandleReply: failed.\n");
cf26e54c 328 err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND);
329 err->request = requestLink(urnState->request);
330 err->url = xstrdup(storeUrl(e));
331 errorAppendEntry(e, err);
528b2c61 332 httpReplyDestroy(rep);
add2192d 333 goto error;
85491f8d 334 }
528b2c61 335 httpReplyDestroy(rep);
b6a2f15e 336 while (xisspace(*s))
23d92c64 337 s++;
9ce5e3e6 338 urls = urnParseReply(s, urnState->request->method);
00141c96 339 for (i = 0; NULL != urls[i].url; i++)
9ce5e3e6 340 urlcnt++;
94934dbe 341 debug(53, 3) ("urnFindMinRtt: Counted %d URLs\n", i);
164f7660 342 if (urls == NULL) { /* unkown URN error */
6f6f0853 343 debug(52, 3) ("urnTranslateDone: unknown URN %s\n", storeUrl(e));
cf26e54c 344 err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND);
345 err->request = requestLink(urnState->request);
346 err->url = xstrdup(storeUrl(e));
347 errorAppendEntry(e, err);
add2192d 348 goto error;
164f7660 349 }
9ce5e3e6 350 min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
351 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
cf26e54c 352 storeBuffer(e);
02922e76 353 memBufDefInit(&mb);
354 memBufPrintf(&mb,
cf26e54c 355 "<TITLE>Select URL for %s</TITLE>\n"
d8c0128e 356 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
cf26e54c 357 "<H2>Select URL for %s</H2>\n"
df339671 358 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", storeUrl(e), storeUrl(e));
9ce5e3e6 359 for (i = 0; i < urlcnt; i++) {
00141c96 360 u = &urls[i];
94934dbe 361 debug(52, 3) ("URL {%s}\n", u->url);
9ce5e3e6 362 memBufPrintf(&mb,
00141c96 363 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
364 if (urls[i].rtt > 0)
9ce5e3e6 365 memBufPrintf(&mb,
22567bb5 366 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
164f7660 367 else
df339671 368 memBufPrintf(&mb, "<TD align=\"right\">Unknown</TD>");
9ce5e3e6 369 memBufPrintf(&mb,
00141c96 370 "<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
cf26e54c 371 }
02922e76 372 memBufPrintf(&mb,
2e141322 373 "</TABLE>"
df339671 374 "<HR noshade size=\"1px\">\n"
cf26e54c 375 "<ADDRESS>\n"
02922e76 376 "Generated by %s@%s\n"
cf26e54c 377 "</ADDRESS>\n",
02922e76 378 full_appname_string, getMyHostname());
528b2c61 379 rep = httpReplyCreate();
bffee5af 380 httpBuildVersion(&version, 1, 0);
ccf44862 381 httpReplySetHeaders(rep, version, HTTP_MOVED_TEMPORARILY, NULL,
02922e76 382 "text/html", mb.size, 0, squid_curtime);
b515fc11 383 if (urnState->flags.force_menu) {
cb69b4c7 384 debug(51, 3) ("urnHandleReply: forcing menu\n");
9ce5e3e6 385 } else if (min_u) {
386 httpHeaderPutStr(&rep->header, HDR_LOCATION, min_u->url);
cb69b4c7 387 }
96a5be3d 388 httpBodySet(&rep->body, &mb);
cb69b4c7 389 httpReplySwapOut(rep, e);
528b2c61 390 e->complete();
9ce5e3e6 391 for (i = 0; i < urlcnt; i++) {
00141c96 392 safe_free(urls[i].url);
393 safe_free(urls[i].host);
9ce5e3e6 394 }
395 safe_free(urls);
96a5be3d 396 /* mb was absorbed in httpBodySet call, so we must not clean it */
06d2839d 397 storeUnregister(urnState->sc, urlres_e, urnState);
fa80a8ef 398 error:
cf26e54c 399 storeUnlockObject(urlres_e);
8d709a1b 400 storeUnlockObject(urnState->entry);
401 requestUnlink(urnState->request);
40548fef 402 requestUnlink(urnState->urlres_r);
e6ccf245 403 delete urnState;
85491f8d 404}
405
9ce5e3e6 406static url_entry *
407urnParseReply(const char *inbuf, method_t m)
85491f8d 408{
23d92c64 409 char *buf = xstrdup(inbuf);
410 char *token;
9ce5e3e6 411 char *url;
412 char *host;
9ce5e3e6 413 int rtt;
414 url_entry *list;
415 url_entry *old;
416 int n = 32;
417 int i = 0;
6f6f0853 418 debug(52, 3) ("urnParseReply\n");
e6ccf245 419 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
23d92c64 420 for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
6f6f0853 421 debug(52, 3) ("urnParseReply: got '%s'\n", token);
9ce5e3e6 422 if (i == n) {
423 old = list;
424 n <<= 2;
e6ccf245 425 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
9ce5e3e6 426 xmemcpy(list, old, i * sizeof(*list));
427 safe_free(old);
428 }
429 url = xstrdup(token);
430 host = urlHostname(url);
431 if (NULL == host)
432 continue;
433 rtt = netdbHostRtt(host);
434 if (0 == rtt) {
435 debug(52, 3) ("urnParseReply: Pinging %s\n", host);
436 netdbPingSite(host);
437 }
00141c96 438 list[i].url = url;
439 list[i].host = xstrdup(host);
440 list[i].rtt = rtt;
08e5d64f 441 list[i].flags.cached = storeGetPublic(url, m) ? 1 : 0;
9ce5e3e6 442 i++;
85491f8d 443 }
94934dbe 444 debug(52, 3) ("urnParseReply: Found %d URLs\n", i);
9ce5e3e6 445 return list;
85491f8d 446}