2 * DEBUG: section 61 Redirector
3 * AUTHOR: Duane Wessels
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
34 #include "acl/Checklist.h"
35 #include "client_side.h"
36 #include "client_side_reply.h"
37 #include "client_side_request.h"
38 #include "comm/Connection.h"
40 #include "format/Format.h"
42 #include "HttpRequest.h"
43 #include "mgr/Registration.h"
47 #include "SquidConfig.h"
50 #include "auth/UserRequest.h"
53 #include "ssl/support.h"
56 /// url maximum lengh + extra informations passed to redirector
57 #define MAX_REDIRECTOR_REQUEST_STRLEN (MAX_URL + 1024)
59 class RedirectStateData
62 explicit RedirectStateData(const char *url
);
71 CBDATA_CLASS2(RedirectStateData
);
74 static HLPCB redirectHandleReply
;
75 static HLPCB storeIdHandleReply
;
76 static helper
*redirectors
= NULL
;
77 static helper
*storeIds
= NULL
;
78 static OBJH redirectStats
;
79 static OBJH storeIdStats
;
80 static int redirectorBypassed
= 0;
81 static int storeIdBypassed
= 0;
82 static Format::Format
*redirectorExtrasFmt
= NULL
;
83 static Format::Format
*storeIdExtrasFmt
= NULL
;
85 CBDATA_CLASS_INIT(RedirectStateData
);
87 RedirectStateData::RedirectStateData(const char *url
) :
94 RedirectStateData::~RedirectStateData()
99 redirectHandleReply(void *data
, const HelperReply
&reply
)
101 RedirectStateData
*r
= static_cast<RedirectStateData
*>(data
);
102 debugs(61, 5, HERE
<< "reply=" << reply
);
104 // XXX: This function is now kept only to check for and display the garbage use-case
105 // and to map the old helper response format(s) into new format result code and key=value pairs
106 // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
108 if (reply
.result
== HelperReply::Unknown
) {
109 // BACKWARD COMPATIBILITY 2012-06-15:
110 // Some nasty old helpers send back the entire input line including extra format keys.
111 // This is especially bad for simple perl search-replace filter scripts.
113 // * trim all but the first word off the response.
114 // * warn once every 50 responses that this will stop being fixed-up soon.
116 if (const char * res
= reply
.other().content()) {
117 if (const char *t
= strchr(res
, ' ')) {
119 debugs(61, (!(warn
++%50)? DBG_CRITICAL
:2), "UPGRADE WARNING: URL rewriter reponded with garbage '" << t
<<
120 "'. Future Squid will treat this as part of the URL.");
121 const mb_size_t garbageLength
= reply
.other().contentSize() - (t
-res
);
122 reply
.modifiableOther().truncate(garbageLength
);
124 if (reply
.other().hasContent() && *res
== '\0')
125 reply
.modifiableOther().clean(); // drop the whole buffer of garbage.
127 // if we still have anything in other() after all that
128 // parse it into status=, url= and rewrite-url= keys
129 if (reply
.other().hasContent()) {
130 /* 2012-06-28: This cast is due to urlParse() truncating too-long URLs itself.
131 * At this point altering the helper buffer in that way is not harmful, but annoying.
132 * When Bug 1961 is resolved and urlParse has a const API, this needs to die.
134 char * result
= reply
.modifiableOther().content();
136 HelperReply newReply
;
137 // BACKWARD COMPATIBILITY 2012-06-15:
138 // We got HelperReply::Unknown reply result but new
139 // RedirectStateData handlers require HelperReply::Okay,
140 // else will drop the helper reply
141 newReply
.result
= HelperReply::Okay
;
142 newReply
.notes
.append(&reply
.notes
);
144 // check and parse for obsoleted Squid-2 urlgroup feature
145 if (*result
== '!') {
146 static int urlgroupWarning
= 0;
147 if (!urlgroupWarning
++)
148 debugs(85, DBG_IMPORTANT
, "UPGRADE WARNING: URL rewriter using obsolete Squid-2 urlgroup feature needs updating.");
149 if (char *t
= strchr(result
+1, '!')) {
151 newReply
.notes
.add("urlgroup", result
+1);
156 const Http::StatusCode status
= static_cast<Http::StatusCode
>(atoi(result
));
158 if (status
== Http::scMovedPermanently
159 || status
== Http::scFound
160 || status
== Http::scSeeOther
161 || status
== Http::scPermanentRedirect
162 || status
== Http::scTemporaryRedirect
) {
164 if (const char *t
= strchr(result
, ':')) {
166 snprintf(statusBuf
, sizeof(statusBuf
),"%3u",status
);
167 newReply
.notes
.add("status", statusBuf
);
169 // TODO: validate the URL produced here is RFC 2616 compliant URI
170 newReply
.notes
.add("url", t
);
172 debugs(85, DBG_CRITICAL
, "ERROR: URL-rewrite produces invalid " << status
<< " redirect Location: " << result
);
175 // status code is not a redirect code (or does not exist)
176 // treat as a re-write URL request
177 // TODO: validate the URL produced here is RFC 2616 compliant URI
179 newReply
.notes
.add("rewrite-url", result
);
183 if (cbdataReferenceValidDone(r
->data
, &cbdata
))
184 r
->handler(cbdata
, newReply
);
193 if (cbdataReferenceValidDone(r
->data
, &cbdata
))
194 r
->handler(cbdata
, reply
);
200 storeIdHandleReply(void *data
, const HelperReply
&reply
)
202 RedirectStateData
*r
= static_cast<RedirectStateData
*>(data
);
203 debugs(61, 5,"StoreId helper: reply=" << reply
);
205 // XXX: This function is now kept only to check for and display the garbage use-case
206 // and to map the old helper response format(s) into new format result code and key=value pairs
207 // it can be removed when the helpers are all updated to the normalized "OK/ERR kv-pairs" format
209 if (cbdataReferenceValidDone(r
->data
, &cbdata
))
210 r
->handler(cbdata
, reply
);
216 redirectStats(StoreEntry
* sentry
)
218 if (redirectors
== NULL
) {
219 storeAppendPrintf(sentry
, "No redirectors defined\n");
223 helperStats(sentry
, redirectors
, "Redirector Statistics");
225 if (Config
.onoff
.redirector_bypass
)
226 storeAppendPrintf(sentry
, "\nNumber of requests bypassed "
227 "because all redirectors were busy: %d\n", redirectorBypassed
);
231 storeIdStats(StoreEntry
* sentry
)
233 if (storeIds
== NULL
) {
234 storeAppendPrintf(sentry
, "No StoreId helpers defined\n");
238 helperStats(sentry
, storeIds
, "StoreId helper Statistics");
240 if (Config
.onoff
.store_id_bypass
)
241 storeAppendPrintf(sentry
, "\nNumber of requests bypassed "
242 "because all StoreId helpers were busy: %d\n", storeIdBypassed
);
246 constructHelperQuery(const char *name
, helper
*hlp
, HLPCB
*replyHandler
, ClientHttpRequest
* http
, HLPCB
*handler
, void *data
, Format::Format
*requestExtrasFmt
)
248 char buf
[MAX_REDIRECTOR_REQUEST_STRLEN
];
250 Http::StatusCode status
;
252 /** TODO: create a standalone method to initialize
253 * the RedirectStateData for all the helpers.
255 RedirectStateData
*r
= new RedirectStateData(http
->uri
);
256 r
->handler
= handler
;
257 r
->data
= cbdataReference(data
);
259 static MemBuf requestExtras
;
260 requestExtras
.reset();
261 if (requestExtrasFmt
)
262 requestExtrasFmt
->assemble(requestExtras
, http
->al
, 0);
264 sz
= snprintf(buf
, MAX_REDIRECTOR_REQUEST_STRLEN
, "%s%s%s\n",
266 requestExtras
.hasContent() ? " " : "",
267 requestExtras
.hasContent() ? requestExtras
.content() : "");
269 if ((sz
<=0) || (sz
>=MAX_REDIRECTOR_REQUEST_STRLEN
)) {
271 status
= Http::scInternalServerError
;
272 debugs(61, DBG_CRITICAL
, "ERROR: Gateway Failure. Can not build request to be passed to " << name
<< ". Request ABORTED.");
274 status
= Http::scUriTooLong
;
275 debugs(61, DBG_CRITICAL
, "ERROR: Gateway Failure. Request passed to " << name
<< " exceeds MAX_REDIRECTOR_REQUEST_STRLEN (" << MAX_REDIRECTOR_REQUEST_STRLEN
<< "). Request ABORTED.");
278 clientStreamNode
*node
= (clientStreamNode
*)http
->client_stream
.tail
->prev
->data
;
279 clientReplyContext
*repContext
= dynamic_cast<clientReplyContext
*>(node
->data
.getRaw());
281 Ip::Address tmpnoaddr
;
282 tmpnoaddr
.setNoAddr();
283 repContext
->setReplyToError(ERR_GATEWAY_FAILURE
, status
,
284 http
->request
->method
, NULL
,
285 http
->getConn() != NULL
&& http
->getConn()->clientConnection
!= NULL
?
286 http
->getConn()->clientConnection
->remote
: tmpnoaddr
,
290 http
->getConn() != NULL
&& http
->getConn()->getAuth() != NULL
?
291 http
->getConn()->getAuth() : http
->request
->auth_user_request
);
296 node
= (clientStreamNode
*)http
->client_stream
.tail
->data
;
297 clientStreamRead(node
, http
, node
->readBuffer
);
301 debugs(61,6, HERE
<< "sending '" << buf
<< "' to the " << name
<< " helper");
302 helperSubmit(hlp
, buf
, replyHandler
, r
);
305 /**** PUBLIC FUNCTIONS ****/
308 redirectStart(ClientHttpRequest
* http
, HLPCB
* handler
, void *data
)
312 debugs(61, 5, "redirectStart: '" << http
->uri
<< "'");
314 if (Config
.onoff
.redirector_bypass
&& redirectors
->stats
.queue_size
) {
315 /* Skip redirector if there is one request queued */
316 ++redirectorBypassed
;
317 HelperReply bypassReply
;
318 bypassReply
.result
= HelperReply::Okay
;
319 bypassReply
.notes
.add("message","URL rewrite/redirect queue too long. Bypassed.");
320 handler(data
, bypassReply
);
324 constructHelperQuery("redirector", redirectors
, redirectHandleReply
, http
, handler
, data
, redirectorExtrasFmt
);
328 * Handles the StoreID feature helper starting.
329 * For now it cannot be done using the redirectStart method.
332 storeIdStart(ClientHttpRequest
* http
, HLPCB
* handler
, void *data
)
336 debugs(61, 5, "storeIdStart: '" << http
->uri
<< "'");
338 if (Config
.onoff
.store_id_bypass
&& storeIds
->stats
.queue_size
) {
339 /* Skip StoreID Helper if there is one request queued */
341 HelperReply bypassReply
;
343 bypassReply
.result
= HelperReply::Okay
;
345 bypassReply
.notes
.add("message","StoreId helper queue too long. Bypassed.");
346 handler(data
, bypassReply
);
350 constructHelperQuery("storeId helper", storeIds
, storeIdHandleReply
, http
, handler
, data
, storeIdExtrasFmt
);
356 static bool init
= false;
359 Mgr::RegisterAction("redirector", "URL Redirector Stats", redirectStats
, 0, 1);
360 Mgr::RegisterAction("store_id", "StoreId helper Stats", storeIdStats
, 0, 1);
363 if (Config
.Program
.redirect
) {
365 if (redirectors
== NULL
)
366 redirectors
= new helper("redirector");
368 redirectors
->cmdline
= Config
.Program
.redirect
;
370 redirectors
->childs
.updateLimits(Config
.redirectChildren
);
372 redirectors
->ipc_type
= IPC_STREAM
;
374 helperOpenServers(redirectors
);
377 if (Config
.Program
.store_id
) {
379 if (storeIds
== NULL
)
380 storeIds
= new helper("store_id");
382 storeIds
->cmdline
= Config
.Program
.store_id
;
384 storeIds
->childs
.updateLimits(Config
.storeIdChildren
);
386 storeIds
->ipc_type
= IPC_STREAM
;
388 helperOpenServers(storeIds
);
391 if (Config
.redirector_extras
) {
392 redirectorExtrasFmt
= new ::Format::Format("url_rewrite_extras");
393 (void)redirectorExtrasFmt
->parse(Config
.redirector_extras
);
396 if (Config
.storeId_extras
) {
397 storeIdExtrasFmt
= new ::Format::Format("store_id_extras");
398 (void)storeIdExtrasFmt
->parse(Config
.storeId_extras
);
405 redirectShutdown(void)
407 /** FIXME: Temporary unified helpers Shutdown
408 * When and if needed for more helpers a separated shutdown
409 * method will be added for each of them.
411 if (!storeIds
&& !redirectors
)
415 helperShutdown(redirectors
);
418 helperShutdown(storeIds
);
429 delete redirectorExtrasFmt
;
430 redirectorExtrasFmt
= NULL
;
432 delete storeIdExtrasFmt
;
433 storeIdExtrasFmt
= NULL
;