/* * $Id: urn.c,v 1.82.2.1 2008/04/08 22:52:59 hno Exp $ * * DEBUG: section 52 URN Parsing * AUTHOR: Kostas Anagnostakis * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- * * Squid is the result of efforts by numerous individuals from * the Internet community; see the CONTRIBUTORS file for full * details. Many organizations have provided support for Squid's * development; see the SPONSORS file for full details. Squid is * Copyrighted (C) 2001 by the Regents of the University of * California; see the COPYRIGHT file for full details. Squid * incorporates software developed and/or copyrighted by other * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid.h" typedef struct { StoreEntry *entry; store_client *sc; StoreEntry *urlres_e; request_t *request; request_t *urlres_r; struct { unsigned int force_menu:1; } flags; } UrnState; typedef struct { char *url; char *host; int rtt; struct { int cached; } flags; } url_entry; static STCB urnHandleReply; static url_entry *urnParseReply(const char *inbuf, method_t); static const char *const crlf = "\r\n"; static QS url_entry_sort; static url_entry * urnFindMinRtt(url_entry * urls, method_t m, int *rtt_ret) { int min_rtt = 0; url_entry *u = NULL; url_entry *min_u = NULL; int i; int urlcnt = 0; debug(52, 3) ("urnFindMinRtt\n"); assert(urls != NULL); for (i = 0; NULL != urls[i].url; i++) urlcnt++; debug(52, 3) ("urnFindMinRtt: Counted %d URLs\n", i); if (1 == urlcnt) { debug(52, 3) ("urnFindMinRtt: Only one URL - return it!\n"); return urls; } for (i = 0; i < urlcnt; i++) { u = &urls[i]; debug(52, 3) ("urnFindMinRtt: %s rtt=%d\n", u->host, u->rtt); if (u->rtt == 0) continue; if (u->rtt > min_rtt && min_rtt != 0) continue; min_rtt = u->rtt; min_u = u; } if (rtt_ret) *rtt_ret = min_rtt; debug(52, 1) ("urnFindMinRtt: Returning '%s' RTT %d\n", min_u ? min_u->url : "NONE", min_rtt); return min_u; } CBDATA_TYPE(UrnState); void urnStart(request_t * r, StoreEntry * e) { LOCAL_ARRAY(char, urlres, 4096); request_t *urlres_r = NULL; const char *t; char *host; UrnState *urnState; StoreEntry *urlres_e; ErrorState *err; debug(52, 3) ("urnStart: '%s'\n", storeUrl(e)); CBDATA_INIT_TYPE(UrnState); urnState = cbdataAlloc(UrnState); urnState->entry = e; urnState->request = requestLink(r); storeLockObject(urnState->entry); if (strncasecmp(strBuf(r->urlpath), "menu.", 5) == 0) { char *new_path = xstrdup(strBuf(r->urlpath) + 5); urnState->flags.force_menu = 1; stringReset(&r->urlpath, new_path); xfree(new_path); } if ((t = strChr(r->urlpath, ':')) != NULL) { strSet(r->urlpath, t, '\0'); host = xstrdup(strBuf(r->urlpath)); strSet(r->urlpath, t, ':'); } else { host = xstrdup(strBuf(r->urlpath)); } snprintf(urlres, 4096, "http://%s/uri-res/N2L?urn:%s", host, strBuf(r->urlpath)); safe_free(host); urlres_r = urlParse(METHOD_GET, urlres); if (urlres_r == NULL) { debug(52, 3) ("urnStart: Bad uri-res URL %s\n", urlres); err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, r); err->url = xstrdup(urlres); errorAppendEntry(e, err); return; } httpHeaderPutStr(&urlres_r->header, HDR_ACCEPT, "text/plain"); if ((urlres_e = storeGetPublic(urlres, METHOD_GET)) == NULL) { urlres_e = storeCreateEntry(urlres, null_request_flags, METHOD_GET); urnState->sc = storeClientRegister(urlres_e, urnState); fwdStart(-1, urlres_e, urlres_r); } else { storeLockObject(urlres_e); urnState->sc = storeClientRegister(urlres_e, urnState); } urnState->urlres_e = urlres_e; urnState->urlres_r = requestLink(urlres_r); storeClientCopy(urnState->sc, urlres_e, 0, 0, 4096, memAllocate(MEM_4K_BUF), urnHandleReply, urnState); } static int url_entry_sort(const void *A, const void *B) { const url_entry *u1 = A; const url_entry *u2 = B; if (u2->rtt == u1->rtt) return 0; else if (0 == u1->rtt) return 1; else if (0 == u2->rtt) return -1; else return u1->rtt - u2->rtt; } static void urnHandleReply(void *data, char *buf, ssize_t size) { UrnState *urnState = data; StoreEntry *e = urnState->entry; StoreEntry *urlres_e = urnState->urlres_e; char *s = NULL; HttpReply *rep; url_entry *urls; url_entry *u; url_entry *min_u; MemBuf mb; ErrorState *err; int i; int urlcnt = 0; debug(52, 3) ("urnHandleReply: Called with size=%d.\n", (int) size); if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED)) { memFree(buf, MEM_4K_BUF); return; } if (size == 0) { memFree(buf, MEM_4K_BUF); return; } else if (size < 0) { memFree(buf, MEM_4K_BUF); return; } if (urlres_e->store_status == STORE_PENDING && size < SM_PAGE_SIZE) { storeClientCopy(urnState->sc, urlres_e, size, 0, SM_PAGE_SIZE, buf, urnHandleReply, urnState); return; } debug(52, 3) ("mem->reply exists, code=%d.\n", urlres_e->mem_obj->reply->sline.status); if (urlres_e->mem_obj->reply->sline.status != HTTP_OK) { debug(52, 3) ("urnHandleReply: failed.\n"); err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request); err->url = xstrdup(storeUrl(e)); errorAppendEntry(e, err); return; } /* Make sure the data is null terminated so we can parse it as a string */ if (size == SM_PAGE_SIZE) size--; s = buf + urlres_e->mem_obj->reply->hdr_sz; size -= urlres_e->mem_obj->reply->hdr_sz; if (size < 0) goto error; while (xisspace(*s) && size > 0) { s++; size--; } s = xstrndup(s, size); urls = urnParseReply(s, urnState->request->method); safe_free(s); for (i = 0; NULL != urls[i].url; i++) urlcnt++; debug(52, 3) ("urnHandleReply: Counted %d URLs\n", i); if (urls == NULL) { /* unkown URN error */ error: debug(52, 3) ("urnHandleReply: unknown URN %s\n", storeUrl(e)); err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND, urnState->request); err->url = xstrdup(storeUrl(e)); errorAppendEntry(e, err); return; } min_u = urnFindMinRtt(urls, urnState->request->method, NULL); qsort(urls, urlcnt, sizeof(*urls), url_entry_sort); storeBuffer(e); memBufDefInit(&mb); memBufPrintf(&mb, "Select URL for %s\n" "\n" "

Select URL for %s

\n" "\n", storeUrl(e), storeUrl(e)); for (i = 0; i < urlcnt; i++) { u = &urls[i]; debug(52, 3) ("URL {%s}\n", u->url); memBufPrintf(&mb, "", u->url, u->url); if (urls[i].rtt > 0) memBufPrintf(&mb, "", u->rtt); else memBufPrintf(&mb, ""); memBufPrintf(&mb, "\n", u->flags.cached ? " [cached]" : " "); } memBufPrintf(&mb, "
%s%4d msUnknown%s
" "
\n" "
\n" "Generated by %s@%s\n" "
\n", full_appname_string, getMyHostname()); rep = e->mem_obj->reply; httpReplyReset(rep); httpReplySetHeaders(rep, HTTP_MOVED_TEMPORARILY, NULL, "text/html", mb.size, -1, squid_curtime); if (urnState->flags.force_menu) { debug(52, 3) ("urnHandleReply: forcing menu\n"); } else if (min_u) { httpHeaderPutStr(&rep->header, HDR_LOCATION, min_u->url); } httpBodySet(&rep->body, &mb); httpReplySwapOut(rep, e); storeComplete(e); memFree(buf, MEM_4K_BUF); for (i = 0; i < urlcnt; i++) { safe_free(urls[i].url); safe_free(urls[i].host); } safe_free(urls); /* mb was absorbed in httpBodySet call, so we must not clean it */ storeClientUnregister(urnState->sc, urlres_e, urnState); storeUnlockObject(urlres_e); storeUnlockObject(urnState->entry); requestUnlink(urnState->request); requestUnlink(urnState->urlres_r); cbdataFree(urnState); } static url_entry * urnParseReply(const char *inbuf, method_t m) { char *buf = xstrdup(inbuf); char *token; char *url; char *host; int rtt; url_entry *list; url_entry *old; int n = 32; int i = 0; debug(52, 3) ("urnParseReply\n"); list = xcalloc(n + 1, sizeof(*list)); for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) { debug(52, 3) ("urnParseReply: got '%s'\n", token); if (i == n) { old = list; n <<= 2; list = xcalloc(n + 1, sizeof(*list)); xmemcpy(list, old, i * sizeof(*list)); safe_free(old); } url = xstrdup(token); host = urlHostname(url); if (NULL == host) continue; rtt = netdbHostRtt(host); if (0 == rtt) { debug(52, 3) ("urnParseReply: Pinging %s\n", host); netdbPingSite(host); } list[i].url = url; list[i].host = xstrdup(host); list[i].rtt = rtt; list[i].flags.cached = storeGetPublic(url, m) ? 1 : 0; i++; } debug(52, 3) ("urnParseReply: Found %d URLs\n", i); return list; }