redirect.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 61 Redirector */
10
11#include "squid.h"
12#include "acl/Checklist.h"
13#include "cache_cf.h"
14#include "client_side.h"
15#include "client_side_reply.h"
16#include "client_side_request.h"
17#include "comm/Connection.h"
18#include "fde.h"
19#include "format/Format.h"
20#include "globals.h"
21#include "helper.h"
22#include "helper/Reply.h"
23#include "http/Stream.h"
24#include "HttpRequest.h"
25#include "mgr/Registration.h"
26#include "redirect.h"
27#include "rfc1738.h"
28#include "sbuf/SBuf.h"
29#include "SquidConfig.h"
30#include "Store.h"
31#if USE_AUTH
32#include "auth/UserRequest.h"
33#endif
34#if USE_OPENSSL
35#include "ssl/support.h"
36#endif
37
39#define MAX_REDIRECTOR_REQUEST_STRLEN (MAX_URL + 1024)
40
42{
44
45public:
46 explicit RedirectStateData(const char *url);
48
49 void *data;
51
53};
54
61static int redirectorBypassed = 0;
62static int storeIdBypassed = 0;
65
67
69 data(nullptr),
70 orig_url(url),
71 handler(nullptr)
72{
73}
74
76{
77}
78
79static void
80redirectHandleReply(void *data, const Helper::Reply &reply)
81{
82 RedirectStateData *r = static_cast<RedirectStateData *>(data);
83 debugs(61, 5, "reply=" << reply);
84
85 // XXX: This function is now kept only to check for and display the garbage use-case
86 // and to map the old helper response format(s) into new format result code and key=value pairs
87 // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
88
89 if (reply.result == Helper::Unknown) {
90 // BACKWARD COMPATIBILITY 2012-06-15:
91 // Some nasty old helpers send back the entire input line including extra format keys.
92 // This is especially bad for simple perl search-replace filter scripts.
93 //
94 // * trim all but the first word off the response.
95 // * warn once every 50 responses that this will stop being fixed-up soon.
96 //
97 if (reply.other().hasContent()) {
98 const char * res = reply.other().content();
99 size_t replySize = 0;
100 if (const char *t = strchr(res, ' ')) {
101 static int warn = 0;
102 debugs(61, (!(warn++%50)? DBG_CRITICAL:2), "WARNING: UPGRADE: URL rewriter reponded with garbage '" << t <<
103 "'. Future Squid will treat this as part of the URL.");
104 replySize = t - res;
105 } else
106 replySize = reply.other().contentSize();
107
108 // if we still have anything in other() after all that
109 // parse it into status=, url= and rewrite-url= keys
110 if (replySize) {
111 MemBuf replyBuffer;
112 replyBuffer.init(replySize, replySize);
113 replyBuffer.append(reply.other().content(), reply.other().contentSize());
114 char * result = replyBuffer.content();
115
116 Helper::Reply newReply;
117 // BACKWARD COMPATIBILITY 2012-06-15:
118 // We got Helper::Unknown reply result but new
119 // RedirectStateData handlers require Helper::Okay,
120 // else will drop the helper reply
121 newReply.result = Helper::Okay;
122 newReply.notes.append(&reply.notes);
123
124 // check and parse for obsoleted Squid-2 urlgroup feature
125 if (*result == '!') {
126 static int urlgroupWarning = 0;
127 if (!urlgroupWarning++)
128 debugs(85, DBG_IMPORTANT, "WARNING: UPGRADE: URL rewriter using obsolete Squid-2 urlgroup feature needs updating.");
129 if (char *t = strchr(result+1, '!')) {
130 *t = '\0';
131 newReply.notes.add("urlgroup", result+1);
132 result = t + 1;
133 }
134 }
135
136 const Http::StatusCode status = static_cast<Http::StatusCode>(atoi(result));
137
138 if (status == Http::scMovedPermanently
139 || status == Http::scFound
140 || status == Http::scSeeOther
141 || status == Http::scPermanentRedirect
142 || status == Http::scTemporaryRedirect) {
143
144 if (const char *t = strchr(result, ':')) {
145 char statusBuf[4];
146 snprintf(statusBuf, sizeof(statusBuf),"%3u",status);
147 newReply.notes.add("status", statusBuf);
148 ++t;
149 // TODO: validate the URL produced here is RFC 2616 compliant URI
150 newReply.notes.add("url", t);
151 } else {
152 debugs(85, DBG_CRITICAL, "ERROR: URL-rewrite produces invalid " << status << " redirect Location: " << result);
153 }
154 } else {
155 // status code is not a redirect code (or does not exist)
156 // treat as a re-write URL request
157 // TODO: validate the URL produced here is RFC 2616 compliant URI
158 if (*result)
159 newReply.notes.add("rewrite-url", result);
160 }
161
162 void *cbdata;
164 r->handler(cbdata, newReply);
165
166 delete r;
167 return;
168 }
169 }
170 }
171
172 void *cbdata;
174 r->handler(cbdata, reply);
175
176 delete r;
177}
178
179static void
180storeIdHandleReply(void *data, const Helper::Reply &reply)
181{
182 RedirectStateData *r = static_cast<RedirectStateData *>(data);
183 debugs(61, 5,"StoreId helper: reply=" << reply);
184
185 // XXX: This function is now kept only to check for and display the garbage use-case
186 // and to map the old helper response format(s) into new format result code and key=value pairs
187 // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
188 void *cbdata;
190 r->handler(cbdata, reply);
191
192 delete r;
193}
194
195static void
197{
198 if (redirectors == nullptr) {
199 storeAppendPrintf(sentry, "No redirectors defined\n");
200 return;
201 }
202
203 redirectors->packStatsInto(sentry, "Redirector Statistics");
204
206 storeAppendPrintf(sentry, "\nNumber of requests bypassed "
207 "because all redirectors were busy: %d\n", redirectorBypassed);
208}
209
210static void
212{
213 if (storeIds == nullptr) {
214 storeAppendPrintf(sentry, "No StoreId helpers defined\n");
215 return;
216 }
217
218 storeIds->packStatsInto(sentry, "StoreId helper Statistics");
219
221 storeAppendPrintf(sentry, "\nNumber of requests bypassed "
222 "because all StoreId helpers were busy: %d\n", storeIdBypassed);
223}
224
225static void
226constructHelperQuery(const char * const name, const Helper::Client::Pointer &hlp, HLPCB * const replyHandler, ClientHttpRequest * const http, HLPCB * const handler, void * const data, Format::Format * const requestExtrasFmt)
227{
229 int sz;
230 Http::StatusCode status;
231
236 r->handler = handler;
237 r->data = cbdataReference(data);
238
239 static MemBuf requestExtras;
240 requestExtras.reset();
241 if (requestExtrasFmt)
242 requestExtrasFmt->assemble(requestExtras, http->al, 0);
243
244 sz = snprintf(buf, MAX_REDIRECTOR_REQUEST_STRLEN, "%s%s%s\n",
245 r->orig_url.c_str(),
246 requestExtras.hasContent() ? " " : "",
247 requestExtras.hasContent() ? requestExtras.content() : "");
248
249 if ((sz<=0) || (sz>=MAX_REDIRECTOR_REQUEST_STRLEN)) {
250 if (sz<=0) {
252 debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Can not build request to be passed to " << name << ". Request ABORTED.");
253 } else {
254 status = Http::scUriTooLong;
255 debugs(61, DBG_CRITICAL, "ERROR: Gateway Failure. Request passed to " << name << " exceeds MAX_REDIRECTOR_REQUEST_STRLEN (" << MAX_REDIRECTOR_REQUEST_STRLEN << "). Request ABORTED.");
256 }
257
259 clientReplyContext *repContext = dynamic_cast<clientReplyContext *>(node->data.getRaw());
260 assert (repContext);
261 repContext->setReplyToError(ERR_GATEWAY_FAILURE, status,
262 nullptr,
263 http->getConn(),
264 http->request,
265 nullptr,
266#if USE_AUTH
267 http->getConn() != nullptr && http->getConn()->getAuth() != nullptr ?
268 http->getConn()->getAuth() : http->request->auth_user_request);
269#else
270 nullptr);
271#endif
272
274 clientStreamRead(node, http, node->readBuffer);
275 return;
276 }
277
278 debugs(61,6, "sending '" << buf << "' to the " << name << " helper");
279 helperSubmit(hlp, buf, replyHandler, r);
280}
281
282/**** PUBLIC FUNCTIONS ****/
283
284void
286{
287 assert(http);
289 debugs(61, 5, "redirectStart: '" << http->uri << "'");
290
291 // TODO: Deprecate Config.onoff.redirector_bypass in favor of either
292 // onPersistentOverload or a new onOverload option that applies to all helpers.
293 if (Config.onoff.redirector_bypass && redirectors->willOverload()) {
294 /* Skip redirector if the queue is full */
296 Helper::Reply bypassReply;
297 bypassReply.result = Helper::Okay;
298 bypassReply.notes.add("message","URL rewrite/redirect queue too long. Bypassed.");
299 handler(data, bypassReply);
300 return;
301 }
302
304}
305
310void
312{
313 assert(http);
315 debugs(61, 5, "storeIdStart: '" << http->uri << "'");
316
317 if (Config.onoff.store_id_bypass && storeIds->willOverload()) {
318 /* Skip StoreID Helper if the queue is full */
320 Helper::Reply bypassReply;
321
322 bypassReply.result = Helper::Okay;
323
324 bypassReply.notes.add("message","StoreId helper queue too long. Bypassed.");
325 handler(data, bypassReply);
326 return;
327 }
328
330}
331
332void
334{
335 static bool init = false;
336
337 if (!init) {
338 Mgr::RegisterAction("redirector", "URL Redirector Stats", redirectStats, 0, 1);
339 Mgr::RegisterAction("store_id", "StoreId helper Stats", storeIdStats, 0, 1);
340 }
341
342 if (Config.Program.redirect) {
343
344 if (redirectors == nullptr)
345 redirectors = Helper::Client::Make("redirector");
346
348
349 // BACKWARD COMPATIBILITY:
350 // if redirectot_bypass is set then use queue_size=0 as default size
353
354 redirectors->childs.updateLimits(Config.redirectChildren);
355
356 redirectors->ipc_type = IPC_STREAM;
357
359
361 redirectors->retryBrokenHelper = true; // XXX: make this configurable ?
362 redirectors->onTimedOutResponse.clear();
364 redirectors->onTimedOutResponse.assign(Config.onUrlRewriteTimeout.response);
365
366 redirectors->openSessions();
367 }
368
369 if (Config.Program.store_id) {
370
371 if (storeIds == nullptr)
372 storeIds = Helper::Client::Make("store_id");
373
374 storeIds->cmdline = Config.Program.store_id;
375
376 // BACKWARD COMPATIBILITY:
377 // if store_id_bypass is set then use queue_size=0 as default size
380
381 storeIds->childs.updateLimits(Config.storeIdChildren);
382
383 storeIds->ipc_type = IPC_STREAM;
384
385 storeIds->retryBrokenHelper = true; // XXX: make this configurable ?
386
387 storeIds->openSessions();
388 }
389
391 delete redirectorExtrasFmt;
392 redirectorExtrasFmt = new ::Format::Format("url_rewrite_extras");
394 }
395
397 delete storeIdExtrasFmt;
398 storeIdExtrasFmt = new ::Format::Format("store_id_extras");
400 }
401
402 init = true;
403}
404
405void
407{
412 if (redirectors)
414
415 if (storeIds)
417
418 if (!shutting_down)
419 return;
420
421 redirectors = nullptr;
422
423 storeIds = nullptr;
424
425 delete redirectorExtrasFmt;
426 redirectorExtrasFmt = nullptr;
427
428 delete storeIdExtrasFmt;
429 storeIdExtrasFmt = nullptr;
430}
431
432void
434{
436 redirectInit();
437}
438
class SquidConfig Config
Definition: SquidConfig.cc:12
void warn(char *format,...)
#define assert(EX)
Definition: assert.h:17
#define cbdataReference(var)
Definition: cbdata.h:343
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:320
#define cbdataReferenceValidDone(var, ptr)
Definition: cbdata.h:239
#define CBDATA_CLASS(type)
Definition: cbdata.h:289
HttpRequest *const request
ConnStateData * getConn() const
const AccessLogEntry::Pointer al
access.log entry
const Auth::UserRequest::Pointer & getAuth() const
Definition: client_side.h:123
bool parse(const char *def)
Definition: Format.cc:66
void assemble(MemBuf &mb, const AccessLogEntryPointer &al, int logSequenceNumber) const
assemble the state information into a formatted line.
Definition: Format.cc:377
unsigned int queue_size
Definition: ChildConfig.h:91
static Pointer Make(const char *name)
Definition: helper.cc:759
NotePairs notes
Definition: Reply.h:62
Helper::ResultCode result
The helper response 'result' field.
Definition: Reply.h:59
const MemBuf & other() const
Definition: Reply.h:42
Auth::UserRequest::Pointer auth_user_request
Definition: HttpRequest.h:127
Definition: MemBuf.h:24
void append(const char *c, int sz) override
Definition: MemBuf.cc:209
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
char * content()
start of the added data
Definition: MemBuf.h:41
mb_size_t contentSize() const
available data size
Definition: MemBuf.h:47
void reset()
Definition: MemBuf.cc:129
bool hasContent() const
Definition: MemBuf.h:54
void append(const NotePairs *src)
Append the entries of the src NotePairs list to our list.
Definition: Notes.cc:374
void add(const SBuf &key, const SBuf &value)
Definition: Notes.cc:312
RedirectStateData(const char *url)
Definition: redirect.cc:68
HLPCB * handler
Definition: redirect.cc:52
Definition: SBuf.h:94
const char * c_str()
Definition: SBuf.cc:516
Helper::ChildConfig redirectChildren
Definition: SquidConfig.h:216
wordlist * store_id
Definition: SquidConfig.h:202
Helper::ChildConfig storeIdChildren
Definition: SquidConfig.h:217
int store_id_bypass
Definition: SquidConfig.h:302
char * storeId_extras
Definition: SquidConfig.h:534
struct SquidConfig::@106 onoff
int redirector_bypass
Definition: SquidConfig.h:301
wordlist * redirect
Definition: SquidConfig.h:201
struct SquidConfig::@99 Program
time_t urlRewrite
Definition: SquidConfig.h:132
struct SquidConfig::UrlHelperTimeout onUrlRewriteTimeout
char * redirector_extras
Definition: SquidConfig.h:527
struct SquidConfig::@93 Timeout
Definition: cbdata.cc:38
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define DBG_CRITICAL
Definition: Stream.h:37
#define IPC_STREAM
Definition: defines.h:106
@ ERR_GATEWAY_FAILURE
Definition: forward.h:67
int shutting_down
void clientStreamRead(clientStreamNode *thisObject, ClientHttpRequest *http, StoreIOBuffer readBuffer)
void HLPCB(void *, const Helper::Reply &)
Definition: forward.h:33
void helperShutdown(const Helper::Client::Pointer &hlp)
Definition: helper.cc:771
void helperSubmit(const Helper::Client::Pointer &hlp, const char *const buf, HLPCB *const callback, void *const data)
Definition: helper.cc:480
void OBJH(StoreEntry *)
Definition: forward.h:44
@ Unknown
Definition: ResultCode.h:17
@ Okay
Definition: ResultCode.h:18
StatusCode
Definition: StatusCode.h:20
@ scUriTooLong
Definition: StatusCode.h:58
@ scFound
Definition: StatusCode.h:38
@ scInternalServerError
Definition: StatusCode.h:71
@ scPermanentRedirect
Definition: StatusCode.h:43
@ scSeeOther
Definition: StatusCode.h:39
@ scTemporaryRedirect
Definition: StatusCode.h:42
@ scMovedPermanently
Definition: StatusCode.h:37
void RegisterAction(char const *action, char const *desc, OBJH *handler, int pw_req_flag, int atomic)
Definition: Registration.cc:16
static void handler(int signo)
Definition: purge.cc:858
static void constructHelperQuery(const char *const name, const Helper::Client::Pointer &hlp, HLPCB *const replyHandler, ClientHttpRequest *const http, HLPCB *const handler, void *const data, Format::Format *const requestExtrasFmt)
Definition: redirect.cc:226
static Format::Format * redirectorExtrasFmt
Definition: redirect.cc:63
static HLPCB redirectHandleReply
Definition: redirect.cc:55
void redirectInit(void)
Definition: redirect.cc:333
static OBJH storeIdStats
Definition: redirect.cc:60
static Helper::Client::Pointer storeIds
Definition: redirect.cc:58
static HLPCB storeIdHandleReply
Definition: redirect.cc:56
void storeIdStart(ClientHttpRequest *http, HLPCB *handler, void *data)
Definition: redirect.cc:311
static Format::Format * storeIdExtrasFmt
Definition: redirect.cc:64
static Helper::Client::Pointer redirectors
Definition: redirect.cc:57
#define MAX_REDIRECTOR_REQUEST_STRLEN
url maximum length + extra information passed to redirector
Definition: redirect.cc:39
void redirectReconfigure()
Definition: redirect.cc:433
void redirectStart(ClientHttpRequest *http, HLPCB *handler, void *data)
Definition: redirect.cc:285
static int storeIdBypassed
Definition: redirect.cc:62
static int redirectorBypassed
Definition: redirect.cc:61
void redirectShutdown(void)
Definition: redirect.cc:406
static OBJH redirectStats
Definition: redirect.cc:59
@ toutActUseConfiguredResponse
Definition: redirect.h:16
@ toutActRetry
Definition: redirect.h:16
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:841
Definition: parse.c:104

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors