]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/urn.cc
2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
9 /* DEBUG: section 52 URN Parsing */
13 #include "errorpage.h"
16 #include "HttpReply.h"
17 #include "HttpRequest.h"
18 #include "icmp/net_db.h"
20 #include "mime_header.h"
21 #include "RequestFlags.h"
22 #include "SquidTime.h"
24 #include "StoreClient.h"
29 #define URN_REQBUF_SZ 4096
31 class UrnState
: public StoreClient
33 CBDATA_CLASS(UrnState
);
36 void created (StoreEntry
*newEntry
);
37 void start (HttpRequest
*, StoreEntry
*);
38 char *getHost(const SBuf
&urlpath
);
39 void setUriResFromRequest(HttpRequest
*);
46 HttpRequest::Pointer request
;
47 HttpRequest::Pointer urlres_r
;
52 char reqbuf
[URN_REQBUF_SZ
];
69 static STCB urnHandleReply
;
70 static url_entry
*urnParseReply(const char *inbuf
, const HttpRequestMethod
&);
71 static const char *const crlf
= "\r\n";
72 static QS url_entry_sort
;
74 CBDATA_CLASS_INIT(UrnState
);
82 urnFindMinRtt(url_entry
* urls
, const HttpRequestMethod
&, int *rtt_ret
)
86 url_entry
*min_u
= NULL
;
89 debugs(52, 3, "urnFindMinRtt");
92 for (i
= 0; NULL
!= urls
[i
].url
; ++i
)
95 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
98 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
102 for (i
= 0; i
< urlcnt
; ++i
) {
104 debugs(52, 3, "urnFindMinRtt: " << u
->host
<< " rtt=" << u
->rtt
);
109 if (u
->rtt
> min_rtt
&& min_rtt
!= 0)
120 debugs(52, DBG_IMPORTANT
, "urnFindMinRtt: Returning '" <<
121 (min_u
? min_u
->url
: "NONE") << "' RTT " <<
128 UrnState::getHost(const SBuf
&urlpath
)
130 /** FIXME: this appears to be parsing the URL. *very* badly. */
131 /* a proper encapsulated URI/URL type needs to clear this up. */
133 if ((p
= urlpath
.find(':')) != SBuf::npos
)
134 return SBufToCstring(urlpath
.substr(0, p
-1));
136 return SBufToCstring(urlpath
);
140 UrnState::setUriResFromRequest(HttpRequest
*r
)
142 static const SBuf
menu(".menu");
143 if (r
->url
.path().startsWith(menu
)) {
144 r
->url
.path(r
->url
.path().substr(5)); // strip prefix "menu."
145 flags
.force_menu
= true;
148 SBuf uri
= r
->url
.path();
149 LOCAL_ARRAY(char, local_urlres
, 4096);
150 char *host
= getHost(uri
);
151 snprintf(local_urlres
, 4096, "http://%s/uri-res/N2L?urn:" SQUIDSBUFPH
, host
, SQUIDSBUFPRINT(uri
));
154 urlres
= xstrdup(local_urlres
);
155 urlres_r
= HttpRequest::CreateFromUrl(urlres
);
157 if (urlres_r
== NULL
) {
158 debugs(52, 3, "urnStart: Bad uri-res URL " << urlres
);
159 ErrorState
*err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, r
);
162 errorAppendEntry(entry
, err
);
166 urlres_r
->header
.putStr(HDR_ACCEPT
, "text/plain");
170 UrnState::start(HttpRequest
* r
, StoreEntry
* e
)
172 debugs(52, 3, "urnStart: '" << e
->url() << "'" );
176 entry
->lock("UrnState::start");
177 setUriResFromRequest(r
);
179 if (urlres_r
== NULL
)
182 StoreEntry::getPublic (this, urlres
, Http::METHOD_GET
);
186 UrnState::created(StoreEntry
*newEntry
)
190 if (urlres_e
->isNull()) {
191 urlres_e
= storeCreateEntry(urlres
, urlres
, RequestFlags(), Http::METHOD_GET
);
192 sc
= storeClientListAdd(urlres_e
, this);
193 FwdState::fwdStart(Comm::ConnectionPointer(), urlres_e
, urlres_r
.getRaw());
195 urlres_e
->lock("UrnState::created");
196 sc
= storeClientListAdd(urlres_e
, this);
200 StoreIOBuffer tempBuffer
;
201 tempBuffer
.offset
= reqofs
;
202 tempBuffer
.length
= URN_REQBUF_SZ
;
203 tempBuffer
.data
= reqbuf
;
204 storeClientCopy(sc
, urlres_e
,
211 urnStart(HttpRequest
* r
, StoreEntry
* e
)
213 UrnState
*anUrn
= new UrnState();
218 url_entry_sort(const void *A
, const void *B
)
220 const url_entry
*u1
= (const url_entry
*)A
;
221 const url_entry
*u2
= (const url_entry
*)B
;
223 if (u2
->rtt
== u1
->rtt
)
225 else if (0 == u1
->rtt
)
227 else if (0 == u2
->rtt
)
230 return u1
->rtt
- u2
->rtt
;
234 urnHandleReplyError(UrnState
*urnState
, StoreEntry
*urlres_e
)
236 urlres_e
->unlock("urnHandleReplyError+res");
237 urnState
->entry
->unlock("urnHandleReplyError+prime");
241 /* TODO: use the clientStream support for this */
243 urnHandleReply(void *data
, StoreIOBuffer result
)
245 UrnState
*urnState
= static_cast<UrnState
*>(data
);
246 StoreEntry
*e
= urnState
->entry
;
247 StoreEntry
*urlres_e
= urnState
->urlres_e
;
258 char *buf
= urnState
->reqbuf
;
259 StoreIOBuffer tempBuffer
;
261 debugs(52, 3, "urnHandleReply: Called with size=" << result
.length
<< ".");
263 if (EBIT_TEST(urlres_e
->flags
, ENTRY_ABORTED
) || result
.length
== 0 || result
.flags
.error
) {
264 urnHandleReplyError(urnState
, urlres_e
);
268 /* Update reqofs to point to where in the buffer we'd be */
269 urnState
->reqofs
+= result
.length
;
271 /* Handle reqofs being bigger than normal */
272 if (urnState
->reqofs
>= URN_REQBUF_SZ
) {
273 urnHandleReplyError(urnState
, urlres_e
);
277 /* If we haven't received the entire object (urn), copy more */
278 if (urlres_e
->store_status
== STORE_PENDING
&&
279 urnState
->reqofs
< URN_REQBUF_SZ
) {
280 tempBuffer
.offset
= urnState
->reqofs
;
281 tempBuffer
.length
= URN_REQBUF_SZ
;
282 tempBuffer
.data
= urnState
->reqbuf
+ urnState
->reqofs
;
283 storeClientCopy(urnState
->sc
, urlres_e
,
290 /* we know its STORE_OK */
291 k
= headersEnd(buf
, urnState
->reqofs
);
294 debugs(52, DBG_IMPORTANT
, "urnHandleReply: didn't find end-of-headers for " << e
->url() );
295 urnHandleReplyError(urnState
, urlres_e
);
300 assert(urlres_e
->getReply());
302 rep
->parseCharBuf(buf
, k
);
303 debugs(52, 3, "reply exists, code=" << rep
->sline
.status() << ".");
305 if (rep
->sline
.status() != Http::scOkay
) {
306 debugs(52, 3, "urnHandleReply: failed.");
307 err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, urnState
->request
.getRaw());
308 err
->url
= xstrdup(e
->url());
309 errorAppendEntry(e
, err
);
311 urnHandleReplyError(urnState
, urlres_e
);
320 urls
= urnParseReply(s
, urnState
->request
->method
);
322 for (i
= 0; NULL
!= urls
[i
].url
; ++i
)
325 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
327 if (urls
== NULL
) { /* unkown URN error */
328 debugs(52, 3, "urnTranslateDone: unknown URN " << e
->url());
329 err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, urnState
->request
.getRaw());
330 err
->url
= xstrdup(e
->url());
331 errorAppendEntry(e
, err
);
332 urnHandleReplyError(urnState
, urlres_e
);
336 min_u
= urnFindMinRtt(urls
, urnState
->request
->method
, NULL
);
337 qsort(urls
, urlcnt
, sizeof(*urls
), url_entry_sort
);
341 mb
->appendf( "<TITLE>Select URL for %s</TITLE>\n"
342 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
343 "<H2>Select URL for %s</H2>\n"
344 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e
->url(), e
->url());
346 for (i
= 0; i
< urlcnt
; ++i
) {
348 debugs(52, 3, "URL {" << u
->url
<< "}");
350 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u
->url
, u
->url
);
354 "<TD align=\"right\">%4d <it>ms</it></TD>", u
->rtt
);
356 mb
->appendf("<TD align=\"right\">Unknown</TD>");
358 mb
->appendf("<TD>%s</TD></TR>\n", u
->flags
.cached
? " [cached]" : " ");
363 "<HR noshade size=\"1px\">\n"
365 "Generated by %s@%s\n"
367 APP_FULLNAME
, getMyHostname());
369 rep
->setHeaders(Http::scFound
, NULL
, "text/html", mb
->contentSize(), 0, squid_curtime
);
371 if (urnState
->flags
.force_menu
) {
372 debugs(51, 3, "urnHandleReply: forcing menu");
374 rep
->header
.putStr(HDR_LOCATION
, min_u
->url
);
378 /* don't clean or delete mb; rep->body owns it now */
379 e
->replaceHttpReply(rep
);
382 for (i
= 0; i
< urlcnt
; ++i
) {
383 safe_free(urls
[i
].url
);
384 safe_free(urls
[i
].host
);
388 /* mb was absorbed in httpBodySet call, so we must not clean it */
389 storeUnregister(urnState
->sc
, urlres_e
, urnState
);
391 urnHandleReplyError(urnState
, urlres_e
);
395 urnParseReply(const char *inbuf
, const HttpRequestMethod
& m
)
397 char *buf
= xstrdup(inbuf
);
405 debugs(52, 3, "urnParseReply");
406 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
408 for (token
= strtok(buf
, crlf
); token
; token
= strtok(NULL
, crlf
)) {
409 debugs(52, 3, "urnParseReply: got '" << token
<< "'");
414 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
415 memcpy(list
, old
, i
* sizeof(*list
));
419 url
= xstrdup(token
);
420 host
= urlHostname(url
);
426 list
[i
].rtt
= netdbHostRtt(host
);
428 if (0 == list
[i
].rtt
) {
429 debugs(52, 3, "urnParseReply: Pinging " << host
);
437 list
[i
].host
= xstrdup(host
);
438 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
440 list
[i
].flags
.cached
= storeGetPublic(url
, m
) ? 1 : 0;
444 debugs(52, 3, "urnParseReply: Found " << i
<< " URLs");