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