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#define URN_REQBUF_SZ 4096
31
32class UrnState : public StoreClient
33{
35
36public:
37 explicit UrnState(const AccessLogEntry::Pointer &anAle): ale(anAle) {}
38
39 void start (HttpRequest *, StoreEntry *);
41
42 ~UrnState() override;
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
54private:
55 /* StoreClient API */
56 LogTags *loggingTags() const override { return ale ? &ale->cache.code : nullptr; }
57 void fillChecklist(ACLFilledChecklist &) const override;
58
59 char *urlres = nullptr;
60};
61
62typedef struct {
63 char *url;
64 char *host;
65 int rtt;
66
67 struct {
68 int cached;
69 } flags;
70} url_entry;
71
73static url_entry *urnParseReply(const char *inbuf, const HttpRequestMethod&);
74static const char *const crlf = "\r\n";
75
77
79{
81 if (urlres_e) {
82 if (sc)
84 urlres_e->unlock("~UrnState+res");
85 }
86
87 if (entry)
88 entry->unlock("~UrnState+prime");
89
91 });
92}
93
94static url_entry *
95urnFindMinRtt(url_entry * urls, const HttpRequestMethod &, int *rtt_ret)
96{
97 int min_rtt = 0;
98 url_entry *u = nullptr;
99 url_entry *min_u = nullptr;
100 int i;
101 int urlcnt = 0;
102 debugs(52, 3, "urnFindMinRtt");
103 assert(urls != nullptr);
104
105 for (i = 0; nullptr != 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
140void
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));
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);
156 return;
157 }
158
159 urlres = xstrdup(local_urlres);
161}
162
163void
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 == nullptr)
174 return;
175
176 auto urlEntry = storeGetPublic(urlres, Http::METHOD_GET);
177
178 if (!urlEntry || (urlEntry->hittingRequiresCollapsing() && !startCollapsingOn(*urlEntry, false))) {
182 if (urlEntry) {
183 urlEntry->abandon(__FUNCTION__);
184 urlEntry = nullptr;
185 }
186 } else {
187 urlres_e = urlEntry;
188 urlres_e->lock("UrnState::start");
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
203void
205{
206 checklist.setRequest(request.getRaw());
207 checklist.al = ale;
208}
209
210void
212{
213 const auto anUrn = new UrnState(ale);
214 anUrn->start (r, e);
215}
216
217static int
218url_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 */
234static void
235urnHandleReply(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 = nullptr;
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, nullptr);
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, nullptr, "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
386static url_entry *
387urnParseReply(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(nullptr, 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
time_t squid_curtime
Definition: stub_libtime.cc:20
#define SQUIDSBUFPH
Definition: SBuf.h:31
#define SQUIDSBUFPRINT(s)
Definition: SBuf.h:32
void STCB(void *, StoreIOBuffer)
Definition: StoreClient.h:19
#define SWALLOW_EXCEPTIONS(code)
Definition: TextException.h:75
#define Must(condition)
Definition: TextException.h:71
#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:584
void host(const char *src)
Definition: Uri.cc:99
bool parse(const HttpRequestMethod &, const SBuf &url)
Definition: Uri.cc:254
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 set(const SBuf &newContent)
Definition: HttpBody.h:26
void putStr(Http::HdrType id, const char *str)
Definition: HttpHeader.cc:1027
Http::StatusLine sline
Definition: HttpReply.h:56
void setHeaders(Http::StatusCode status, const char *reason, const char *ctype, int64_t clen, time_t lmt, time_t expires)
Definition: HttpReply.cc:167
HttpBody body
Definition: HttpReply.h:58
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
bool parseCharBuf(const char *buf, ssize_t end)
Definition: Message.cc:140
Http::StatusCode status() const
retrieve the status code for this status line
Definition: StatusLine.h:45
C * getRaw() const
Definition: RefCount.h:80
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,...)
Definition: SBuf.cc:229
a storeGetPublic*() caller
Definition: StoreClient.h:27
bool startCollapsingOn(const StoreEntry &, const bool doingRevalidation) const
Definition: store_client.cc:64
bool isAccepting() const
Definition: store.cc:1957
uint16_t flags
Definition: Store.h:232
int unlock(const char *context)
Definition: store.cc:443
const char * url() const
Definition: store.cc:1534
void complete()
Definition: store.cc:1005
void lock(const char *context)
Definition: store.cc:419
void replaceHttpReply(const HttpReplyPointer &, const bool andStartWriting=true)
Definition: store.cc:1673
store_status_t store_status
Definition: Store.h:244
void buffer() override
Definition: store.cc:1569
unsigned error
Definition: StoreIOBuffer.h:52
int64_t offset
Definition: StoreIOBuffer.h:55
struct StoreIOBuffer::@142 flags
Definition: urn.cc:33
AccessLogEntry::Pointer ale
details of the requesting transaction
Definition: urn.cc:49
void setUriResFromRequest(HttpRequest *)
Definition: urn.cc:141
HttpRequest::Pointer urlres_r
Definition: urn.cc:48
StoreEntry * urlres_e
Definition: urn.cc:46
void start(HttpRequest *, StoreEntry *)
Definition: urn.cc:164
char reqbuf[URN_REQBUF_SZ]
Definition: urn.cc:51
char * urlres
Definition: urn.cc:59
store_client * sc
Definition: urn.cc:45
int reqofs
Definition: urn.cc:52
UrnState(const AccessLogEntry::Pointer &anAle)
Definition: urn.cc:37
~UrnState() override
Definition: urn.cc:78
StoreEntry * entry
Definition: urn.cc:44
LogTags * loggingTags() const override
Definition: urn.cc:56
void fillChecklist(ACLFilledChecklist &) const override
configure the given checklist (to reflect the current transaction state)
Definition: urn.cc:204
HttpRequest::Pointer request
Definition: urn.cc:47
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:193
#define EBIT_TEST(flag, bit)
Definition: defines.h:69
@ ENTRY_ABORTED
Definition: enums.h:115
@ STORE_PENDING
Definition: enums.h:51
@ ERR_URN_RESOLVE
Definition: forward.h:36
char const * visible_appname_string
void errorAppendEntry(StoreEntry *entry, ErrorState *err)
Definition: errorpage.cc:718
static uint32 A
Definition: md4.c:43
static uint32 B
Definition: md4.c:43
size_t headersEnd(const char *mime, size_t l, bool &containsObsFold)
Definition: mime_header.cc:17
@ 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:887
int netdbHostRtt(const char *host)
Definition: net_db.cc:1020
#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:733
StoreEntry * storeGetPublic(const char *uri, const HttpRequestMethod &method)
Definition: store.cc:478
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:62
int cached
Definition: urn.cc:68
int rtt
Definition: urn.cc:65
struct url_entry::@146 flags
char * host
Definition: urn.cc:64
char * url
Definition: urn.cc:63
const char * getMyHostname(void)
Definition: tools.cc:467
static const char *const crlf
Definition: urn.cc:74
void urnStart(HttpRequest *r, StoreEntry *e, const AccessLogEntryPointer &ale)
Definition: urn.cc:211
static url_entry * urnParseReply(const char *inbuf, const HttpRequestMethod &)
Definition: urn.cc:387
#define URN_REQBUF_SZ
Definition: urn.cc:30
static int url_entry_sort(const void *A, const void *B)
Definition: urn.cc:218
static url_entry * urnFindMinRtt(url_entry *urls, const HttpRequestMethod &, int *rtt_ret)
Definition: urn.cc:95
static STCB urnHandleReply
Definition: urn.cc:72
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:17

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors