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