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