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 
30 class UrnState : public StoreClient
31 {
33 
34 public:
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 
52 private:
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 
60 typedef struct {
61  char *url;
62  char *host;
63  int rtt;
64 
65  struct {
66  int cached;
67  } flags;
68 } url_entry;
69 
71 static url_entry *urnParseReply(const SBuf &, const HttpRequestMethod &);
72 static const char *const crlf = "\r\n";
73 
75 
77 {
79  if (urlres_e) {
80  if (sc)
81  storeUnregister(sc, urlres_e, this);
82  urlres_e->unlock("~UrnState+res");
83  }
84 
85  if (entry)
86  entry->unlock("~UrnState+prime");
87 
89  });
90 }
91 
92 static url_entry *
93 urnFindMinRtt(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 
138 void
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));
146  safe_free(urlres);
147  urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
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);
153  errorAppendEntry(entry, err);
154  return;
155  }
156 
157  urlres = xstrdup(local_urlres);
159 }
160 
161 void
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))) {
178  sc = storeClientListAdd(urlres_e, this);
180  if (urlEntry) {
181  urlEntry->abandon(__func__);
182  urlEntry = nullptr;
183  }
184  } else {
185  urlres_e = urlEntry;
186  urlres_e->lock(__func__);
187  sc = storeClientListAdd(urlres_e, this);
188  }
189 
193  this);
194 }
195 
196 void
198 {
199  checklist.setRequest(request.getRaw());
200  checklist.al = ale;
201 }
202 
203 void
205 {
206  const auto anUrn = new UrnState(ale);
207  anUrn->start (r, e);
208 }
209 
210 static int
211 url_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 */
227 static void
228 urnHandleReply(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  // XXX: Missing reply freshness checks (e.g., calling refreshCheckHTTP()).
286 
287  urls = urnParseReply(urnState->parsingBuffer.toSBuf(), urnState->request->method);
288 
289  if (!urls) { /* unknown URN error */
290  debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
291  err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
292  err->url = xstrdup(e->url());
293  errorAppendEntry(e, err);
294  delete urnState;
295  return;
296  }
297 
298  for (i = 0; urls[i].url; ++i)
299  ++urlcnt;
300 
301  debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
302 
303  min_u = urnFindMinRtt(urls, urnState->request->method, nullptr);
304  qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
305  e->buffer();
306  SBuf body;
307  SBuf *mb = &body; // diff reduction hack; TODO: Remove
308  mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
309  "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
310  "<H2>Select URL for %s</H2>\n"
311  "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
312 
313  for (i = 0; i < urlcnt; ++i) {
314  u = &urls[i];
315  debugs(52, 3, "URL {" << u->url << "}");
316  mb->appendf(
317  "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
318 
319  if (urls[i].rtt > 0)
320  mb->appendf(
321  "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
322  else
323  mb->appendf("<TD align=\"right\">Unknown</TD>");
324 
325  mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
326  }
327 
328  mb->appendf(
329  "</TABLE>"
330  "<HR noshade size=\"1px\">\n"
331  "<ADDRESS>\n"
332  "Generated by %s@%s\n"
333  "</ADDRESS>\n",
335  const auto rep = new HttpReply;
336  rep->setHeaders(Http::scFound, nullptr, "text/html", mb->length(), 0, squid_curtime);
337 
338  if (min_u) {
339  rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
340  }
341 
342  rep->body.set(body);
343  e->replaceHttpReply(rep);
344  e->complete();
345 
346  for (i = 0; i < urlcnt; ++i) {
347  safe_free(urls[i].url);
348  safe_free(urls[i].host);
349  }
350 
351  safe_free(urls);
352 
353  delete urnState;
354 }
355 
356 static url_entry *
357 urnParseReply(const SBuf &inBuf, const HttpRequestMethod &m)
358 {
359  char *token;
360  url_entry *list;
361  url_entry *old;
362  int n = 32;
363  int i = 0;
364  debugs(52, 3, "urnParseReply");
365  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
366 
367  // XXX: Switch to tokenizer-based parsing.
368  const auto allocated = SBufToCstring(inBuf);
369 
370  auto buf = allocated;
371  while (xisspace(*buf))
372  ++buf;
373 
374  for (token = strtok(buf, crlf); token; token = strtok(nullptr, crlf)) {
375  debugs(52, 3, "urnParseReply: got '" << token << "'");
376 
377  if (i == n) {
378  old = list;
379  n <<= 2;
380  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
381  memcpy(list, old, i * sizeof(*list));
382  safe_free(old);
383  }
384 
385  AnyP::Uri uri;
386  if (!uri.parse(m, SBuf(token)) || !*uri.host())
387  continue;
388 
389 #if USE_ICMP
390  list[i].rtt = netdbHostRtt(uri.host());
391 
392  if (0 == list[i].rtt) {
393  debugs(52, 3, "Pinging " << uri.host());
394  netdbPingSite(uri.host());
395  }
396 #else
397  list[i].rtt = 0;
398 #endif
399 
400  list[i].url = xstrdup(uri.absolute().c_str());
401  list[i].host = xstrdup(uri.host());
402  // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
403  // ones.
404  list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
405  ++i;
406  }
407 
408  debugs(52, 3, "urnParseReply: Found " << i << " URLs");
409  xfree(allocated);
410  return list;
411 }
412 
struct url_entry::@134 flags
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
struct StoreIOBuffer::@130 flags
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
Definition: Uri.h:31
void start(HttpRequest *, StoreEntry *)
Definition: urn.cc:162
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:62
HttpHeader header
Definition: Message.h:74
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:738
const char * url() const
Definition: store.cc:1566
MemObject & mem()
Definition: Store.h:47
void lock(const char *context)
Definition: store.cc:445
@ ENTRY_ABORTED
Definition: enums.h:110
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
bool parse(const HttpRequestMethod &, const SBuf &url)
Definition: Uri.cc:295
static url_entry * urnFindMinRtt(url_entry *urls, const HttpRequestMethod &, int *rtt_ret)
Definition: urn.cc:93
char * urlres
Definition: urn.cc:57
Definition: SBuf.h:93
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:756
#define xstrdup
StoreIOBuffer & positionAt(const int64_t newOffset)
convenience method for changing the offset of a being-configured buffer
Definition: StoreIOBuffer.h:47
int cached
Definition: urn.cc:66
C * getRaw() const
Definition: RefCount.h:89
uint16_t flags
Definition: Store.h:231
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool isAccepting() const
Definition: store.cc:1988
int netdbHostRtt(const char *host)
Definition: net_db.cc:944
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1705
AccessLogEntry::Pointer ale
details of the requesting transaction
Definition: urn.cc:47
~UrnState() override
Definition: urn.cc:76
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:338
HttpRequest::Pointer request
Definition: urn.cc:45
HttpRequest::Pointer urlres_r
Definition: urn.cc:46
a storeGetPublic*() caller
Definition: StoreClient.h:40
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
static int url_entry_sort(const void *A, const void *B)
Definition: urn.cc:211
int rtt
Definition: urn.cc:63
const HttpReply & baseReply() const
Definition: MemObject.h:60
Definition: urn.cc:30
static uint32 A
Definition: md4.c:43
store_client * sc
Definition: urn.cc:43
void appended(const char *, size_t)
remember the new bytes received into the previously provided space()
static url_entry * urnParseReply(const SBuf &, const HttpRequestMethod &)
Definition: urn.cc:357
unsigned error
Definition: StoreIOBuffer.h:55
#define EBIT_TEST(flag, bit)
Definition: defines.h:67
int unlock(const char *context)
Definition: store.cc:469
#define safe_free(x)
Definition: xalloc.h:73
const char * visible_appname_string
MasterXaction::Pointer masterXaction
the master transaction this request belongs to. Never nil.
Definition: HttpRequest.h:238
#define assert(EX)
Definition: assert.h:17
class AccessLogEntry::CacheDetails cache
UrnState(const AccessLogEntry::Pointer &anAle)
Definition: urn.cc:35
char * url
Definition: urn.cc:61
void buffer() override
Definition: store.cc:1601
const char * c_str()
Definition: SBuf.cc:516
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:325
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:419
void setUriResFromRequest(HttpRequest *)
Definition: urn.cc:139
time_t squid_curtime
Definition: stub_libtime.cc:20
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:759
#define xfree
StoreEntry * entry
Definition: urn.cc:42
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:66
static const char *const crlf
Definition: urn.cc:72
void complete()
Definition: store.cc:1031
void urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
Definition: urn.cc:204
@ scFound
Definition: StatusCode.h:39
SBuf & absolute() const
Definition: Uri.cc:711
HttpRequestMethod method
Definition: HttpRequest.h:114
StoreIOBuffer makeInitialSpace()
Definition: ParsingBuffer.h:84
@ scNotFound
Definition: StatusCode.h:49
Store::ParsingBuffer parsingBuffer
for receiving a URN resolver reply body from Store and interpreting it
Definition: urn.cc:50
char * url
Definition: errorpage.h:178
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:995
void setRequest(HttpRequest *)
configure client request-related fields for the first time
const char * getMyHostname(void)
Definition: tools.cc:467
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:528
Definition: urn.cc:60
#define DBG_IMPORTANT
Definition: Stream.h:38
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:504
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
Definition: urn.cc:197
char * host
Definition: urn.cc:62
SBuf toSBuf() const
export content() into SBuf, avoiding content copying when possible
LogTags * loggingTags() const override
Definition: urn.cc:54
bool atEof() const
Definition: StoreClient.h:106
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:170
#define xisspace(x)
Definition: xis.h:15
#define SWALLOW_EXCEPTIONS(code)
Definition: TextException.h:79
static uint32 B
Definition: md4.c:43
@ scOkay
Definition: StatusCode.h:27
void(void *, StoreIOBuffer) STCB
Definition: StoreClient.h:32
SBuf & appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Definition: SBuf.cc:229
@ ERR_URN_RESOLVE
Definition: forward.h:36
@ METHOD_GET
Definition: MethodType.h:25
StoreEntry * urlres_e
Definition: urn.cc:44
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
size_t contentSize() const
the total number of append()ed bytes that were not consume()d
void host(const char *src)
Definition: Uri.cc:123
StoreIOBuffer space()
void netdbPingSite(const char *hostname)
Definition: net_db.cc:811
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
static STCB urnHandleReply
Definition: urn.cc:70
#define SQUIDSBUFPH
Definition: SBuf.h:31
store_client * storeClientListAdd(StoreEntry *e, void *data)

 

Introduction

Documentation

Support

Miscellaneous