XRootD
XrdClMessageUtils.cc
Go to the documentation of this file.
1 //------------------------------------------------------------------------------
2 // Copyright (c) 2011-2014 by European Organization for Nuclear Research (CERN)
3 // Author: Lukasz Janyst <ljanyst@cern.ch>
4 //------------------------------------------------------------------------------
5 // This file is part of the XRootD software suite.
6 //
7 // XRootD is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Lesser General Public License as published by
9 // the Free Software Foundation, either version 3 of the License, or
10 // (at your option) any later version.
11 //
12 // XRootD is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public License
18 // along with XRootD. If not, see <http://www.gnu.org/licenses/>.
19 //
20 // In applying this licence, CERN does not waive the privileges and immunities
21 // granted to it by virtue of its status as an Intergovernmental Organization
22 // or submit itself to any jurisdiction.
23 //------------------------------------------------------------------------------
24 
26 #include "XrdCl/XrdClLog.hh"
27 #include "XrdCl/XrdClDefaultEnv.hh"
28 #include "XrdCl/XrdClConstants.hh"
29 #include "XrdCl/XrdClAnyObject.hh"
30 #include "XrdCl/XrdClSIDManager.hh"
31 #include "XrdCl/XrdClPostMaster.hh"
35 
36 #include "XProtocol/XProtocol.hh"
37 
38 namespace XrdCl
39 {
40  //----------------------------------------------------------------------------
41  // Send a message
42  //----------------------------------------------------------------------------
44  Message *msg,
45  ResponseHandler *handler,
46  MessageSendParams &sendParams,
47  LocalFileHandler *lFileHandler )
48  {
49  //--------------------------------------------------------------------------
50  // Get the stuff needed to send the message
51  //--------------------------------------------------------------------------
52  Log *log = DefaultEnv::GetLog();
53  PostMaster *postMaster = DefaultEnv::GetPostMaster();
54  XRootDStatus st;
55 
56  if( !postMaster )
58 
59  log->Dump( XRootDMsg, "[%s] Sending message %s",
60  url.GetHostId().c_str(), msg->GetObfuscatedDescription().c_str() );
61 
62  //--------------------------------------------------------------------------
63  // Get an instance of SID manager object
64  //--------------------------------------------------------------------------
65  std::shared_ptr<SIDManager> sidMgr( SIDMgrPool::Instance().GetSIDMgr( url ) );
67 
68  //--------------------------------------------------------------------------
69  // Allocate the SID and marshall the message
70  //--------------------------------------------------------------------------
71  st = sidMgr->AllocateSID( req->streamid );
72  if( !st.IsOK() )
73  {
74  log->Error( XRootDMsg, "[%s] Unable to allocate stream id",
75  url.GetHostId().c_str() );
76  return st;
77  }
78 
79  //--------------------------------------------------------------------------
80  // Make sure that in case of checkpoint xeq request the embedded request
81  // SID is matching
82  //--------------------------------------------------------------------------
83  if( req->requestid == kXR_chkpoint )
84  {
85  ClientRequest *r = (ClientRequest*)req;
86  if( r->chkpoint.opcode == kXR_ckpXeq )
87  {
88  ClientRequest *xeq = (ClientRequest*) msg->GetBuffer( sizeof( ClientChkPointRequest ) );
89  xeq->header.streamid[0] = req->streamid[0];
90  xeq->header.streamid[1] = req->streamid[1];
91  }
92  }
93 
95 
96  //--------------------------------------------------------------------------
97  // Create and set up the message handler
98  //--------------------------------------------------------------------------
99  XRootDMsgHandler *msgHandler;
100  msgHandler = new XRootDMsgHandler( msg, handler, &url, sidMgr, lFileHandler );
101  msgHandler->SetExpiration( sendParams.expires );
102  msgHandler->SetRedirectAsAnswer( !sendParams.followRedirects );
103  msgHandler->SetOksofarAsAnswer( sendParams.chunkedResponse );
104  msgHandler->SetChunkList( sendParams.chunkList );
105  msgHandler->SetKernelBuffer( sendParams.kbuff );
106  msgHandler->SetRedirectCounter( sendParams.redirectLimit );
107  msgHandler->SetStateful( sendParams.stateful );
108  msgHandler->SetCrc32cDigests( std::move( sendParams.crc32cDigests ) );
109 
110  if( sendParams.loadBalancer.url.IsValid() )
111  msgHandler->SetLoadBalancer( sendParams.loadBalancer );
112 
113  HostList *list = 0;
114  if( sendParams.hostList )
115  {
116  list = sendParams.hostList;
117  sendParams.hostList = nullptr;
118  }
119  else
120  list = new HostList();
121  list->push_back( url );
122  msgHandler->SetHostList( list );
123 
124  //--------------------------------------------------------------------------
125  // Send the message
126  //--------------------------------------------------------------------------
127  st = postMaster->Send( url, msg, msgHandler, sendParams.stateful,
128  sendParams.expires );
129  if( !st.IsOK() )
130  {
132  log->Error( XRootDMsg, "[%s] Unable to send the message %s: %s",
133  url.GetHostId().c_str(), msg->GetObfuscatedDescription().c_str(),
134  st.ToString().c_str() );
135 
136  // we need to reassign req as its current value might have been
137  // invalidated in the meanwhile due to a realloc
138  req = (ClientRequestHdr*)msg->GetBuffer();
139  // Release the SID as the request was never send
140  sidMgr->ReleaseSID( req->streamid );
141  delete msgHandler;
142  return st;
143  }
144  return XRootDStatus();
145  }
146 
147  //----------------------------------------------------------------------------
148  // Redirect a message
149  //----------------------------------------------------------------------------
151  Message *msg,
152  ResponseHandler *handler,
153  MessageSendParams &sendParams,
154  LocalFileHandler *lFileHandler )
155  {
156  //--------------------------------------------------------------------------
157  // Register a new virtual redirector
158  //--------------------------------------------------------------------------
160  Status st = registry.Register( url );
161  if( !st.IsOK() )
162  return st;
163 
164  //--------------------------------------------------------------------------
165  // Get the stuff needed to send the message
166  //--------------------------------------------------------------------------
167  Log *log = DefaultEnv::GetLog();
168  PostMaster *postMaster = DefaultEnv::GetPostMaster();
169 
170  if( !postMaster )
171  return Status( stError, errUninitialized );
172 
173  log->Dump( XRootDMsg, "[%s] Redirecting message %s",
174  url.GetHostId().c_str(), msg->GetObfuscatedDescription().c_str() );
175 
177 
178  //--------------------------------------------------------------------------
179  // Create and set up the message handler
180  //--------------------------------------------------------------------------
181  XRootDMsgHandler *msgHandler;
182  msgHandler = new XRootDMsgHandler( msg, handler, &url, std::shared_ptr<SIDManager>(), lFileHandler );
183  msgHandler->SetExpiration( sendParams.expires );
184  msgHandler->SetRedirectAsAnswer( !sendParams.followRedirects );
185  msgHandler->SetOksofarAsAnswer( sendParams.chunkedResponse );
186  msgHandler->SetChunkList( sendParams.chunkList );
187  msgHandler->SetRedirectCounter( sendParams.redirectLimit );
188  msgHandler->SetFollowMetalink( true );
189 
190  HostInfo info( url, true );
192  sendParams.loadBalancer = info;
193  msgHandler->SetLoadBalancer( info );
194 
195  HostList *list = 0;
196  list = new HostList();
197  list->push_back( info );
198  msgHandler->SetHostList( list );
199 
200  //--------------------------------------------------------------------------
201  // Redirect the message
202  //--------------------------------------------------------------------------
203  st = postMaster->Redirect( url, msg, msgHandler );
204  if( !st.IsOK() )
205  {
207  log->Error( XRootDMsg, "[%s] Unable to send the message %s: %s",
208  url.GetHostId().c_str(), msg->GetObfuscatedDescription().c_str(),
209  st.ToString().c_str() );
210  delete msgHandler;
211  delete list;
212  return st;
213  }
214  return Status();
215  }
216 
217  //----------------------------------------------------------------------------
218  // Process sending params
219  //----------------------------------------------------------------------------
221  {
222  //--------------------------------------------------------------------------
223  // Timeout
224  //--------------------------------------------------------------------------
225  Env *env = DefaultEnv::GetEnv();
226  if( sendParams.timeout == 0 )
227  {
228  int requestTimeout = DefaultRequestTimeout;
229  env->GetInt( "RequestTimeout", requestTimeout );
230  sendParams.timeout = requestTimeout;
231  }
232 
233  if( sendParams.expires == 0 )
234  sendParams.expires = ::time(0)+sendParams.timeout;
235 
236  //--------------------------------------------------------------------------
237  // Redirect limit
238  //--------------------------------------------------------------------------
239  if( sendParams.redirectLimit == 0 )
240  {
241  int redirectLimit = DefaultRedirectLimit;
242  env->GetInt( "RedirectLimit", redirectLimit );
243  sendParams.redirectLimit = redirectLimit;
244  }
245  }
246 
247  //----------------------------------------------------------------------------
249  //----------------------------------------------------------------------------
251  const URL::ParamsMap &newCgi,
252  bool replace,
253  const std::string &newPath,
254  std::string *opathp )
255  {
256  ClientRequest *req = (ClientRequest *)msg->GetBuffer();
257  switch( req->header.requestid )
258  {
259  case kXR_chmod:
260  case kXR_mkdir:
261  case kXR_mv:
262  case kXR_open:
263  case kXR_rm:
264  case kXR_rmdir:
265  case kXR_stat:
266  case kXR_truncate:
267  {
268  //----------------------------------------------------------------------
269  // Get the pointer to the appropriate path
270  //----------------------------------------------------------------------
271  char *path = msg->GetBuffer( 24 );
272  size_t length = req->header.dlen;
273  if( req->header.requestid == kXR_mv )
274  {
275  for( int i = 0; i < req->header.dlen; ++i, ++path, --length )
276  if( *path == ' ' )
277  break;
278  ++path;
279  --length;
280  }
281 
282  //----------------------------------------------------------------------
283  // Create a fake URL from an existing CGI
284  //----------------------------------------------------------------------
285  char *pathWithNull = new char[length+1];
286  memcpy( pathWithNull, path, length );
287  pathWithNull[length] = 0;
288  std::ostringstream o;
289  o << "fake://fake:111/" << pathWithNull;
290  delete [] pathWithNull;
291 
292  URL currentPath( o.str() );
293  URL::ParamsMap currentCgi = currentPath.GetParams();
294  MergeCGI( currentCgi, newCgi, replace );
295  currentPath.SetParams( currentCgi );
296  if( opathp )
297  *opathp = currentPath.GetPath();
298  if( !newPath.empty() )
299  currentPath.SetPath( newPath );
300  std::string newPathWitParams = currentPath.GetPathWithFilteredParams();
301 
302  //----------------------------------------------------------------------
303  // Write the path with the new cgi appended to the message
304  //----------------------------------------------------------------------
305  uint32_t newDlen = req->header.dlen - length + newPathWitParams.size();
306  msg->ReAllocate( 24+newDlen );
307  req = (ClientRequest *)msg->GetBuffer();
308  path = msg->GetBuffer( 24 );
309  if( req->header.requestid == kXR_mv )
310  {
311  for( int i = 0; i < req->header.dlen; ++i, ++path )
312  if( *path == ' ' )
313  break;
314  ++path;
315  }
316  memcpy( path, newPathWitParams.c_str(), newPathWitParams.size() );
317  req->header.dlen = newDlen;
318  break;
319  }
320  case kXR_locate:
321  {
322  Env *env = DefaultEnv::GetEnv();
323  int preserveLocateTried = DefaultPreserveLocateTried;
324  env->GetInt( "PreserveLocateTried", preserveLocateTried );
325 
326  if( !preserveLocateTried ) break;
327 
328  //----------------------------------------------------------------------
329  // In case of locate we only want to preserve tried/triedrc CGI info
330  //----------------------------------------------------------------------
331  URL::ParamsMap triedCgi;
332  URL::ParamsMap::const_iterator itr = newCgi.find( "triedrc" );
333  if( itr != newCgi.end() )
334  triedCgi[itr->first] = itr->second;
335  itr = newCgi.find( "tried" );
336  if( itr != newCgi.end() )
337  triedCgi[itr->first] = itr->second;
338 
339  //----------------------------------------------------------------------
340  // Is there anything to do?
341  //----------------------------------------------------------------------
342  if( triedCgi.empty() ) break;
343 
344  //----------------------------------------------------------------------
345  // Get the pointer to the appropriate path
346  //----------------------------------------------------------------------
347  char *path = msg->GetBuffer( 24 );
348  size_t length = req->header.dlen;
349 
350  //----------------------------------------------------------------------
351  // Create a fake URL from an existing CGI
352  //----------------------------------------------------------------------
353  std::string strpath( path, length );
354  std::ostringstream o;
355  o << "fake://fake:111/" << strpath;
356 
357  URL currentPath( o.str() );
358  URL::ParamsMap currentCgi = currentPath.GetParams();
359  MergeCGI( currentCgi, triedCgi, replace );
360  currentPath.SetParams( currentCgi );
361  std::string pathWitParams = currentPath.GetPathWithFilteredParams();
362 
363  //----------------------------------------------------------------------
364  // Write the path with the new cgi appended to the message
365  //----------------------------------------------------------------------
366  uint32_t newDlen = pathWitParams.size();
367  msg->ReAllocate( 24+newDlen );
368  req = (ClientRequest *)msg->GetBuffer();
369  path = msg->GetBuffer( 24 );
370  memcpy( path, pathWitParams.c_str(), pathWitParams.size() );
371  req->header.dlen = newDlen;
372  break;
373  }
374  }
376  }
377 
378  //------------------------------------------------------------------------
380  //------------------------------------------------------------------------
382  const URL::ParamsMap &cgi2,
383  bool replace )
384  {
385  URL::ParamsMap::const_iterator it;
386  for( it = cgi2.begin(); it != cgi2.end(); ++it )
387  {
388  if( replace || cgi1.find( it->first ) == cgi1.end() )
389  cgi1[it->first] = it->second;
390  else
391  {
392  std::string &v = cgi1[it->first];
393  if( v.empty() )
394  v = it->second;
395  else
396  {
397  v += ',';
398  v += it->second;
399  }
400  }
401  }
402  }
403 
404  //------------------------------------------------------------------------
406  //------------------------------------------------------------------------
407  Status MessageUtils::CreateXAttrVec( const std::vector<xattr_t> &attrs,
408  std::vector<char> &avec )
409  {
410  if( attrs.empty() )
411  return Status();
412 
413  if( attrs.size() > xfaLimits::kXR_faMaxVars )
414  return Status( stError, errInvalidArgs );
415 
416  //----------------------------------------------------------------------
417  // Calculate the name and value vector lengths
418  //----------------------------------------------------------------------
419 
420  // 2 bytes for rc + 1 byte for null character at the end
421  static const int name_overhead = 3;
422  // 4 bytes for value length
423  static const int value_overhead = 4;
424 
425  size_t nlen = 0, vlen = 0;
426  for( auto itr = attrs.begin(); itr != attrs.end(); ++itr )
427  {
428  nlen += std::get<xattr_name>( *itr ).size() + name_overhead;
429  vlen += std::get<xattr_value>( *itr ).size() + value_overhead;
430  }
431 
432  if( nlen > xfaLimits::kXR_faMaxNlen )
433  return Status( stError, errInvalidArgs );
434 
435  if( vlen > xfaLimits::kXR_faMaxVlen )
436  return Status( stError, errInvalidArgs );
437 
438  //----------------------------------------------------------------------
439  // Create name and value vectors
440  //----------------------------------------------------------------------
441  avec.resize( nlen + vlen, 0 );
442  char *nvec = avec.data(), *vvec = avec.data() + nlen;
443 
444  for( auto itr = attrs.begin(); itr != attrs.end(); ++itr )
445  {
446  const std::string &name = std::get<xattr_name>( *itr );
447  nvec = ClientFattrRequest::NVecInsert( name.c_str(), nvec );
448  const std::string &value = std::get<xattr_value>( *itr );
449  vvec = ClientFattrRequest::VVecInsert( value.c_str(), vvec );
450  }
451 
452  return Status();
453  }
454 
455  //------------------------------------------------------------------------
456  // Create xattr name vector vector
457  //------------------------------------------------------------------------
458  Status MessageUtils::CreateXAttrVec( const std::vector<std::string> &attrs,
459  std::vector<char> &nvec )
460  {
461  if( attrs.empty() )
462  return Status();
463 
464  if( attrs.size() > xfaLimits::kXR_faMaxVars )
465  return Status( stError, errInvalidArgs );
466 
467  //----------------------------------------------------------------------
468  // Calculate the name and value vector lengths
469  //----------------------------------------------------------------------
470 
471  // 2 bytes for rc + 1 byte for null character at the end
472  static const int name_overhead = 3;
473 
474  size_t nlen = 0;
475  for( auto itr = attrs.begin(); itr != attrs.end(); ++itr )
476  nlen += itr->size() + name_overhead;
477 
478  if( nlen > xfaLimits::kXR_faMaxNlen )
479  return Status( stError, errInvalidArgs );
480 
481  //----------------------------------------------------------------------
482  // Create name vector
483  //----------------------------------------------------------------------
484  nvec.resize( nlen, 0 );
485  char *nptr = nvec.data();
486 
487  for( auto itr = attrs.begin(); itr != attrs.end(); ++itr )
488  nptr = ClientFattrRequest::NVecInsert( itr->c_str(), nptr );
489 
490  return Status();
491  }
492 }
#define kXR_isManager
Definition: XProtocol.hh:1198
#define kXR_attrMeta
Definition: XProtocol.hh:1201
kXR_char streamid[2]
Definition: XProtocol.hh:158
static const int kXR_ckpXeq
Definition: XProtocol.hh:218
struct ClientRequestHdr header
Definition: XProtocol.hh:887
kXR_unt16 requestid
Definition: XProtocol.hh:159
@ kXR_open
Definition: XProtocol.hh:123
@ kXR_mkdir
Definition: XProtocol.hh:121
@ kXR_chmod
Definition: XProtocol.hh:115
@ kXR_rm
Definition: XProtocol.hh:127
@ kXR_rmdir
Definition: XProtocol.hh:128
@ kXR_truncate
Definition: XProtocol.hh:141
@ kXR_mv
Definition: XProtocol.hh:122
@ kXR_stat
Definition: XProtocol.hh:130
@ kXR_chkpoint
Definition: XProtocol.hh:125
@ kXR_locate
Definition: XProtocol.hh:140
#define kXR_attrVirtRdr
Definition: XProtocol.hh:1204
@ kXR_faMaxVars
Definition: XProtocol.hh:310
@ kXR_faMaxVlen
Definition: XProtocol.hh:312
@ kXR_faMaxNlen
Definition: XProtocol.hh:311
struct ClientChkPointRequest chkpoint
Definition: XProtocol.hh:890
kXR_int32 dlen
Definition: XProtocol.hh:161
const char * GetBuffer(uint32_t offset=0) const
Get the message buffer.
Definition: XrdClBuffer.hh:72
void ReAllocate(uint32_t size)
Reallocate the buffer to a new location of a given size.
Definition: XrdClBuffer.hh:88
static Log * GetLog()
Get default log.
static PostMaster * GetPostMaster()
Get default post master.
static Env * GetEnv()
Get default client environment.
bool GetInt(const std::string &key, int &value)
Definition: XrdClEnv.cc:115
Handle diagnostics.
Definition: XrdClLog.hh:101
void Error(uint64_t topic, const char *format,...)
Report an error.
Definition: XrdClLog.cc:231
void Dump(uint64_t topic, const char *format,...)
Print a dump message.
Definition: XrdClLog.cc:299
static void MergeCGI(URL::ParamsMap &cgi1, const URL::ParamsMap &cgi2, bool replace)
Merge cgi2 into cgi1.
static Status CreateXAttrVec(const std::vector< xattr_t > &attrs, std::vector< char > &avec)
Create xattr vector.
static void ProcessSendParams(MessageSendParams &sendParams)
Process sending params.
static void RewriteCGIAndPath(Message *msg, const URL::ParamsMap &newCgi, bool replace, const std::string &newPath, std::string *opathp=nullptr)
Append cgi to the one already present in the message.
static Status RedirectMessage(const URL &url, Message *msg, ResponseHandler *handler, MessageSendParams &sendParams, LocalFileHandler *lFileHandler)
Redirect message.
static XRootDStatus SendMessage(const URL &url, Message *msg, ResponseHandler *handler, MessageSendParams &sendParams, LocalFileHandler *lFileHandler)
Send message.
The message representation used throughout the system.
Definition: XrdClMessage.hh:32
const std::string & GetObfuscatedDescription() const
Get the description of the message with authz parameter obfuscated.
A hub for dispatching and receiving messages.
XRootDStatus Send(const URL &url, Message *msg, MsgHandler *handler, bool stateful, time_t expires)
Status Redirect(const URL &url, Message *msg, MsgHandler *handler)
Singleton access to URL to virtual redirector mapping.
static RedirectorRegistry & Instance()
Returns reference to the single instance.
XRootDStatus Register(const URL &url)
Creates a new virtual redirector and registers it (async).
Handle an async response.
static SIDMgrPool & Instance()
URL representation.
Definition: XrdClURL.hh:31
std::string GetHostId() const
Get the host part of the URL (user:password@host:port)
Definition: XrdClURL.hh:99
std::map< std::string, std::string > ParamsMap
Definition: XrdClURL.hh:33
void SetParams(const std::string &params)
Set params.
Definition: XrdClURL.cc:402
std::string GetPathWithFilteredParams() const
Get the path with params, filteres out 'xrdcl.'.
Definition: XrdClURL.cc:331
void SetPath(const std::string &path)
Set the path.
Definition: XrdClURL.hh:225
const ParamsMap & GetParams() const
Get the URL params.
Definition: XrdClURL.hh:244
const std::string & GetPath() const
Get the path.
Definition: XrdClURL.hh:217
bool IsValid() const
Is the url valid.
Definition: XrdClURL.cc:452
Handle/Process/Forward XRootD messages.
void SetRedirectCounter(uint16_t redirectCounter)
Set the redirect counter.
void SetFollowMetalink(bool followMetalink)
void SetChunkList(ChunkList *chunkList)
Set the chunk list.
void SetHostList(HostList *hostList)
Set host list.
void SetCrc32cDigests(std::vector< uint32_t > &&crc32cDigests)
void SetLoadBalancer(const HostInfo &loadBalancer)
Set the load balancer.
void SetStateful(bool stateful)
void SetOksofarAsAnswer(bool oksofarAsAnswer)
void SetKernelBuffer(XrdSys::KernelBuffer *kbuff)
Set the kernel buffer.
void SetExpiration(time_t expiration)
Set a timestamp after which we give up.
void SetRedirectAsAnswer(bool redirectAsAnswer)
static void SetDescription(Message *msg)
Get the description of a message.
static XRootDStatus UnMarshallRequest(Message *msg)
static XRootDStatus MarshallRequest(Message *msg)
Marshal the outgoing message.
const int DefaultPreserveLocateTried
const int DefaultRedirectLimit
const uint16_t errUninitialized
Definition: XrdClStatus.hh:60
const uint16_t stError
An error occurred that could potentially be retried.
Definition: XrdClStatus.hh:32
const uint64_t XRootDMsg
std::vector< HostInfo > HostList
const uint16_t errInvalidArgs
Definition: XrdClStatus.hh:58
const int DefaultRequestTimeout
static char * VVecInsert(const char *value, char *buffer)
Definition: XProtocol.cc:192
static char * NVecInsert(const char *name, char *buffer)
Definition: XProtocol.cc:176
URL url
URL of the host.
uint32_t flags
Host type.
std::vector< uint32_t > crc32cDigests
XrdSys::KernelBuffer * kbuff
Procedure execution status.
Definition: XrdClStatus.hh:115
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124
std::string ToString() const
Create a string representation.
Definition: XrdClStatus.cc:97