libzypp  17.35.8
PluginFrame.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 #include <iostream>
13 #include <utility>
16 #include <zypp-core/zyppng/core/String>
17 
18 using std::endl;
19 
20 #undef ZYPP_BASE_LOGGER_LOGGROUP
21 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::plugin"
22 
24 namespace zypp
25 {
26 
28  //
29  // CLASS NAME : PluginFrame::Impl
30  //
33  {
34  public:
35  Impl()
36  {}
37 
38  Impl( const std::string & command_r )
39  { setCommand( command_r ); }
40 
41  Impl( const std::string & command_r, ByteArray &&body_r )
42  : _body(std::move( body_r ))
43  { setCommand( command_r ); }
44 
45  Impl( const std::string & command_r, HeaderInitializerList contents_r )
46  { setCommand( command_r ); addHeader( contents_r ); }
47 
48  Impl( const std::string & command_r, ByteArray &&body_r, HeaderInitializerList contents_r )
49  : _body(std::move( body_r ))
50  { setCommand( command_r ); addHeader( contents_r ); }
51 
52  Impl( std::istream & stream_r );
53 
54  public:
55  bool empty() const
56  { return _command.empty() && _body.empty(); }
57 
58  const std::string & command() const
59  { return _command; }
60 
61  void setCommand( const std::string & command_r )
62  {
63  if ( command_r.find( '\n' ) != std::string::npos )
64  ZYPP_THROW( PluginFrameException( "Multiline command", command_r ) );
65  _command = command_r;
66  }
67 
68  const ByteArray & body() const
69  { return _body; }
70 
72  { return _body; }
73 
74  void setBody( ByteArray && body_r )
75  { _body = std::move(body_r); }
76 
77  static std::string escapeHeader( std::string_view val ) {
78 
79  std::string escaped;
80  /*
81  Escape rules from the STOMP spec:
82  \r (octet 92 and 114) translates to carriage return (octet 13)
83  \n (octet 92 and 110) translates to line feed (octet 10)
84  \c (octet 92 and 99) translates to : (octet 58)
85  \\ (octet 92 and 92) translates to \ (octet 92)
86  Undefined escape sequences such as \t (octet 92 and 116) MUST be treated as a fatal protocol error.
87  */
88  for ( auto c = val.begin (); c!= val.end(); c++ ) {
89  switch( *c ) {
90  case '\n': {
91  escaped.push_back('\\');
92  escaped.push_back('n');
93  break;
94  }
95  case '\r': {
96  escaped.push_back('\\');
97  escaped.push_back('r');
98  break;
99  }
100  case '\\': {
101  escaped.push_back('\\');
102  escaped.push_back('\\');
103  break;
104  }
105  case ':': {
106  escaped.push_back('\\');
107  escaped.push_back('c');
108  break;
109  }
110  default:
111  escaped.push_back (*c);
112  break;
113  }
114  }
115  return escaped;
116  }
117 
118  static std::string unescapeHeader( std::string_view val ) {
119  std::string unescaped;
120  for ( auto c = val.begin (); c!= val.end(); ) {
121  if ( *c != '\\' ) {
122  unescaped.push_back (*c);
123  c++;
124  continue;
125  }
126 
127  c++;
128  if ( c == val.end() )
129  ZYPP_THROW( PluginFrameException( "Invalid start of escape sequence" ) );
130 
131  switch ( *c ) {
132  case 'n': {
133  unescaped.push_back('\n');
134  c++;
135  break;
136  }
137  case 'r': {
138  unescaped.push_back('\r');
139  c++;
140  break;
141  }
142  case '\\': {
143  unescaped.push_back('\\');
144  c++;
145  break;
146  }
147  case 'c': {
148  unescaped.push_back(':');
149  c++;
150  break;
151  }
152  default:
153  ZYPP_THROW( PluginFrameException( "Unknown escape sequence" ) );
154  break;
155  }
156  }
157  return unescaped;
158  }
159 
160  public:
161  using constKeyRange = std::pair<HeaderListIterator, HeaderListIterator>;
162  using KeyRange = std::pair<HeaderList::iterator, HeaderList::iterator>;
163 
165  { return _header; }
166 
167  const HeaderList & headerList() const
168  { return _header; }
169 
170  const std::string & getHeader( const std::string & key_r ) const
171  {
172  constKeyRange r( _header.equal_range( key_r ) );
173  if ( r.first == r.second )
174  ZYPP_THROW( PluginFrameException( "No value for key", key_r ) );
175  const std::string & ret( r.first->second );
176  if ( ++r.first != r.second )
177  ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
178  return ret;
179  }
180 
181  const std::string & getHeader( const std::string & key_r, const std::string & default_r ) const
182  {
183  constKeyRange r( _header.equal_range( key_r ) );
184  if ( r.first == r.second )
185  return default_r;
186  const std::string & ret( r.first->second );
187  if ( ++r.first != r.second )
188  ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
189  return ret;
190  }
191 
192  const std::string & getHeaderNT( const std::string & key_r, const std::string & default_r ) const
193  {
194  HeaderListIterator iter( _header.find( key_r ) );
195  return iter != _header.end() ? iter->second : default_r;
196  }
197 
198  HeaderList::value_type mkHeaderPair( const std::string & key_r, const std::string & value_r )
199  {
200  return HeaderList::value_type( key_r, value_r );
201  }
202 
203  void setHeader( const std::string & key_r, const std::string & value_r )
204  {
205  clearHeader( key_r );
206  addHeader( key_r, value_r );
207  }
208 
209  void addHeader( const std::string & key_r, const std::string & value_r )
210  {
211  _header.insert( mkHeaderPair( key_r, value_r ) );
212  }
213 
214  void addHeader( HeaderInitializerList contents_r )
215  {
216  for ( const auto & el : contents_r )
217  addHeader( el.first, el.second );
218  }
219 
220  void addRawHeader ( const std::string_view data )
221  {
222  std::string::size_type sep( data.find( ':') );
223  if ( sep == std::string::npos )
224  ZYPP_THROW( PluginFrameException( "Missing colon in header" ) );
225 
226  _header.insert( HeaderList::value_type( unescapeHeader(data.substr(0,sep)), unescapeHeader(data.substr(sep+1)) ) );
227  }
228 
229  void clearHeader( const std::string & key_r )
230  {
231  _header.erase( key_r );
232  }
233 
234  public:
235  std::ostream & writeTo( std::ostream & stream_r ) const;
236 
237  private:
238  std::string _command;
241 
242  public:
244  static shared_ptr<Impl> nullimpl()
245  {
246  static shared_ptr<Impl> _nullimpl( new Impl );
247  return _nullimpl;
248  }
249  private:
250  friend Impl * rwcowClone<Impl>( const Impl * rhs );
252  Impl * clone() const
253  { return new Impl( *this ); }
254  };
256 
258  inline std::ostream & operator<<( std::ostream & str, const PluginFrame::Impl & obj )
259  {
260  return str << "PluginFrame[" << obj.command() << "](" << obj.headerList().size() << "){" << obj.body().size() << "}";
261  }
262 
263  PluginFrame::Impl::Impl( std::istream & stream_r )
264  {
265  // ATTENTION: Remember to also update the parser logic in zypp-core/zyppng/rpc/stompframestream.cc
266  // if code here is changed or features are added.
267 
268  //DBG << "Parse from " << stream_r << endl;
269  if ( ! stream_r )
270  ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
271 
272  // JFYI: stream status after getline():
273  // Bool | Bits
274  // ------|---------------
275  // true | [g___] >FOO< : FOO line was \n-terminated
276  // true | [_e__] >BAA< : BAA before EOF, but not \n-terminated
277  // false | [_eF_] >< : No valid data to consume
278 
279  //command
280  _command = str::getline( stream_r );
281  if ( ! stream_r.good() )
282  ZYPP_THROW( PluginFrameException( "Missing NL after command" ) );
283 
284  // header
285  do {
286  std::string data = str::getline( stream_r );
287  if ( ! stream_r.good() )
288  ZYPP_THROW( PluginFrameException( "Missing NL after header" ) );
289 
290  if ( data.empty() )
291  break; // --> empty line sep. header and body
292 
293  addRawHeader( data );
294 
295  } while ( true );
296 
297 
298  // check for content-length header
299  std::optional<uint64_t> cLen;
300  {
301  const auto &contentLen = getHeaderNT( zypp::PluginFrame::contentLengthHeader(), std::string() );
302  if ( !contentLen.empty() ) {
303  cLen = zyppng::str::safe_strtonum<uint64_t>(contentLen);
304  if ( !cLen ) {
305  ERR << "Received malformed message from peer: Invalid value for " << zypp::PluginFrame::contentLengthHeader() << ":" << contentLen << std::endl;
306  ZYPP_THROW( PluginFrameException( "Invalid value for content-length." ) );
307  }
308 
309  // do not keep the header, we regenerate it again when writing the frame anyway
311  }
312 
313  }
314 
315  // data
316  if ( cLen ) {
317  _body.resize ( (*cLen)+1, '\0' );
318  stream_r.read ( _body.data(), (*cLen)+1 );
319 
320  if ( ! stream_r.good() )
321  ZYPP_THROW( PluginFrameException( "Missing data in stream" ) );
322  if ( _body.back() != '\0' )
323  ZYPP_THROW( PluginFrameException( "Missing NUL after body" ) );
324 
325  _body.pop_back (); // get rid of \0
326 
327  } else {
328  const auto &data = str::receiveUpTo( stream_r, '\0' );
329  _body = ByteArray( data.c_str(), data.size() );
330  if ( ! stream_r.good() )
331  ZYPP_THROW( PluginFrameException( "Missing NUL after body" ) );
332  }
333  }
334 
335  std::ostream & PluginFrame::Impl::writeTo( std::ostream & stream_r ) const
336  {
337  //DBG << "Write " << *this << " to " << stream_r << endl;
338  if ( ! stream_r )
339  ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
340 
341  // command
342  stream_r << _command << "\n";
343 
344  // STOMP recommends sending a content-length header
345  stream_r << contentLengthHeader() << ':' << str::numstring( _body.size() ) << "\n";
346 
347  // header
348  for_( it, _header.begin(), _header.end() )
349  stream_r << escapeHeader(it->first) << ':' << escapeHeader(it->second) << "\n";
350 
351  // header end
352  stream_r << "\n";
353 
354  // body
355  stream_r.write( _body.data(), _body.size() );
356 
357  // body end
358  stream_r << '\0';
359  stream_r.flush();
360 
361  if ( ! stream_r )
362  ZYPP_THROW( PluginFrameException( "Write error" ) );
363  return stream_r;
364  }
365 
367  //
368  // CLASS NAME : PluginFrame
369  //
371 
372  const std::string & PluginFrame::ackCommand()
373  {
374  static std::string _val( "ACK" );
375  return _val;
376  }
377 
378  const std::string & PluginFrame::errorCommand()
379  {
380  static std::string _val( "ERROR" );
381  return _val;
382  }
383 
384  const std::string & PluginFrame::enomethodCommand()
385  {
386  static std::string _val( "_ENOMETHOD" );
387  return _val;
388  }
389 
390  const std::string &PluginFrame::contentLengthHeader()
391  {
392  static std::string _val("content-length");
393  return _val;
394  }
395 
397  : _pimpl( Impl::nullimpl() )
398  {}
399 
400  PluginFrame::PluginFrame( const std::string & command_r )
401  : _pimpl( new Impl( command_r ) )
402  {}
403 
404  PluginFrame::PluginFrame(const std::string & command_r, std::string body_r )
405  : _pimpl( new Impl( command_r, ByteArray(body_r) ) )
406  {}
407 
408  PluginFrame::PluginFrame(const std::string & command_r, ByteArray body_r )
409  : _pimpl( new Impl( command_r, std::move(body_r) ) )
410  {}
411 
412  PluginFrame::PluginFrame( const std::string & command_r, HeaderInitializerList contents_r )
413  : _pimpl( new Impl( command_r, contents_r ) )
414  {}
415 
416  PluginFrame::PluginFrame(const std::string & command_r, ByteArray body_r, HeaderInitializerList contents_r )
417  : _pimpl( new Impl( command_r, std::move(body_r), contents_r ) )
418  {}
419 
420  PluginFrame::PluginFrame( std::istream & stream_r )
421  : _pimpl( new Impl( stream_r ) )
422  {}
423 
424  bool PluginFrame::empty() const
425  { return _pimpl->empty(); }
426 
427  const std::string & PluginFrame::command() const
428  { return _pimpl->command(); }
429 
430  void PluginFrame::setCommand( const std::string & command_r )
431  { _pimpl->setCommand( command_r ); }
432 
434  { return _pimpl->body(); }
435 
437  { return _pimpl->bodyRef(); }
438 
439  void PluginFrame::setBody( const std::string & body_r )
440  { _pimpl->setBody( ByteArray(body_r.data(), body_r.size()) ); }
441 
442  void PluginFrame::setBody( const ByteArray & body_r )
443  { _pimpl->setBody( ByteArray(body_r) ); }
444 
445  void PluginFrame::setBody( ByteArray && body_r )
446  { _pimpl->setBody( std::move(body_r) ); }
447 
448  std::ostream & PluginFrame::writeTo( std::ostream & stream_r ) const
449  { return _pimpl->writeTo( stream_r ); }
450 
452  { return _pimpl->headerList(); }
453 
455  { return _pimpl->headerList(); }
456 
457  const std::string & PluginFrame::getHeader( const std::string & key_r ) const
458  { return _pimpl->getHeader( key_r ); }
459 
460  const std::string & PluginFrame::getHeader( const std::string & key_r, const std::string & default_r ) const
461  { return _pimpl->getHeader( key_r, default_r ); }
462 
463  const std::string & PluginFrame::getHeaderNT( const std::string & key_r, const std::string & default_r ) const
464  { return _pimpl->getHeaderNT( key_r, default_r ); }
465 
466  void PluginFrame::setHeader( const std::string & key_r, const std::string & value_r )
467  { _pimpl->setHeader( key_r, value_r ); }
468 
469  void PluginFrame::addHeader( const std::string & key_r, const std::string & value_r )
470  { _pimpl->addHeader( key_r, value_r ); }
471 
473  { _pimpl->addHeader( contents_r ); }
474 
475  void PluginFrame::addRawHeader( const ByteArray &header )
476  {
477  _pimpl->addRawHeader( header.asStringView() );
478  }
479 
480  void PluginFrame::clearHeader( const std::string & key_r )
481  { _pimpl->clearHeader( key_r ); }
482 
484 
485  std::ostream & operator<<( std::ostream & str, const PluginFrame & obj )
486  { return str << *obj._pimpl; }
487 
488  bool operator==( const PluginFrame & lhs, const PluginFrame & rhs )
489  {
490  return ( lhs._pimpl == rhs._pimpl )
491  || (( lhs.command() == rhs.command() ) && ( lhs.headerList() == rhs.headerList() ) && ( lhs.body() == rhs.body() ));
492  }
493 
495 } // namespace zypp
Impl(const std::string &command_r, ByteArray &&body_r)
Definition: PluginFrame.cc:41
std::ostream & writeTo(std::ostream &stream_r) const
Write frame to stream.
Definition: PluginFrame.cc:448
PluginFrame()
Default ctor (empty frame)
Definition: PluginFrame.cc:396
ByteArray & bodyRef()
Definition: PluginFrame.cc:71
void addRawHeader(const ByteArray &header)
Definition: PluginFrame.cc:475
static shared_ptr< Impl > nullimpl()
Offer default Impl.
Definition: PluginFrame.cc:244
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:479
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition: Exception.h:424
std::pair< HeaderListIterator, HeaderListIterator > constKeyRange
Definition: PluginFrame.cc:161
Command frame for communication with PluginScript.
Definition: PluginFrame.h:41
void setCommand(const std::string &command_r)
Set the frame command.
Definition: PluginFrame.cc:430
const std::string & command() const
Return the frame command.
Definition: PluginFrame.cc:427
const ByteArray & body() const
Return the frame body.
Definition: PluginFrame.cc:433
std::multimap< std::string, std::string > HeaderList
The header list.
Definition: PluginFrame.h:149
void clearHeader(const std::string &key_r)
Definition: PluginFrame.cc:229
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
const std::string & getHeaderNT(const std::string &key_r, const std::string &default_r=std::string()) const
Not throwing version returing one of the matching header values or default_r string.
Definition: PluginFrame.cc:463
String related utilities and Regular expression matching.
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
Definition: SerialNumber.cc:52
bool empty() const
Whether this is an empty frame.
Definition: PluginFrame.cc:424
Definition: Arch.h:363
Impl(const std::string &command_r)
Definition: PluginFrame.cc:38
ByteArray & bodyRef()
Return a reference to the frame body.
Definition: PluginFrame.cc:436
Impl(const std::string &command_r, ByteArray &&body_r, HeaderInitializerList contents_r)
Definition: PluginFrame.cc:48
void addHeader(const std::string &key_r, const std::string &value_r=std::string())
Add header for key_r leaving already existing headers for key_r unchanged.
Definition: PluginFrame.cc:469
bool operator==(const SetRelation::Enum &lhs, const SetCompare &rhs)
static const std::string & errorCommand()
"ERROR" command.
Definition: PluginFrame.cc:378
void setCommand(const std::string &command_r)
Definition: PluginFrame.cc:61
void addRawHeader(const std::string_view data)
Definition: PluginFrame.cc:220
const std::string & getHeader(const std::string &key_r, const std::string &default_r) const
Definition: PluginFrame.cc:181
#define ERR
Definition: Logger.h:100
std::ostream & writeTo(std::ostream &stream_r) const
Definition: PluginFrame.cc:335
std::pair< HeaderList::iterator, HeaderList::iterator > KeyRange
Definition: PluginFrame.cc:162
void setBody(ByteArray &&body_r)
Definition: PluginFrame.cc:74
static const std::string & enomethodCommand()
"_ENOMETHOD" command.
Definition: PluginFrame.cc:384
static std::string unescapeHeader(std::string_view val)
Definition: PluginFrame.cc:118
void addHeader(HeaderInitializerList contents_r)
Definition: PluginFrame.cc:214
const ByteArray & body() const
Definition: PluginFrame.cc:68
Base class for PluginFrame Exception.
Impl(const std::string &command_r, HeaderInitializerList contents_r)
Definition: PluginFrame.cc:45
static const std::string & ackCommand()
"ACK" command.
Definition: PluginFrame.cc:372
RWCOW_pointer< Impl > _pimpl
Pointer to implementation.
Definition: PluginFrame.h:270
std::string numstring(char n, int w=0)
Definition: String.h:289
void setHeader(const std::string &key_r, const std::string &value_r)
Definition: PluginFrame.cc:203
HeaderList & headerList()
Definition: PluginFrame.cc:164
void setBody(const std::string &body_r)
Set the frame body.
Definition: PluginFrame.cc:439
Impl * clone() const
clone for RWCOW_pointer
Definition: PluginFrame.cc:252
HeaderList::value_type mkHeaderPair(const std::string &key_r, const std::string &value_r)
Definition: PluginFrame.cc:198
const std::string & getHeaderNT(const std::string &key_r, const std::string &default_r) const
Definition: PluginFrame.cc:192
void addHeader(const std::string &key_r, const std::string &value_r)
Definition: PluginFrame.cc:209
const std::initializer_list< std::pair< std::string, std::string > > & HeaderInitializerList
Definition: PluginFrame.h:46
const std::string & getHeader(const std::string &key_r) const
Definition: PluginFrame.cc:170
std::string receiveUpTo(std::istream &str, const char delim_r, bool returnDelim_r)
Return stream content up to the next ocurrence of delim_r or EOF delim_r, if found, is always read from the stream.
Definition: String.cc:489
HeaderList::const_iterator HeaderListIterator
Header list iterator.
Definition: PluginFrame.h:152
void setHeader(const std::string &key_r, const std::string &value_r=std::string())
Set header for key_r removing all other occurrences of key_r.
Definition: PluginFrame.cc:466
HeaderList & headerList()
Modifyalble header list for internal use only.
Definition: PluginFrame.cc:451
const std::string & getHeader(const std::string &key_r) const
Return header value for key_r.
Definition: PluginFrame.cc:457
const HeaderList & headerList() const
Definition: PluginFrame.cc:167
std::ostream & operator<<(std::ostream &str, const PluginFrame::Impl &obj)
Definition: PluginFrame.cc:258
PluginFrame implementation.
Definition: PluginFrame.cc:32
Easy-to use interface to the ZYPP dependency resolver.
Definition: Application.cc:19
SolvableIdType size_type
Definition: PoolMember.h:126
void clearHeader(const std::string &key_r)
Remove all headers for key_r.
Definition: PluginFrame.cc:480
const std::string & command() const
Definition: PluginFrame.cc:58
static const std::string & contentLengthHeader()
"content-lenght" header name
Definition: PluginFrame.cc:390
zypp::ByteArray ByteArray
Definition: bytearray.h:21
static std::string escapeHeader(std::string_view val)
Definition: PluginFrame.cc:77