urn.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
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.
7 */
8
9/* DEBUG: section 52 URN Parsing */
10
11#include "squid.h"
12#include "AccessLogEntry.h"
13#include "acl/FilledChecklist.h"
14#include "base/TextException.h"
15#include "cbdata.h"
16#include "errorpage.h"
17#include "FwdState.h"
18#include "globals.h"
19#include "HttpReply.h"
20#include "HttpRequest.h"
21#include "icmp/net_db.h"
22#include "MemBuf.h"
23#include "mime_header.h"
24#include "RequestFlags.h"
25#include "Store.h"
26#include "StoreClient.h"
27#include "tools.h"
28#include "urn.h"
29
30class UrnState : public StoreClient
31{
33
34public:
35 explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
36
37 void start (HttpRequest *, StoreEntry *);
39
40 ~UrnState() override;
41
42 StoreEntry *entry = nullptr;
43 store_client *sc = nullptr;
44 StoreEntry *urlres_e = nullptr;
48
51
52private:
53 /* StoreClient API */
54 LogTags *loggingTags() const override { return ale ? &ale->cache.code : nullptr; }
55 void fillChecklist(ACLFilledChecklist &) const override;
56
57 char *urlres = nullptr;
58};
59
60typedef struct {
61 char *url;
62 char *host;
63 int rtt;
64
65 struct {
66 int cached;
67 } flags;
68} url_entry;
69
71static url_entry *urnParseReply(const SBuf &, const HttpRequestMethod &);
72static const char *const crlf = "\r\n";
73
75
77{
79 if (urlres_e) {
80 if (sc)
82 urlres_e->unlock("~UrnState+res");
83 }
84
85 if (entry)
86 entry->unlock("~UrnState+prime");
87
89 });
90}
91
92static url_entry *
93urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
94{
95 int min_rtt = 0;
96 url_entry *u = nullptr;
97 url_entry *min_u = nullptr;
98 int i;
99 int urlcnt = 0;
100 debugs(52, 3, "urnFindMinRtt");
101 assert(urls != nullptr);
102
103 for (i = 0; nullptr != urls[i].url; ++i)
104 ++urlcnt;
105
106 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
107
108 if (1 == urlcnt) {
109 debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
110 return urls;
111 }
112
113 for (i = 0; i < urlcnt; ++i) {
114 u = &urls[i];
115 debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
116
117 if (u->rtt == 0)
118 continue;
119
120 if (u->rtt > min_rtt && min_rtt != 0)
121 continue;
122
123 min_rtt = u->rtt;
124
125 min_u = u;
126 }
127
128 if (rtt_ret)
129 *rtt_ret = min_rtt;
130
131 debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
132 (min_u ? min_u->url : "NONE") << "' RTT " <<
133 min_rtt );
134
135 return min_u;
136}
137
138void
140{
141 const auto &query = r->url.absolute();
142 const auto host = r->url.host();
143 // TODO: use class AnyP::Uri instead of generating a string and re-parsing
144 LOCAL_ARRAY(char, local_urlres, 4096);
145 snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH, host, SQUIDSBUFPRINT(query));
148
149 if (!urlres_r) {
150 debugs(52, 3, "Bad uri-res URL " << local_urlres);
151 const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
152 err->url = xstrdup(local_urlres);
154 return;
155 }
156
157 urlres = xstrdup(local_urlres);
159}
160
161void
163{
164 debugs(52, 3, "urnStart: '" << e->url() << "'" );
165 entry = e;
166 request = r;
167
168 entry->lock("UrnState::start");
170
171 if (urlres_r == nullptr)
172 return;
173
174 auto urlEntry = storeGetPublic(urlres, Http::METHOD_GET);
175
176 if (!urlEntry || (urlEntry->hittingRequiresCollapsing() && !startCollapsingOn(*urlEntry, false))) {
180 if (urlEntry) {
181 urlEntry->abandon(__FUNCTION__);
182 urlEntry = nullptr;
183 }
184 } else {
185 urlres_e = urlEntry;
186 urlres_e->lock("UrnState::start");
188 }
189
193 this);
194}
195
196void
198{
199 checklist.setRequest(request.getRaw());
200 checklist.al = ale;
201}
202
203void
205{
206 const auto anUrn = new UrnState(ale);
207 anUrn->start (r, e);
208}
209
210static int
211url_entry_sort(const void *A, const void *B)
212{
213 const url_entry *u1 = (const url_entry *)A;
214 const url_entry *u2 = (const url_entry *)B;
215
216 if (u2->rtt == u1->rtt)
217 return 0;
218 else if (0 == u1->rtt)
219 return 1;
220 else if (0 == u2->rtt)
221 return -1;
222 else
223 return u1->rtt - u2->rtt;
224}
225
226/* TODO: use the clientStream support for this */
227static void
228urnHandleReply(void *data, StoreIOBuffer result)
229{
230 UrnState *urnState = static_cast<UrnState *>(data);
231 StoreEntry *e = urnState->entry;
232 StoreEntry *urlres_e = urnState->urlres_e;
233 url_entry *urls;
234 url_entry *u;
235 url_entry *min_u;
236 ErrorState *err;
237 int i;
238 int urlcnt = 0;
239
240 debugs(52, 3, result << " with " << *e);
241
242 if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
243 delete urnState;
244 return;
245 }
246
247 if (!e->isAccepting()) {
248 debugs(52, 3, "terminating due to bad " << *e);
249 delete urnState;
250 return;
251 }
252
253 urnState->parsingBuffer.appended(result.data, result.length);
254
255 /* If we haven't received the entire object (urn), copy more */
256 if (!urnState->sc->atEof()) {
257 const auto bufferedBytes = urnState->parsingBuffer.contentSize();
258 const auto remainingSpace = urnState->parsingBuffer.space().positionAt(bufferedBytes);
259
260 if (!remainingSpace.length) {
261 debugs(52, 3, "ran out of buffer space after " << bufferedBytes << " bytes");
262 // TODO: Here and in other error cases, send ERR_URN_RESOLVE to client.
263 delete urnState;
264 return;
265 }
266
267 storeClientCopy(urnState->sc, urlres_e,
268 remainingSpace,
270 urnState);
271 return;
272 }
273
274 const auto &peerReply = urlres_e->mem().baseReply();
275 debugs(52, 3, "got reply, code=" << peerReply.sline.status());
276 if (peerReply.sline.status() != Http::scOkay) {
277 debugs(52, 3, "urnHandleReply: failed.");
278 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
279 err->url = xstrdup(e->url());
280 errorAppendEntry(e, err);
281 delete urnState;
282 return;
283 }
284
285 urls = urnParseReply(urnState->parsingBuffer.toSBuf(), urnState->request->method);
286
287 if (!urls) { /* unknown URN error */
288 debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
289 err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
290 err->url = xstrdup(e->url());
291 errorAppendEntry(e, err);
292 delete urnState;
293 return;
294 }
295
296 for (i = 0; urls[i].url; ++i)
297 ++urlcnt;
298
299 debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
300
301 min_u = urnFindMinRtt(urls, urnState->request->method, nullptr);
302 qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
303 e->buffer();
304 SBuf body;
305 SBuf *mb = &body; // diff reduction hack; TODO: Remove
306 mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
307 "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
308 "<H2>Select URL for %s</H2>\n"
309 "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
310
311 for (i = 0; i < urlcnt; ++i) {
312 u = &urls[i];
313 debugs(52, 3, "URL {" << u->url << "}");
314 mb->appendf(
315 "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
316
317 if (urls[i].rtt > 0)
318 mb->appendf(
319 "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
320 else
321 mb->appendf("<TD align=\"right\">Unknown</TD>");
322
323 mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
324 }
325
326 mb->appendf(
327 "</TABLE>"
328 "<HR noshade size=\"1px\">\n"
329 "<ADDRESS>\n"
330 "Generated by %s@%s\n"
331 "</ADDRESS>\n",
333 const auto rep = new HttpReply;
334 rep->setHeaders(Http::scFound, nullptr, "text/html", mb->length(), 0, squid_curtime);
335
336 if (min_u) {
337 rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
338 }
339
340 rep->body.set(body);
341 e->replaceHttpReply(rep);
342 e->complete();
343
344 for (i = 0; i < urlcnt; ++i) {
345 safe_free(urls[i].url);
346 safe_free(urls[i].host);
347 }
348
349 safe_free(urls);
350
351 delete urnState;
352}
353
354static url_entry *
355urnParseReply(const SBuf &inBuf, const HttpRequestMethod &m)
356{
357 char *token;
358 url_entry *list;
359 url_entry *old;
360 int n = 32;
361 int i = 0;
362 debugs(52, 3, "urnParseReply");
363 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
364
365 // XXX: Switch to tokenizer-based parsing.
366 const auto allocated = SBufToCstring(inBuf);
367
368 auto buf = allocated;
369 while (xisspace(*buf))
370 ++buf;
371
372 for (token = strtok(buf, crlf); token; token = strtok(nullptr, crlf)) {
373 debugs(52, 3, "urnParseReply: got '" << token << "'");
374
375 if (i == n) {
376 old = list;
377 n <<= 2;
378 list = (url_entry *)xcalloc(n + 1, sizeof(*list));
379 memcpy(list, old, i * sizeof(*list));
380 safe_free(old);
381 }
382
383 AnyP::Uri uri;
384 if (!uri.parse(m, SBuf(token)) || !*uri.host())
385 continue;
386
387#if USE_ICMP
388 list[i].rtt = netdbHostRtt(uri.host());
389
390 if (0 == list[i].rtt) {
391 debugs(52, 3, "Pinging " << uri.host());
392 netdbPingSite(uri.host());
393 }
394#else
395 list[i].rtt = 0;
396#endif
397
398 list[i].url = xstrdup(uri.absolute().c_str());
399 list[i].host = xstrdup(uri.host());
400 // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
401 // ones.
402 list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
403 ++i;
404 }
405
406 debugs(52, 3, "urnParseReply: Found " << i << " URLs");
407 xfree(allocated);
408 return list;
409}
410
time_t squid_curtime
Definition: stub_libtime.cc:20
#define SQUIDSBUFPH
Definition: SBuf.h:31
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:752
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void(void *, StoreIOBuffer) STCB
Definition: StoreClient.h:32
#define SWALLOW_EXCEPTIONS(code)
Definition: TextException.h:79
#define assert(EX)
Definition: assert.h:17
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:320
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
void setRequest(HttpRequest *)
configure client request-related fields for the first time
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
class AccessLogEntry::CacheDetails cache
Definition: Uri.h:31
SBuf & absolute() const
Definition: Uri.cc:668
void host(const char *src)
Definition: Uri.cc:100
bool parse(const HttpRequestMethod &, const SBuf &url)
Definition: Uri.cc:252
char * url
Definition: errorpage.h:178
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:341
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:996
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
MasterXaction::Pointer masterXaction
the master transaction this request belongs to. Never nil.
Definition: HttpRequest.h:238
HttpRequestMethod method
Definition: HttpRequest.h:114
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
HttpHeader header
Definition: Message.h:74
const HttpReply & baseReply() const
Definition: MemObject.h:60
C * getRaw() const
Definition: RefCount.h:89
Definition: SBuf.h:94
const char * c_str()
Definition: SBuf.cc:516
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:415
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:229
a storeGetPublic*() caller
Definition: StoreClient.h:40
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:66
bool isAccepting() const
Definition: store.cc:1975
uint16_t flags
Definition: Store.h:232
MemObject & mem()
Definition: Store.h:51
int unlock(const char *context)
Definition: store.cc:455
const char * url() const
Definition: store.cc:1552
void complete()
Definition: store.cc:1017
void lock(const char *context)
Definition: store.cc:431
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1691
void buffer() override
Definition: store.cc:1587
unsigned error
Definition: StoreIOBuffer.h:55
struct StoreIOBuffer::@141 flags
StoreIOBuffer & positionAt(const int64_t newOffset)
convenience method for changing the offset of a being-configured buffer
Definition: StoreIOBuffer.h:47
size_t contentSize() const
the total number of append()ed bytes that were not consume()d
StoreIOBuffer space()
SBuf toSBuf() const
export content() into SBuf, avoiding content copying when possible
StoreIOBuffer makeInitialSpace()
Definition: ParsingBuffer.h:84
void appended(const char *, size_t)
remember the new bytes received into the previously provided space()
Definition: urn.cc:31
AccessLogEntry::Pointer ale
details of the requesting transaction
Definition: urn.cc:47
void setUriResFromRequest(HttpRequest *)
Definition: urn.cc:139
HttpRequest::Pointer urlres_r
Definition: urn.cc:46
StoreEntry * urlres_e
Definition: urn.cc:44
void start(HttpRequest *, StoreEntry *)
Definition: urn.cc:162
Store::ParsingBuffer parsingBuffer
for receiving a URN resolver reply body from Store and interpreting it
Definition: urn.cc:50
char * urlres
Definition: urn.cc:57
store_client * sc
Definition: urn.cc:43
UrnState(const AccessLogEntry::Pointer &anAle)
Definition: urn.cc:35
~UrnState() override
Definition: urn.cc:76
StoreEntry * entry
Definition: urn.cc:42
LogTags * loggingTags() const override
Definition: urn.cc:54
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
Definition: urn.cc:197
HttpRequest::Pointer request
Definition: urn.cc:45
bool atEof() const
Definition: StoreClient.h:109
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
@ ENTRY_ABORTED
Definition: enums.h:115
@ ERR_URN_RESOLVE
Definition: forward.h:36
char const * visible_appname_string
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:717
static uint32 A
Definition: md4.c:43
static uint32 B
Definition: md4.c:43
@ scFound
Definition: StatusCode.h:38
@ scNotFound
Definition: StatusCode.h:48
@ scOkay
Definition: StatusCode.h:26
@ METHOD_GET
Definition: MethodType.h:25
#define xfree
#define xstrdup
void netdbPingSite(const char *hostname)
Definition: net_db.cc:827
int netdbHostRtt(const char *host)
Definition: net_db.cc:960
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:68
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:745
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:490
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
store_client * storeClientListAdd(StoreEntry *e, void *data)
Definition: urn.cc:60
int cached
Definition: urn.cc:66
struct url_entry::@145 flags
int rtt
Definition: urn.cc:63
char * host
Definition: urn.cc:62
char * url
Definition: urn.cc:61
const char * getMyHostname(void)
Definition: tools.cc:467
static const char *const crlf
Definition: urn.cc:72
void urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
Definition: urn.cc:204
static url_entry * urnParseReply(const SBuf &, const HttpRequestMethod &)
Definition: urn.cc:355
static int url_entry_sort(const void *A, const void *B)
Definition: urn.cc:211
static url_entry * urnFindMinRtt(url_entry *urls, const HttpRequestMethod &, int *rtt_ret)
Definition: urn.cc:93
static STCB urnHandleReply
Definition: urn.cc:70
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
#define safe_free(x)
Definition: xalloc.h:73
#define xisspace(x)
Definition: xis.h:15

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors