]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/urn.cc
2 * Copyright (C) 1996-2022 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 */
12 #include "AccessLogEntry.h"
13 #include "acl/FilledChecklist.h"
14 #include "base/TextException.h"
16 #include "errorpage.h"
19 #include "HttpReply.h"
20 #include "HttpRequest.h"
21 #include "icmp/net_db.h"
23 #include "mime_header.h"
24 #include "RequestFlags.h"
26 #include "StoreClient.h"
30 #define URN_REQBUF_SZ 4096
32 class UrnState
: public StoreClient
34 CBDATA_CLASS(UrnState
);
37 explicit UrnState(const AccessLogEntry::Pointer
&anAle
): ale(anAle
) {}
39 void start (HttpRequest
*, StoreEntry
*);
40 void setUriResFromRequest(HttpRequest
*);
44 StoreEntry
*entry
= nullptr;
45 store_client
*sc
= nullptr;
46 StoreEntry
*urlres_e
= nullptr;
47 HttpRequest::Pointer request
;
48 HttpRequest::Pointer urlres_r
;
49 AccessLogEntry::Pointer ale
; ///< details of the requesting transaction
51 char reqbuf
[URN_REQBUF_SZ
] = { '\0' };
56 virtual LogTags
*loggingTags() const { return ale
? &ale
->cache
.code
: nullptr; }
57 virtual void fillChecklist(ACLFilledChecklist
&) const;
59 char *urlres
= nullptr;
72 static STCB urnHandleReply
;
73 static url_entry
*urnParseReply(const char *inbuf
, const HttpRequestMethod
&);
74 static const char *const crlf
= "\r\n";
76 CBDATA_CLASS_INIT(UrnState
);
83 storeUnregister(sc
, urlres_e
, this);
84 urlres_e
->unlock("~UrnState+res");
88 entry
->unlock("~UrnState+prime");
95 urnFindMinRtt(url_entry
* urls
, const HttpRequestMethod
&, int *rtt_ret
)
99 url_entry
*min_u
= NULL
;
102 debugs(52, 3, "urnFindMinRtt");
103 assert(urls
!= NULL
);
105 for (i
= 0; NULL
!= urls
[i
].url
; ++i
)
108 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
111 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
115 for (i
= 0; i
< urlcnt
; ++i
) {
117 debugs(52, 3, "urnFindMinRtt: " << u
->host
<< " rtt=" << u
->rtt
);
122 if (u
->rtt
> min_rtt
&& min_rtt
!= 0)
133 debugs(52, DBG_IMPORTANT
, "urnFindMinRtt: Returning '" <<
134 (min_u
? min_u
->url
: "NONE") << "' RTT " <<
141 UrnState::setUriResFromRequest(HttpRequest
*r
)
143 const auto &query
= r
->url
.absolute();
144 const auto host
= r
->url
.host();
145 // TODO: use class AnyP::Uri instead of generating a string and re-parsing
146 LOCAL_ARRAY(char, local_urlres
, 4096);
147 snprintf(local_urlres
, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH
, host
, SQUIDSBUFPRINT(query
));
149 urlres_r
= HttpRequest::FromUrlXXX(local_urlres
, r
->masterXaction
);
152 debugs(52, 3, "Bad uri-res URL " << local_urlres
);
153 const auto err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, r
, ale
);
154 err
->url
= xstrdup(local_urlres
);
155 errorAppendEntry(entry
, err
);
159 urlres
= xstrdup(local_urlres
);
160 urlres_r
->header
.putStr(Http::HdrType::ACCEPT
, "text/plain");
164 UrnState::start(HttpRequest
* r
, StoreEntry
* e
)
166 debugs(52, 3, "urnStart: '" << e
->url() << "'" );
170 entry
->lock("UrnState::start");
171 setUriResFromRequest(r
);
173 if (urlres_r
== NULL
)
176 auto urlEntry
= storeGetPublic(urlres
, Http::METHOD_GET
);
178 if (!urlEntry
|| (urlEntry
->hittingRequiresCollapsing() && !startCollapsingOn(*urlEntry
, false))) {
179 urlres_e
= storeCreateEntry(urlres
, urlres
, RequestFlags(), Http::METHOD_GET
);
180 sc
= storeClientListAdd(urlres_e
, this);
181 FwdState::Start(Comm::ConnectionPointer(), urlres_e
, urlres_r
.getRaw(), ale
);
183 urlEntry
->abandon(__FUNCTION__
);
188 urlres_e
->lock("UrnState::start");
189 sc
= storeClientListAdd(urlres_e
, this);
193 StoreIOBuffer tempBuffer
;
194 tempBuffer
.offset
= reqofs
;
195 tempBuffer
.length
= URN_REQBUF_SZ
;
196 tempBuffer
.data
= reqbuf
;
197 storeClientCopy(sc
, urlres_e
,
204 UrnState::fillChecklist(ACLFilledChecklist
&checklist
) const
206 checklist
.setRequest(request
.getRaw());
211 urnStart(HttpRequest
*r
, StoreEntry
*e
, const AccessLogEntryPointer
&ale
)
213 const auto anUrn
= new UrnState(ale
);
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
;
233 /* TODO: use the clientStream support for this */
235 urnHandleReply(void *data
, StoreIOBuffer result
)
237 UrnState
*urnState
= static_cast<UrnState
*>(data
);
238 StoreEntry
*e
= urnState
->entry
;
239 StoreEntry
*urlres_e
= urnState
->urlres_e
;
249 char *buf
= urnState
->reqbuf
;
250 StoreIOBuffer tempBuffer
;
252 debugs(52, 3, "urnHandleReply: Called with size=" << result
.length
<< ".");
254 if (EBIT_TEST(urlres_e
->flags
, ENTRY_ABORTED
) || result
.flags
.error
) {
259 if (!e
->isAccepting()) {
260 debugs(52, 3, "terminating due to bad " << *e
);
265 /* Update reqofs to point to where in the buffer we'd be */
266 urnState
->reqofs
+= result
.length
;
268 /* Handle reqofs being bigger than normal */
269 if (urnState
->reqofs
>= URN_REQBUF_SZ
) {
274 /* If we haven't received the entire object (urn), copy more */
275 if (urlres_e
->store_status
== STORE_PENDING
) {
276 Must(result
.length
> 0); // zero length ought to imply STORE_OK
277 tempBuffer
.offset
= urnState
->reqofs
;
278 tempBuffer
.length
= URN_REQBUF_SZ
- urnState
->reqofs
;
279 tempBuffer
.data
= urnState
->reqbuf
+ urnState
->reqofs
;
280 storeClientCopy(urnState
->sc
, urlres_e
,
287 /* we know its STORE_OK */
288 k
= headersEnd(buf
, urnState
->reqofs
);
291 debugs(52, DBG_IMPORTANT
, "urnHandleReply: didn't find end-of-headers for " << e
->url() );
297 // TODO: Check whether we should parse urlres_e reply, as before 528b2c61.
299 rep
->parseCharBuf(buf
, k
);
300 debugs(52, 3, "reply exists, code=" << rep
->sline
.status() << ".");
302 if (rep
->sline
.status() != Http::scOkay
) {
303 debugs(52, 3, "urnHandleReply: failed.");
304 err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, urnState
->request
.getRaw(), urnState
->ale
);
305 err
->url
= xstrdup(e
->url());
306 errorAppendEntry(e
, err
);
317 urls
= urnParseReply(s
, urnState
->request
->method
);
319 if (!urls
) { /* unknown URN error */
320 debugs(52, 3, "urnTranslateDone: unknown URN " << e
->url());
321 err
= new ErrorState(ERR_URN_RESOLVE
, Http::scNotFound
, urnState
->request
.getRaw(), urnState
->ale
);
322 err
->url
= xstrdup(e
->url());
323 errorAppendEntry(e
, err
);
328 for (i
= 0; urls
[i
].url
; ++i
)
331 debugs(53, 3, "urnFindMinRtt: Counted " << i
<< " URLs");
333 min_u
= urnFindMinRtt(urls
, urnState
->request
->method
, NULL
);
334 qsort(urls
, urlcnt
, sizeof(*urls
), url_entry_sort
);
337 SBuf
*mb
= &body
; // diff reduction hack; TODO: Remove
338 mb
->appendf( "<TITLE>Select URL for %s</TITLE>\n"
339 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
340 "<H2>Select URL for %s</H2>\n"
341 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e
->url(), e
->url());
343 for (i
= 0; i
< urlcnt
; ++i
) {
345 debugs(52, 3, "URL {" << u
->url
<< "}");
347 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u
->url
, u
->url
);
351 "<TD align=\"right\">%4d <it>ms</it></TD>", u
->rtt
);
353 mb
->appendf("<TD align=\"right\">Unknown</TD>");
355 mb
->appendf("<TD>%s</TD></TR>\n", u
->flags
.cached
? " [cached]" : " ");
360 "<HR noshade size=\"1px\">\n"
362 "Generated by %s@%s\n"
364 APP_FULLNAME
, getMyHostname());
366 rep
->setHeaders(Http::scFound
, NULL
, "text/html", mb
->length(), 0, squid_curtime
);
369 rep
->header
.putStr(Http::HdrType::LOCATION
, min_u
->url
);
373 e
->replaceHttpReply(rep
);
376 for (i
= 0; i
< urlcnt
; ++i
) {
377 safe_free(urls
[i
].url
);
378 safe_free(urls
[i
].host
);
387 urnParseReply(const char *inbuf
, const HttpRequestMethod
& m
)
389 char *buf
= xstrdup(inbuf
);
395 debugs(52, 3, "urnParseReply");
396 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
398 for (token
= strtok(buf
, crlf
); token
; token
= strtok(NULL
, crlf
)) {
399 debugs(52, 3, "urnParseReply: got '" << token
<< "'");
404 list
= (url_entry
*)xcalloc(n
+ 1, sizeof(*list
));
405 memcpy(list
, old
, i
* sizeof(*list
));
410 if (!uri
.parse(m
, SBuf(token
)) || !*uri
.host())
414 list
[i
].rtt
= netdbHostRtt(uri
.host());
416 if (0 == list
[i
].rtt
) {
417 debugs(52, 3, "Pinging " << uri
.host());
418 netdbPingSite(uri
.host());
424 list
[i
].url
= xstrdup(uri
.absolute().c_str());
425 list
[i
].host
= xstrdup(uri
.host());
426 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
428 list
[i
].flags
.cached
= storeGetPublic(list
[i
].url
, m
) ? 1 : 0;
432 debugs(52, 3, "urnParseReply: Found " << i
<< " URLs");