XRootD
Loading...
Searching...
No Matches
XrdXrootdRedirHelper Class Reference

#include <XrdXrootdRedirHelper.hh>

Collaboration diagram for XrdXrootdRedirHelper:

Public Types

enum class  Outcome {
  Unchanged ,
  Replaced ,
  Error
}

Static Public Member Functions

static void Init (XrdXrootdRedirPI *pi, XrdSysError *eDest, int ipHold)
static bool IsActive ()
static bool ParseURL (const char *url, std::string &urlHead, std::string &host, std::string &port, std::string &urlTail)
static Outcome Redirect (const char *trg, int &port, XrdNetAddrInfo &clientAddr, std::string &outTarget, std::string &errMsg)
static void SetClockForTesting (std::function< time_t()> nowFn)

Detailed Description

Process-wide adapter around the redirect plugin (XrdXrootdRedirPI) so the plugin can be invoked uniformly from the XRootD protocol and from any other subsystem that produces redirect targets (e.g. the HTTP TPC handler addressed by issue #2767).

Responsibilities centralised here so they do not need to be reproduced at every call site:

  • the static plugin pointer plus the logger used for diagnostics;
  • a cache of XrdNetAddrInfo objects for previously-resolved redirect targets (each plugin call needs the target's resolved netaddr, which is comparatively expensive to obtain);
  • the empty / "<new target>" / "!<error>" return-string contract of the plugin, normalised into a tri-state Outcome.

The class is non-instantiable; it is initialised once by Init() (called from XrdXrootdConfig.cc after the redirect plugin has been loaded) and its Redirect() static method is then invoked concurrently from any number of worker threads. When no plugin is configured IsActive() returns false and Redirect() is an inexpensive no-op.

Definition at line 59 of file XrdXrootdRedirHelper.hh.

Member Enumeration Documentation

◆ Outcome

enum class XrdXrootdRedirHelper::Outcome
strong

Tri-state outcome returned by Redirect().

Mirrors the three cases of the plugin's return-string contract:

  • empty string -> Unchanged: caller keeps the original target
  • "<new target>" -> Replaced: caller emits the new target
  • "!<message>" -> Error: caller fails the request, surfaces the message
Enumerator
Unchanged 
Replaced 
Error 

Definition at line 73 of file XrdXrootdRedirHelper.hh.

73{ Unchanged, Replaced, Error };

Member Function Documentation

◆ Init()

void XrdXrootdRedirHelper::Init ( XrdXrootdRedirPI * pi,
XrdSysError * eDest,
int ipHold )
static

Initialise the helper. Must be called exactly once, after the redirect plugin has been loaded and before any worker thread invokes Redirect(). Calling Init(nullptr, ...) is a valid no-op that simply disarms the helper.

Parameters
pipointer to the loaded redirect plugin, or nullptr if no plugin was loaded. When nullptr, IsActive() returns false and Redirect() always returns Unchanged.
eDestlogger used to report cache-population failures. Must not be nullptr when pi is non-null.
ipHoldnumber of seconds for which a successfully-resolved target XrdNetAddrInfo is reused before being refreshed. Mirrors XrdXrootdProtocol::redirIPHold.

Definition at line 164 of file XrdXrootdRedirHelper.cc.

166{
167 gPlugin = pi;
168 gLog = eDest;
169 gIPHold = ipHold;
170}
static XrdSysError eDest(0,"crypto_")

References eDest.

Referenced by XrdXrootdProtocol::Configure().

Here is the caller graph for this function:

◆ IsActive()

bool XrdXrootdRedirHelper::IsActive ( )
static
Returns
true iff a redirect plugin has been registered via Init().

Definition at line 176 of file XrdXrootdRedirHelper.cc.

176{ return gPlugin != nullptr; }

◆ ParseURL()

bool XrdXrootdRedirHelper::ParseURL ( const char * url,
std::string & urlHead,
std::string & host,
std::string & port,
std::string & urlTail )
static

Parse a scheme://host[:port][/tail] redirect URL into its components.

Exposed as a static method, rather than kept private to the .cc, so the URL grammar Redirect() relies on for URL-form targets can be unit-tested directly without a plugin, the netaddr cache or DNS. Redirect() is its only production caller.

Parameters
urlURL to parse. Must not be nullptr.
urlHeadoutput: scheme prefix up to and including "://".
hostoutput: host name, with any ":port" suffix removed.
portoutput: port string, or "" when no port is present.
urlTailoutput: path/query tail including the leading '/', or "" when the URL has no path.
Returns
true on success. false when url is not a parseable scheme://host[:port][/tail] URL; the output parameters are then left in an unspecified state.

Definition at line 275 of file XrdXrootdRedirHelper.cc.

278{
279 const char *hBeg = strstr(url, "://");
280 if (!hBeg) return false;
281 hBeg += 3;
282 urlHead.assign(url, hBeg - url);
283
284 // Split off the path/query tail; require the host[:port] authority that
285 // precedes it to be at least two characters long.
286 if (const char *tail = strstr(hBeg, "/"); !tail)
287 {urlTail.clear(); host = hBeg;}
288 else {if (tail - hBeg < 3) return false;
289 host.assign(hBeg, tail - hBeg);
290 urlTail = tail;
291 }
292
293 // Separate an optional ":port" suffix from the host.
294 port.clear();
295 if (size_t colon = host.find(':'); colon != std::string::npos)
296 {port.assign(host, colon + 1, std::string::npos);
297 host.erase(colon);
298 }
299 return true;
300}

Referenced by Redirect().

Here is the caller graph for this function:

◆ Redirect()

XrdXrootdRedirHelper::Outcome XrdXrootdRedirHelper::Redirect ( const char * trg,
int & port,
XrdNetAddrInfo & clientAddr,
std::string & outTarget,
std::string & errMsg )
static

Run a redirect target through the redirect plugin.

Single entry point for both redirect forms; port selects which one, mirroring the calling convention of XrdXrootdProtocol::fsRedirPI():

  • port >= 0 : trg is a host[?cgi] target and port is its numeric port. The target is split internally (via splitHostCgi) and the plugin's Redirect() entry point is invoked; port is rewritten in place when the plugin asks for a different port.
  • port < 0 : trg is a full scheme://host[:port][/tail] URL. It is parsed internally (via ParseURL) and the plugin's RedirectURL() entry point is invoked. Here port doubles as the plugin's rdrOpts argument; because it is also the protocol's URL marker it is passed to the plugin but left unmodified on return. A URL that cannot be parsed is logged and yields Outcome::Unchanged without invoking the plugin.

Used by the XRootD protocol (fsRedirPI) for both forms and by the HTTP TPC handler (issue #2767), which only ever passes the host[?cgi] form.

Parameters
trgredirect target: host[?cgi] when port >= 0, or a scheme://host[:port][/tail] URL when port < 0. A nullptr is tolerated defensively and yields Outcome::Unchanged without invoking the plugin.
portin/out, doubling as the host-vs-URL selector; see the per-form description above.
clientAddrnetwork info of the client being redirected.
outTargetoutput: when Replaced, holds the new redirect target. Untouched otherwise.
errMsgoutput: when Error, holds the plugin's error message with the contract's leading '!' already stripped. Untouched otherwise.
Returns
Unchanged if no plugin is configured, the target's network address could not be resolved, a URL target could not be parsed, or the plugin returned an empty string. Replaced if the plugin returned a new target. Error if the plugin returned a fatal error.
Note
Thread-safe. Internal state is protected by a mutex; the caller may invoke this from any thread once Init() has returned.

Definition at line 199 of file XrdXrootdRedirHelper.cc.

202{
203 if (!gPlugin || !trg) return Outcome::Unchanged;
204
205 std::string pluginReply; // raw plugin reply, filled by the branch below
206 uint16_t hostPort = 0; // host form: the plugin's port (in/out)
207
208 if (port >= 0)
209 {// Host[?cgi] form: split the target and invoke the plugin's host+port
210 // Redirect() entry point, which may rewrite the port in place.
211 if (port > UINT16_MAX)
212 {if (gLog) gLog->Emsg("RedirPI", "Redirect port out of range -",
213 std::to_string(port).c_str());
214 return Outcome::Unchanged;
215 }
216 std::string host;
217 std::string cgi;
218 splitHostCgi(trg, host, cgi);
219
220 // The plugin call needs the target's resolved netaddr. If we cannot
221 // produce one the safest fallback is to leave the redirect alone
222 // (Unchanged) so the caller emits the original target unmodified.
223 THandle T;
224 T.Info = LookupTarget(host.c_str(), port);
225 if (!T.Info) return Outcome::Unchanged;
226
227 hostPort = static_cast<uint16_t>(port);
228 pluginReply = gPlugin->Redirect(host.c_str(), hostPort, cgi.c_str(),
229 T.Info->netAddr, clientAddr);
230 } else {
231 // URL form: parse scheme://host[:port][/tail] and invoke the plugin's
232 // RedirectURL() entry point. port doubles as the rdrOpts argument and
233 // is the protocol's URL marker, so it is passed in but left unmodified.
234 // A URL we cannot parse is not salvageable: skip the plugin and report
235 // Unchanged so the caller emits the original target unmodified.
236 std::string urlHead;
237 std::string host;
238 std::string urlPort;
239 std::string urlTail;
240 if (!ParseURL(trg, urlHead, host, urlPort, urlTail))
241 {if (gLog) gLog->Emsg("RedirPI", "Invalid redirect URL -", trg);
242 return Outcome::Unchanged;
243 }
244
245 // The cache is keyed by host alone (no scheme, no port suffix); pass
246 // -1 so the netaddr resolution does not bind to any specific port. A
247 // resolution failure falls back to Unchanged, as in the host form.
248 THandle T;
249 T.Info = LookupTarget(host.c_str(), -1);
250 if (!T.Info) return Outcome::Unchanged;
251
252 int rdrOpts = port;
253 pluginReply = gPlugin->RedirectURL(urlHead.c_str(), host.c_str(),
254 urlPort.c_str(), urlTail.c_str(),
255 rdrOpts, T.Info->netAddr,
256 clientAddr);
257 }
258
259 // Translate the plugin's "" / "<target>" / "!<msg>" return-string contract.
260 if (pluginReply.empty()) return Outcome::Unchanged;
261 if (pluginReply.front() == '!') { errMsg.assign(pluginReply, 1);
262 return Outcome::Error; }
263
264 // Replaced: commit the plugin's (possibly rewritten) port. Host form only;
265 // the URL form leaves port untouched as the protocol's URL marker.
266 if (port >= 0) port = hostPort;
267 outTarget = std::move(pluginReply);
268 return Outcome::Replaced;
269}
void splitHostCgi(std::string_view target, std::string &host, std::string &cgi)
static bool ParseURL(const char *url, std::string &urlHead, std::string &host, std::string &port, std::string &urlTail)

References XrdSysError::Emsg(), Error, ParseURL(), XrdXrootdRedirPI::Redirect(), XrdXrootdRedirPI::RedirectURL(), Replaced, splitHostCgi(), and Unchanged.

Here is the call graph for this function:

◆ SetClockForTesting()

void XrdXrootdRedirHelper::SetClockForTesting ( std::function< time_t()> nowFn)
static

====================== ONLY FOR TESTING, DO NOT USE ======================

Override the clock function used by the internal target-netaddr cache so unit tests can fast-forward past the ipHold expiry and exercise the refresh path of LookupTarget. Pass nullptr to restore the default clock (time(nullptr)).

Warning
This function exists solely to make XrdXrootdRedirHelper unit- testable. It must never be called from production code. Calling it from anywhere other than tests would replace the wall-clock with a fake clock for every redirect resolution in the whole process and silently break the cache TTL semantics.
Parameters
nowFnfunction returning a time_t to be used in place of time(nullptr). Pass an empty function to restore the default.

Definition at line 189 of file XrdXrootdRedirHelper.cc.

190{
191 gNow = std::move(nowFn);
192}

The documentation for this class was generated from the following files: