urn.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 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 "cbdata.h"
15 #include "errorpage.h"
16 #include "FwdState.h"
17 #include "globals.h"
18 #include "HttpReply.h"
19 #include "HttpRequest.h"
20 #include "icmp/net_db.h"
21 #include "MemBuf.h"
22 #include "mime_header.h"
23 #include "RequestFlags.h"
24 #include "SquidTime.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 created (StoreEntry *newEntry);
40  void start (HttpRequest *, StoreEntry *);
42 
43  virtual ~UrnState();
44 
45  StoreEntry *entry = nullptr;
46  store_client *sc = nullptr;
47  StoreEntry *urlres_e = nullptr;
51 
52  char reqbuf[URN_REQBUF_SZ] = { '\0' };
53  int reqofs = 0;
54 
55 private:
56  /* StoreClient API */
57  virtual LogTags *loggingTags() { return ale ? &ale->cache.code : nullptr; }
58  virtual void fillChecklist(ACLFilledChecklist &) const;
59 
60  char *urlres = nullptr;
61 };
62 
63 typedef struct {
64  char *url;
65  char *host;
66  int rtt;
67 
68  struct {
69  int cached;
70  } flags;
71 } url_entry;
72 
74 static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
75 static const char *const crlf = "\r\n";
76 
78 
80 {
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 static url_entry *
94 urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
95 {
96  int min_rtt = 0;
97  url_entry *u = NULL;
98  url_entry *min_u = NULL;
99  int i;
100  int urlcnt = 0;
101  debugs(52, 3, "urnFindMinRtt");
102  assert(urls != NULL);
103 
104  for (i = 0; NULL != urls[i].url; ++i)
105  ++urlcnt;
106 
107  debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
108 
109  if (1 == urlcnt) {
110  debugs(52, 3, "urnFindMinRtt: Only one URL - return it!");
111  return urls;
112  }
113 
114  for (i = 0; i < urlcnt; ++i) {
115  u = &urls[i];
116  debugs(52, 3, "urnFindMinRtt: " << u->host << " rtt=" << u->rtt);
117 
118  if (u->rtt == 0)
119  continue;
120 
121  if (u->rtt > min_rtt && min_rtt != 0)
122  continue;
123 
124  min_rtt = u->rtt;
125 
126  min_u = u;
127  }
128 
129  if (rtt_ret)
130  *rtt_ret = min_rtt;
131 
132  debugs(52, DBG_IMPORTANT, "urnFindMinRtt: Returning '" <<
133  (min_u ? min_u->url : "NONE") << "' RTT " <<
134  min_rtt );
135 
136  return min_u;
137 }
138 
139 void
141 {
142  const auto &query = r->url.absolute();
143  const auto host = r->url.host();
144  // TODO: use class AnyP::Uri instead of generating a string and re-parsing
145  LOCAL_ARRAY(char, local_urlres, 4096);
146  snprintf(local_urlres, 4096, "http://%s/uri-res/N2L?" SQUIDSBUFPH, host, SQUIDSBUFPRINT(query));
147  safe_free(urlres);
148  urlres_r = HttpRequest::FromUrlXXX(local_urlres, r->masterXaction);
149 
150  if (!urlres_r) {
151  debugs(52, 3, "Bad uri-res URL " << local_urlres);
152  const auto err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, r, ale);
153  err->url = xstrdup(local_urlres);
154  errorAppendEntry(entry, err);
155  return;
156  }
157 
158  urlres = xstrdup(local_urlres);
160 }
161 
162 void
164 {
165  debugs(52, 3, "urnStart: '" << e->url() << "'" );
166  entry = e;
167  request = r;
168 
169  entry->lock("UrnState::start");
171 
172  if (urlres_r == NULL)
173  return;
174 
176 }
177 
178 void
180 {
181  checklist.setRequest(request.getRaw());
182  checklist.al = ale;
183 }
184 
185 void
187 {
188  if (!e || (e->hittingRequiresCollapsing() && !startCollapsingOn(*e, false))) {
190  sc = storeClientListAdd(urlres_e, this);
192  // TODO: StoreClients must either store/lock or abandon found entries.
193  //if (e)
194  // e->abandon();
195  } else {
196  urlres_e = e;
197  urlres_e->lock("UrnState::created");
198  sc = storeClientListAdd(urlres_e, this);
199  }
200 
201  reqofs = 0;
202  StoreIOBuffer tempBuffer;
203  tempBuffer.offset = reqofs;
204  tempBuffer.length = URN_REQBUF_SZ;
205  tempBuffer.data = reqbuf;
207  tempBuffer,
209  this);
210 }
211 
212 void
214 {
215  const auto anUrn = new UrnState(ale);
216  anUrn->start (r, e);
217 }
218 
219 static int
220 url_entry_sort(const void *A, const void *B)
221 {
222  const url_entry *u1 = (const url_entry *)A;
223  const url_entry *u2 = (const url_entry *)B;
224 
225  if (u2->rtt == u1->rtt)
226  return 0;
227  else if (0 == u1->rtt)
228  return 1;
229  else if (0 == u2->rtt)
230  return -1;
231  else
232  return u1->rtt - u2->rtt;
233 }
234 
235 /* TODO: use the clientStream support for this */
236 static void
238 {
239  UrnState *urnState = static_cast<UrnState *>(data);
240  StoreEntry *e = urnState->entry;
241  StoreEntry *urlres_e = urnState->urlres_e;
242  char *s = NULL;
243  size_t k;
244  HttpReply *rep;
245  url_entry *urls;
246  url_entry *u;
247  url_entry *min_u;
248  ErrorState *err;
249  int i;
250  int urlcnt = 0;
251  char *buf = urnState->reqbuf;
252  StoreIOBuffer tempBuffer;
253 
254  debugs(52, 3, "urnHandleReply: Called with size=" << result.length << ".");
255 
256  if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED) || result.flags.error) {
257  delete urnState;
258  return;
259  }
260 
261  /* Update reqofs to point to where in the buffer we'd be */
262  urnState->reqofs += result.length;
263 
264  /* Handle reqofs being bigger than normal */
265  if (urnState->reqofs >= URN_REQBUF_SZ) {
266  delete urnState;
267  return;
268  }
269 
270  /* If we haven't received the entire object (urn), copy more */
271  if (urlres_e->store_status == STORE_PENDING) {
272  Must(result.length > 0); // zero length ought to imply STORE_OK
273  tempBuffer.offset = urnState->reqofs;
274  tempBuffer.length = URN_REQBUF_SZ - urnState->reqofs;
275  tempBuffer.data = urnState->reqbuf + urnState->reqofs;
276  storeClientCopy(urnState->sc, urlres_e,
277  tempBuffer,
279  urnState);
280  return;
281  }
282 
283  /* we know its STORE_OK */
284  k = headersEnd(buf, urnState->reqofs);
285 
286  if (0 == k) {
287  debugs(52, DBG_IMPORTANT, "urnHandleReply: didn't find end-of-headers for " << e->url() );
288  delete urnState;
289  return;
290  }
291 
292  s = buf + k;
293  assert(urlres_e->getReply());
294  rep = new HttpReply;
295  rep->parseCharBuf(buf, k);
296  debugs(52, 3, "reply exists, code=" << rep->sline.status() << ".");
297 
298  if (rep->sline.status() != Http::scOkay) {
299  debugs(52, 3, "urnHandleReply: failed.");
300  err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
301  err->url = xstrdup(e->url());
302  errorAppendEntry(e, err);
303  delete rep;
304  delete urnState;
305  return;
306  }
307 
308  delete rep;
309 
310  while (xisspace(*s))
311  ++s;
312 
313  urls = urnParseReply(s, urnState->request->method);
314 
315  if (!urls) { /* unknown URN error */
316  debugs(52, 3, "urnTranslateDone: unknown URN " << e->url());
317  err = new ErrorState(ERR_URN_RESOLVE, Http::scNotFound, urnState->request.getRaw(), urnState->ale);
318  err->url = xstrdup(e->url());
319  errorAppendEntry(e, err);
320  delete urnState;
321  return;
322  }
323 
324  for (i = 0; urls[i].url; ++i)
325  ++urlcnt;
326 
327  debugs(53, 3, "urnFindMinRtt: Counted " << i << " URLs");
328 
329  min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
330  qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
331  e->buffer();
332  SBuf body;
333  SBuf *mb = &body; // diff reduction hack; TODO: Remove
334  mb->appendf( "<TITLE>Select URL for %s</TITLE>\n"
335  "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
336  "<H2>Select URL for %s</H2>\n"
337  "<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", e->url(), e->url());
338 
339  for (i = 0; i < urlcnt; ++i) {
340  u = &urls[i];
341  debugs(52, 3, "URL {" << u->url << "}");
342  mb->appendf(
343  "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
344 
345  if (urls[i].rtt > 0)
346  mb->appendf(
347  "<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
348  else
349  mb->appendf("<TD align=\"right\">Unknown</TD>");
350 
351  mb->appendf("<TD>%s</TD></TR>\n", u->flags.cached ? " [cached]" : " ");
352  }
353 
354  mb->appendf(
355  "</TABLE>"
356  "<HR noshade size=\"1px\">\n"
357  "<ADDRESS>\n"
358  "Generated by %s@%s\n"
359  "</ADDRESS>\n",
361  rep = new HttpReply;
362  rep->setHeaders(Http::scFound, NULL, "text/html", mb->length(), 0, squid_curtime);
363 
364  if (min_u) {
365  rep->header.putStr(Http::HdrType::LOCATION, min_u->url);
366  }
367 
368  rep->body.set(body);
369  e->replaceHttpReply(rep);
370  e->complete();
371 
372  for (i = 0; i < urlcnt; ++i) {
373  safe_free(urls[i].url);
374  safe_free(urls[i].host);
375  }
376 
377  safe_free(urls);
378 
379  delete urnState;
380 }
381 
382 static url_entry *
383 urnParseReply(const char *inbuf, const HttpRequestMethod& m)
384 {
385  char *buf = xstrdup(inbuf);
386  char *token;
387  char *host;
388  url_entry *list;
389  url_entry *old;
390  int n = 32;
391  int i = 0;
392  debugs(52, 3, "urnParseReply");
393  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
394 
395  for (token = strtok(buf, crlf); token; token = strtok(NULL, crlf)) {
396  debugs(52, 3, "urnParseReply: got '" << token << "'");
397 
398  if (i == n) {
399  old = list;
400  n <<= 2;
401  list = (url_entry *)xcalloc(n + 1, sizeof(*list));
402  memcpy(list, old, i * sizeof(*list));
403  safe_free(old);
404  }
405 
406  host = urlHostname(token);
407 
408  if (NULL == host)
409  continue;
410 
411 #if USE_ICMP
412  list[i].rtt = netdbHostRtt(host);
413 
414  if (0 == list[i].rtt) {
415  debugs(52, 3, "urnParseReply: Pinging " << host);
416  netdbPingSite(host);
417  }
418 #else
419  list[i].rtt = 0;
420 #endif
421 
422  list[i].url = xstrdup(token);
423  list[i].host = xstrdup(host);
424  // TODO: Use storeHas() or lock/unlock entry to avoid creating unlocked
425  // ones.
426  list[i].flags.cached = storeGetPublic(list[i].url, m) ? 1 : 0;
427  ++i;
428  }
429 
430  debugs(52, 3, "urnParseReply: Found " << i << " URLs");
431  return list;
432 }
433 
MasterXaction::Pointer masterXaction
the master transaction this request belongs to. Never nil.
Definition: HttpRequest.h:239
size_type length() const
Returns the number of bytes stored in SBuf.
Definition: SBuf.h:404
void urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
Definition: urn.cc:213
char * urlHostname(const char *url)
Definition: Uri.cc:912
virtual void fillChecklist(ACLFilledChecklist &) const
configure the ACL checklist with the current transaction state
Definition: urn.cc:179
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation)
Definition: store_client.cc:65
char * url
Definition: urn.cc:64
#define assert(EX)
Definition: assert.h:17
SBuf & appendf(const char *fmt,...)
Definition: SBuf.cc:239
void storeClientCopy(store_client *sc, StoreEntry *e, StoreIOBuffer copyInto, STCB *callback, void *data)
UrnState(const AccessLogEntry::Pointer &anAle)
Definition: urn.cc:37
int unlock(const char *context)
Definition: store.cc:463
Definition: SBuf.h:86
static uint32 B
Definition: md4.c:43
#define xcalloc
Definition: membanger.c:57
store_client * sc
Definition: urn.cc:46
StoreEntry * entry
Definition: urn.cc:45
HttpRequestMethod method
Definition: HttpRequest.h:114
int i
Definition: membanger.c:49
#define xstrdup
virtual void buffer()
Definition: store.cc:1641
virtual ~UrnState()
Definition: urn.cc:79
#define safe_free(x)
Definition: xalloc.h:73
static STCB urnHandleReply
Definition: urn.cc:73
AccessLogEntry::Pointer al
info for the future access.log, and external ACL
char * url
Definition: errorpage.h:179
StoreEntry * urlres_e
Definition: urn.cc:47
static HttpRequest * FromUrlXXX(const char *url, const MasterXaction::Pointer &, const HttpRequestMethod &method=Http::METHOD_GET)
Definition: HttpRequest.cc:546
int netdbHostRtt(const char *host)
Definition: net_db.cc:1052
#define Must(condition)
Like assert() but throws an exception instead of aborting the process.
Definition: TextException.h:69
void created(StoreEntry *newEntry)
Definition: urn.cc:186
HttpReply const * getReply() const
Definition: store.cc:1676
store_client * storeClientListAdd(StoreEntry *e, void *data)
#define xisspace(x)
Definition: xis.h:17
static const char *const crlf
Definition: urn.cc:75
const char * getMyHostname(void)
Definition: tools.cc:412
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:522
time_t squid_curtime
Definition: stub_time.cc:17
void replaceHttpReply(HttpReply *, bool andStartWriting=true)
Definition: store.cc:1788
static url_entry * urnFindMinRtt(url_entry *urls, const HttpRequestMethod &, int *rtt_ret)
Definition: urn.cc:94
void complete()
Definition: store.cc:1064
SBuf & absolute() const
Definition: Uri.cc:539
static void getPublic(StoreClient *aClient, const char *uri, const HttpRequestMethod &method)
Definition: store.cc:515
Definition: urn.cc:32
char * host
Definition: urn.cc:65
void const char HLPCB void * data
Definition: stub_helper.cc:16
int64_t offset
Definition: StoreIOBuffer.h:55
struct url_entry::@153 flags
virtual LogTags * loggingTags()
Definition: urn.cc:57
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:161
const char * url() const
Definition: store.cc:1606
StoreEntry * storeCreateEntry(const char *url, const char *logUrl, const RequestFlags &flags, const HttpRequestMethod &method)
Definition: store.cc:782
#define DBG_IMPORTANT
Definition: Debug.h:46
#define URN_REQBUF_SZ
Definition: urn.cc:30
static int url_entry_sort(const void *A, const void *B)
Definition: urn.cc:220
#define CBDATA_CLASS(type)
Definition: cbdata.h:302
struct StoreIOBuffer::@147 flags
AnyP::Uri url
the request URI
Definition: HttpRequest.h:115
int reqofs
Definition: urn.cc:53
uint16_t flags
Definition: Store.h:210
void host(const char *src)
Definition: Uri.cc:48
bool hittingRequiresCollapsing() const
whether this entry can feed collapsed requests and only them
Definition: Store.h:194
#define APP_FULLNAME
Definition: version.h:22
char reqbuf[URN_REQBUF_SZ]
Definition: urn.cc:52
#define LOCAL_ARRAY(type, name, size)
Definition: leakcheck.h:18
void const char * buf
Definition: stub_helper.cc:16
class AccessLogEntry::CacheDetails cache
static uint32 A
Definition: md4.c:43
unsigned error
Definition: StoreIOBuffer.h:52
HttpHeader header
Definition: Message.h:75
int storeUnregister(store_client *sc, StoreEntry *e, void *data)
store_status_t store_status
Definition: Store.h:222
int cached
Definition: urn.cc:69
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1035
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
void STCB(void *, StoreIOBuffer)
Definition: StoreClient.h:16
C * getRaw() const
Definition: RefCount.h:74
AccessLogEntry::Pointer ale
details of the requesting transaction
Definition: urn.cc:50
bool parseCharBuf(const char *buf, ssize_t end)
Definition: Message.cc:148
void start(HttpRequest *, StoreEntry *)
Definition: urn.cc:163
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:711
static void Start(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp)
Initiates request forwarding to a peer or origin server.
Definition: FwdState.cc:329
void setRequest(HttpRequest *)
configure client request-related fields for the first time
HttpRequest::Pointer request
Definition: urn.cc:48
void netdbPingSite(const char *hostname)
Definition: net_db.cc:909
HttpRequest::Pointer urlres_r
Definition: urn.cc:49
#define SQUIDSBUFPH
Definition: SBuf.h:31
char * urlres
Definition: urn.cc:60
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:16
int rtt
Definition: urn.cc:66
static url_entry * urnParseReply(const char *inbuf, const HttpRequestMethod &)
Definition: urn.cc:383
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void setUriResFromRequest(HttpRequest *)
Definition: urn.cc:140
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
#define NULL
Definition: types.h:166
A StoreEntry::getPublic*() caller.
Definition: StoreClient.h:23
void lock(const char *context)
Definition: store.cc:439
Definition: urn.cc:63

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors