urn.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2022 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 #define URN_REQBUF_SZ 4096
31 
32 class UrnState : public StoreClient
33 {
35 
36 public:
37  explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
38 
39  void start (HttpRequest *, StoreEntry *);
41 
42  virtual ~UrnState();
43 
44  StoreEntry *entry = nullptr;
45  store_client *sc = nullptr;
46  StoreEntry *urlres_e = nullptr;
50 
51  char reqbuf[URN_REQBUF_SZ] = { '\0' };
52  int reqofs = 0;
53 
54 private:
55  /* StoreClient API */
56  virtual LogTags *loggingTags() const { return ale ? &ale->cache.code : nullptr; }
57  virtual void fillChecklist(ACLFilledChecklist &) const;
58 
59  char *urlres = nullptr;
60 };
61 
62 typedef struct {
63  char *url;
64  char *host;
65  int rtt;
66 
67  struct {
68  int cached;
69  } flags;
70 } url_entry;
71 
73 static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
74 static const char *const crlf = "\r\n";
75 
77 
79 {
81  if (urlres_e) {
82  if (sc)
83  storeUnregister(sc, urlres_e, this);
84  urlres_e->unlock("~UrnState+res");
85  }
86 
87  if (entry)
88  entry->unlock("~UrnState+prime");
89 
91  });
92 }
93 
94 static url_entry *
95 urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
96 {
97  int min_rtt = 0;
98  url_entry *u = NULL;
99  url_entry *min_u = NULL;
100  int i;
101  int urlcnt = 0;
102  debugs(52, 3, "urnFindMinRtt");
103  assert(urls != NULL);
104 
105  for (i = 0; NULL != urls[i].url; ++i)
106  ++urlcnt;
107 
108  debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
109 
110  if (1 == urlcnt) {
111  debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
112  return urls;
113  }
114 
115  for (i = 0; i < urlcnt; ++i) {
116  u = &urls[i];
117  debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
118 
119  if (u->rtt == 0)
120  continue;
121 
122  if (u->rtt > min_rtt && min_rtt != 0)
123  continue;
124 
125  min_rtt = u->rtt;
126 
127  min_u = u;
128  }
129 
130  if (rtt_ret)
131  *rtt_ret = min_rtt;
132 
133  debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
134  (min_u ? min_u->url : "NONE") << "' RTT " <<
135  min_rtt );
136 
137  return min_u;
138 }
139 
140 void
142 {
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));
148  safe_free(urlres);
149  urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
150 
151  if (!urlres_r) {
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);
156  return;
157  }
158 
159  urlres = xstrdup(local_urlres);
161 }
162 
163 void
165 {
166  debugs(52, 3, "urnStart: '" << e->url() << "'" );
167  entry = e;
168  request = r;
169 
170  entry->lock("UrnState::start");
172 
173  if (urlres_r == NULL)
174  return;
175 
176  auto urlEntry = storeGetPublic(urlres, Http::METHOD_GET);
177 
178  if (!urlEntry || (urlEntry->hittingRequiresCollapsing() && !startCollapsingOn(*urlEntry, false))) {
180  sc = storeClientListAdd(urlres_e, this);
182  if (urlEntry) {
183  urlEntry->abandon(__FUNCTION__);
184  urlEntry = nullptr;
185  }
186  } else {
187  urlres_e = urlEntry;
188  urlres_e->lock("UrnState::start");
189  sc = storeClientListAdd(urlres_e, this);
190  }
191 
192  reqofs = 0;
193  StoreIOBuffer tempBuffer;
194  tempBuffer.offset = reqofs;
195  tempBuffer.length = URN_REQBUF_SZ;
196  tempBuffer.data = reqbuf;
198  tempBuffer,
200  this);
201 }
202 
203 void
205 {
206  checklist.setRequest(request.getRaw());
207  checklist.al = ale;
208 }
209 
210 void
212 {
213  const auto anUrn = new UrnState(ale);
214  anUrn->start (r, e);
215 }
216 
217 static int
218 url_entry_sort(const void *A, const void *B)
219 {
220  const url_entry *u1 = (const url_entry *)A;
221  const url_entry *u2 = (const url_entry *)B;
222 
223  if (u2->rtt == u1->rtt)
224  return 0;
225  else if (0 == u1->rtt)
226  return 1;
227  else if (0 == u2->rtt)
228  return -1;
229  else
230  return u1->rtt - u2->rtt;
231 }
232 
233 /* TODO: use the clientStream support for this */
234 static void
235 urnHandleReply(void *data, StoreIOBuffer result)
236 {
237  UrnState *urnState = static_cast<UrnState *>(data);
238  StoreEntry *e = urnState->entry;
239  StoreEntry *urlres_e = urnState->urlres_e;
240  char *s = NULL;
241  size_t k;
242  HttpReply *rep;
243  url_entry *urls;
244  url_entry *u;
245  url_entry *min_u;
246  ErrorState *err;
247  int i;
248  int urlcnt = 0;
249  char *buf = urnState->reqbuf;
250  StoreIOBuffer tempBuffer;
251 
252  debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
253 
254  if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
255  delete urnState;
256  return;
257  }
258 
259  if (!e->isAccepting()) {
260  debugs(52, 3, "terminating due to bad " << *e);
261  delete urnState;
262  return;
263  }
264 
265  /* Update reqofs to point to where in the buffer we'd be */
266  urnState->reqofs += result.length;
267 
268  /* Handle reqofs being bigger than normal */
269  if (urnState->reqofs >= URN_REQBUF_SZ) {
270  delete urnState;
271  return;
272  }
273 
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,
281  tempBuffer,
283  urnState);
284  return;
285  }
286 
287  /* we know its STORE_OK */
288  k = headersEnd(buf, urnState->reqofs);
289 
290  if (0 == k) {
291  debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url() );
292  delete urnState;
293  return;
294  }
295 
296  s = buf + k;
297  // TODO: Check whether we should parse urlres_e reply, as before 528b2c61.
298  rep = new HttpReply;
299  rep->parseCharBuf(buf, k);
300  debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
301 
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);
307  delete rep;
308  delete urnState;
309  return;
310  }
311 
312  delete rep;
313 
314  while (xisspace(*s))
315  ++s;
316 
317  urls = urnParseReply(s, urnState->request->method);
318 
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);
324  delete urnState;
325  return;
326  }
327 
328  for (i = 0; urls[i].url; ++i)
329  ++urlcnt;
330 
331  debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
332 
333  min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
334  qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
335  e->buffer();
336  SBuf body;
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());
342 
343  for (i = 0; i < urlcnt; ++i) {
344  u = &urls[i];
345  debugs(52, 3, "URL {" << u->url << "}");
346  mb->appendf(
347  "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
348 
349  if (urls[i].rtt > 0)
350  mb->appendf(
351  "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
352  else
353  mb->appendf("<TD align=\"right\">Unknown</TD>");
354 
355  mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
356  }
357 
358  mb->appendf(
359  "</TABLE>"
360  "<HR noshade size=\"1px\">\n"
361  "<ADDRESS>\n"
362  "Generated by %s@%s\n"
363  "</ADDRESS>\n",
365  rep = new HttpReply;
366  rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
367 
368  if (min_u) {
370  }
371 
372  rep->body.set(body);
373  e->replaceHttpReply(rep);
374  e->complete();
375 
376  for (i = 0; i < urlcnt; ++i) {
377  safe_free(urls[i].url);
378  safe_free(urls[i].host);
379  }
380 
381  safe_free(urls);
382 
383  delete urnState;
384 }
385 
386 static url_entry *
387 urnParseReply(const char *inbuf, const HttpRequestMethod& m)
388 {
389  char *buf = xstrdup(inbuf);
390  char *token;
391  url_entry *list;
392  url_entry *old;
393  int n = 32;
394  int i = 0;
395  debugs(52, 3, "urnParseReply");
396  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
397 
398  for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
399  debugs(52, 3, "urnParseReply: got '" << token << "'");
400 
401  if (i == n) {
402  old = list;
403  n <<= 2;
404  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
405  memcpy(list, old, i * sizeof(*list));
406  safe_free(old);
407  }
408 
409  AnyP::Uri uri;
410  if (!uri.parse(m, SBuf(token)) || !*uri.host())
411  continue;
412 
413 #if USE_ICMP
414  list[i].rtt = netdbHostRtt(uri.host());
415 
416  if (0 == list[i].rtt) {
417  debugs(52, 3, "Pinging " << uri.host());
418  netdbPingSite(uri.host());
419  }
420 #else
421  list[i].rtt = 0;
422 #endif
423 
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
427  // ones.
428  list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
429  ++i;
430  }
431 
432  debugs(52, 3, "urnParseReply: Found " << i << " URLs");
433  xfree(buf);
434  return list;
435 }
436 
int reqofs
Definition: urn.cc:52
#define URN_REQBUF_SZ
Definition: urn.cc:30
virtual void buffer()
Definition: store.cc:1579
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
char reqbuf[URN_REQBUF_SZ]
Definition: urn.cc:51
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
Definition: Uri.h:31
void start(HttpRequest *, StoreEntry *)
Definition: urn.cc:164
struct url_entry::@151 flags
#define LOCAL_ARRAY(type, name, size)
Definition: squid.h:75
HttpHeader header
Definition: Message.h:75
virtual LogTags * loggingTags() const
Definition: urn.cc:56
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:719
const char * url() const
Definition: store.cc:1544
void lock(const char *context)
Definition: store.cc:420
#define CBDATA_CLASS(type)
Definition: cbdata.h:302
int64_t offset
Definition: StoreIOBuffer.h:55
bool parse(const HttpRequestMethod &, const SBuf &url)
Definition: Uri.cc:254
static url_entry * urnFindMinRtt(url_entry *urls, const HttpRequestMethod &, int *rtt_ret)
Definition: urn.cc:95
char * urlres
Definition: urn.cc:59
Definition: SBuf.h:87
#define xstrdup
int cached
Definition: urn.cc:68
C * getRaw() const
Definition: RefCount.h:80
uint16_t flags
Definition: Store.h:230
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
bool isAccepting() const
Definition: store.cc:1951
Http::StatusLine sline
Definition: HttpReply.h:56
int netdbHostRtt(const char *host)
Definition: net_db.cc:1055
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1683
AccessLogEntry::Pointer ale
details of the requesting transaction
Definition: urn.cc:49
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:361
bool parseCharBuf(const char *buf, ssize_t end)
Definition: Message.cc:140
HttpRequest::Pointer request
Definition: urn.cc:47
struct StoreIOBuffer::@146 flags
HttpRequest::Pointer urlres_r
Definition: urn.cc:48
@ ENTRY_ABORTED
Definition: enums.h:115
HttpBody body
Definition: HttpReply.h:58
a storeGetPublic*() caller
Definition: StoreClient.h:26
void set(const SBuf &newContent)
Definition: HttpBody.h:26
#define NULL
Definition: types.h:166
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
static int url_entry_sort(const void *A, const void *B)
Definition: urn.cc:218
int rtt
Definition: urn.cc:65
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
Definition: urn.cc:33
static uint32 A
Definition: md4.c:43
store_client * sc
Definition: urn.cc:45
static url_entry * urnParseReply(const char *inbuf, const HttpRequestMethod &)
Definition: urn.cc:387
unsigned error
Definition: StoreIOBuffer.h:52
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
int unlock(const char *context)
Definition: store.cc:444
#define safe_free(x)
Definition: xalloc.h:73
virtual void fillChecklist(ACLFilledChecklist &) const
configure the given checklist (to reflect the current transaction state)
Definition: urn.cc:204
store_status_t store_status
Definition: Store.h:242
MasterXaction::Pointer masterXaction
the master transaction this request belongs to. Never nil.
Definition: HttpRequest.h:238
#define assert(EX)
Definition: assert.h:19
class AccessLogEntry::CacheDetails cache
UrnState(const AccessLogEntry::Pointer &anAle)
Definition: urn.cc:37
char * url
Definition: urn.cc:63
const char * c_str()
Definition: SBuf.cc:516
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:408
void setUriResFromRequest(HttpRequest *)
Definition: urn.cc:141
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:734
#define xfree
StoreEntry * entry
Definition: urn.cc:44
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:65
static const char *const crlf
Definition: urn.cc:74
void complete()
Definition: store.cc:1006
void urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
Definition: urn.cc:211
@ scFound
Definition: StatusCode.h:38
virtual ~UrnState()
Definition: urn.cc:78
SBuf & absolute() const
Definition: Uri.cc:584
HttpRequestMethod method
Definition: HttpRequest.h:114
@ scNotFound
Definition: StatusCode.h:48
char * url
Definition: errorpage.h:176
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1052
void setRequest(HttpRequest *)
configure client request-related fields for the first time
const char * getMyHostname(void)
Definition: tools.cc:464
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:529
#define Must(condition)
Definition: TextException.h:71
Definition: urn.cc:62
#define DBG_IMPORTANT
Definition: Stream.h:41
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:479
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:229
char * host
Definition: urn.cc:64
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:167
#define xisspace(x)
Definition: xis.h:17
#define SWALLOW_EXCEPTIONS(code)
Definition: TextException.h:75
static uint32 B
Definition: md4.c:43
@ scOkay
Definition: StatusCode.h:26
@ ERR_URN_RESOLVE
Definition: forward.h:36
@ METHOD_GET
Definition: MethodType.h:25
StoreEntry * urlres_e
Definition: urn.cc:46
#define APP_FULLNAME
Definition: version.h:25
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:17
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
void host(const char *src)
Definition: Uri.cc:99
void netdbPingSite(const char *hostname)
Definition: net_db.cc:907
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:196
void STCB(void *, StoreIOBuffer)
Definition: StoreClient.h:18
static STCB urnHandleReply
Definition: urn.cc:72
#define SQUIDSBUFPH
Definition: SBuf.h:31
store_client * storeClientListAdd(StoreEntry *e, void *data)
@ STORE_PENDING
Definition: enums.h:51

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors