]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/urn.cc
5 * DEBUG: section 52 URN Parsing
6 * AUTHOR: Kostas Anagnostakis
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
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.
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.
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.
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.
37 #include "errorpage.h"
38 #include "StoreClient.h"
40 #include "HttpReply.h"
41 #include "HttpRequest.h"
45 #include "SquidTime.h"
46 #include "icmp/net_db.h"
47 #include "mime_header.h"
51 #define URN_REQBUF_SZ 4096
53 class UrnState
: public StoreClient
57 void created (StoreEntry
*newEntry
);
58 void *operator new (size_t byteCount
);
59 void operator delete (void *address
);
60 void start (HttpRequest
*, StoreEntry
*);
61 char *getHost (String
&urlpath
);
62 void setUriResFromRequest(HttpRequest
*);
63 bool RequestNeedsMenu(HttpRequest
*r
);
64 void updateRequestURL(HttpRequest
*r
, char const *newPath
, const size_t newPath_len
);
65 void createUriResRequest (String
&uri
);
73 HttpRequest
*urlres_r
;
76 unsigned int force_menu
:1;
78 char reqbuf
[URN_REQBUF_SZ
];
95 static STCB urnHandleReply
;
96 static url_entry
*urnParseReply(const char *inbuf
, const HttpRequestMethod
&);
97 static const char *const crlf
= "\r\n";
98 static QS url_entry_sort
;
100 CBDATA_TYPE(UrnState
);
102 UrnState::operator new (size_t byteCount
)
104 /* derived classes with different sizes must implement their own new */
105 assert (byteCount
== sizeof (UrnState
));
106 CBDATA_INIT_TYPE(UrnState
);
107 return cbdataAlloc(UrnState
);
112 UrnState::operator delete (void *address
)
114 UrnState
* tmp
= (UrnState
*)address
;
118 UrnState::~UrnState ()
124 urnFindMinRtt(url_entry
* urls
, const HttpRequestMethod
& m
, int *rtt_ret
)
128 url_entry
*min_u
= NULL
;
131 debugs(52, 3, "urnFindMinRtt");
132 assert(urls
!= NULL
);
134 for (i
= 0; NULL
!= urls
[i
].url
; ++i
)
137 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
140 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
144 for (i
= 0; i
< urlcnt
; ++i
) {
146 debugs(52, 3, "urnFindMinRtt: " << u
->host
<< " rtt=" << u
->rtt
);
151 if (u
->rtt
> min_rtt
&& min_rtt
!= 0)
162 debugs(52, DBG_IMPORTANT
, "urnFindMinRtt: Returning '" <<
163 (min_u
? min_u
->url
: "NONE") << "' RTT " <<
170 UrnState::getHost (String
&urlpath
)
175 /** FIXME: this appears to be parsing the URL. *very* badly. */
176 /* a proper encapsulated URI/URL type needs to clear this up. */
177 if ((p
=urlpath
.find(':')) != String::npos
) {
178 result
=xstrndup(urlpath
.rawBuf(),p
-1);
180 result
= xstrndup(urlpath
.rawBuf(),urlpath
.size());
186 UrnState::RequestNeedsMenu(HttpRequest
*r
)
188 if (r
->urlpath
.size() < 5)
190 //now we're sure it's long enough
191 return strncasecmp(r
->urlpath
.rawBuf(), "menu.", 5) == 0;
195 UrnState::updateRequestURL(HttpRequest
*r
, char const *newPath
, const size_t newPath_len
)
197 char *new_path
= xstrndup (newPath
, newPath_len
);
198 r
->urlpath
= new_path
;
203 UrnState::createUriResRequest (String
&uri
)
205 LOCAL_ARRAY(char, local_urlres
, 4096);
206 char *host
= getHost (uri
);
207 snprintf(local_urlres
, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSTRINGPH
,
208 host
, SQUIDSTRINGPRINT(uri
));
211 urlres
= xstrdup (local_urlres
);
212 urlres_r
= HttpRequest::CreateFromUrl(urlres
);
216 UrnState::setUriResFromRequest(HttpRequest
*r
)
218 if (RequestNeedsMenu(r
)) {
219 updateRequestURL(r
, r
->urlpath
.rawBuf() + 5, r
->urlpath
.size() - 5 );
220 flags
.force_menu
= 1;
223 createUriResRequest (r
->urlpath
);
225 if (urlres_r
== NULL
) {
226 debugs(52, 3, "urnStart: Bad uri-res URL " << urlres
);
227 ErrorState
*err
= new ErrorState(ERR_URN_RESOLVE
, HTTP_NOT_FOUND
, r
);
230 errorAppendEntry(entry
, err
);
234 HTTPMSGLOCK(urlres_r
);
235 urlres_r
->header
.putStr(HDR_ACCEPT
, "text/plain");
239 UrnState::start(HttpRequest
* r
, StoreEntry
* e
)
241 debugs(52, 3, "urnStart: '" << e
->url() << "'" );
243 request
= HTTPMSGLOCK(r
);
246 setUriResFromRequest(r
);
248 if (urlres_r
== NULL
)
251 StoreEntry::getPublic (this, urlres
, METHOD_GET
);
255 UrnState::created(StoreEntry
*newEntry
)
259 if (urlres_e
->isNull()) {
260 urlres_e
= storeCreateEntry(urlres
, urlres
, request_flags(), METHOD_GET
);
261 sc
= storeClientListAdd(urlres_e
, this);
262 FwdState::fwdStart(Comm::ConnectionPointer(), urlres_e
, urlres_r
);
266 sc
= storeClientListAdd(urlres_e
, this);
270 StoreIOBuffer tempBuffer
;
271 tempBuffer
.offset
= reqofs
;
272 tempBuffer
.length
= URN_REQBUF_SZ
;
273 tempBuffer
.data
= reqbuf
;
274 storeClientCopy(sc
, urlres_e
,
281 urnStart(HttpRequest
* r
, StoreEntry
* e
)
283 UrnState
*anUrn
= new UrnState();
288 url_entry_sort(const void *A
, const void *B
)
290 const url_entry
*u1
= (const url_entry
*)A
;
291 const url_entry
*u2
= (const url_entry
*)B
;
293 if (u2
->rtt
== u1
->rtt
)
295 else if (0 == u1
->rtt
)
297 else if (0 == u2
->rtt
)
300 return u1
->rtt
- u2
->rtt
;
304 urnHandleReplyError(UrnState
*urnState
, StoreEntry
*urlres_e
)
307 urnState
->entry
->unlock();
308 HTTPMSGUNLOCK(urnState
->request
);
309 HTTPMSGUNLOCK(urnState
->urlres_r
);
313 /* TODO: use the clientStream support for this */
315 urnHandleReply(void *data
, StoreIOBuffer result
)
317 UrnState
*urnState
= static_cast<UrnState
*>(data
);
318 StoreEntry
*e
= urnState
->entry
;
319 StoreEntry
*urlres_e
= urnState
->urlres_e
;
330 char *buf
= urnState
->reqbuf
;
331 StoreIOBuffer tempBuffer
;
333 debugs(52, 3, "urnHandleReply: Called with size=" << result
.length
<< ".");
335 if (EBIT_TEST(urlres_e
->flags
, ENTRY_ABORTED
) || result
.length
== 0 || result
.flags
.error
) {
336 urnHandleReplyError(urnState
, urlres_e
);
340 /* Update reqofs to point to where in the buffer we'd be */
341 urnState
->reqofs
+= result
.length
;
343 /* Handle reqofs being bigger than normal */
344 if (urnState
->reqofs
>= URN_REQBUF_SZ
) {
345 urnHandleReplyError(urnState
, urlres_e
);
349 /* If we haven't received the entire object (urn), copy more */
350 if (urlres_e
->store_status
== STORE_PENDING
&&
351 urnState
->reqofs
< URN_REQBUF_SZ
) {
352 tempBuffer
.offset
= urnState
->reqofs
;
353 tempBuffer
.length
= URN_REQBUF_SZ
;
354 tempBuffer
.data
= urnState
->reqbuf
+ urnState
->reqofs
;
355 storeClientCopy(urnState
->sc
, urlres_e
,
362 /* we know its STORE_OK */
363 k
= headersEnd(buf
, urnState
->reqofs
);
366 debugs(52, DBG_IMPORTANT
, "urnHandleReply: didn't find end-of-headers for " << e
->url() );
367 urnHandleReplyError(urnState
, urlres_e
);
372 assert(urlres_e
->getReply());
374 rep
->parseCharBuf(buf
, k
);
375 debugs(52, 3, "reply exists, code=" << rep
->sline
.status
<< ".");
377 if (rep
->sline
.status
!= HTTP_OK
) {
378 debugs(52, 3, "urnHandleReply: failed.");
379 err
= new ErrorState(ERR_URN_RESOLVE
, HTTP_NOT_FOUND
, urnState
->request
);
380 err
->url
= xstrdup(e
->url());
381 errorAppendEntry(e
, err
);
383 urnHandleReplyError(urnState
, urlres_e
);
392 urls
= urnParseReply(s
, urnState
->request
->method
);
394 for (i
= 0; NULL
!= urls
[i
].url
; ++i
)
397 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
399 if (urls
== NULL
) { /* unkown URN error */
400 debugs(52, 3, "urnTranslateDone: unknown URN " << e
->url() );
401 err
= new ErrorState(ERR_URN_RESOLVE
, HTTP_NOT_FOUND
, urnState
->request
);
402 err
->url
= xstrdup(e
->url());
403 errorAppendEntry(e
, err
);
404 urnHandleReplyError(urnState
, urlres_e
);
408 min_u
= urnFindMinRtt(urls
, urnState
->request
->method
, NULL
);
409 qsort(urls
, urlcnt
, sizeof(*urls
), url_entry_sort
);
413 mb
->Printf( "<TITLE>Select URL for %s</TITLE>\n"
414 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
415 "<H2>Select URL for %s</H2>\n"
416 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e
->url(), e
->url());
418 for (i
= 0; i
< urlcnt
; ++i
) {
420 debugs(52, 3, "URL {" << u
->url
<< "}");
422 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u
->url
, u
->url
);
426 "<TD align=\"right\">%4d <it>ms</it></TD>", u
->rtt
);
428 mb
->Printf("<TD align=\"right\">Unknown</TD>");
431 "<TD>%s</TD></TR>\n", u
->flags
.cached
? " [cached]" : " ");
436 "<HR noshade size=\"1px\">\n"
438 "Generated by %s@%s\n"
440 APP_FULLNAME
, getMyHostname());
442 rep
->setHeaders(HTTP_MOVED_TEMPORARILY
, NULL
, "text/html", mb
->contentSize(), 0, squid_curtime
);
444 if (urnState
->flags
.force_menu
) {
445 debugs(51, 3, "urnHandleReply: forcing menu");
447 rep
->header
.putStr(HDR_LOCATION
, min_u
->url
);
451 /* don't clean or delete mb; rep->body owns it now */
452 e
->replaceHttpReply(rep
);
455 for (i
= 0; i
< urlcnt
; ++i
) {
456 safe_free(urls
[i
].url
);
457 safe_free(urls
[i
].host
);
461 /* mb was absorbed in httpBodySet call, so we must not clean it */
462 storeUnregister(urnState
->sc
, urlres_e
, urnState
);
464 urnHandleReplyError(urnState
, urlres_e
);
468 urnParseReply(const char *inbuf
, const HttpRequestMethod
& m
)
470 char *buf
= xstrdup(inbuf
);
478 debugs(52, 3, "urnParseReply");
479 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
481 for (token
= strtok(buf
, crlf
); token
; token
= strtok(NULL
, crlf
)) {
482 debugs(52, 3, "urnParseReply: got '" << token
<< "'");
487 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
488 memcpy(list
, old
, i
* sizeof(*list
));
492 url
= xstrdup(token
);
493 host
= urlHostname(url
);
499 list
[i
].rtt
= netdbHostRtt(host
);
501 if (0 == list
[i
].rtt
) {
502 debugs(52, 3, "urnParseReply: Pinging " << host
);
510 list
[i
].host
= xstrdup(host
);
511 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
513 list
[i
].flags
.cached
= storeGetPublic(url
, m
) ? 1 : 0;
517 debugs(52, 3, "urnParseReply: Found " << i
<< " URLs");