libzypp 17.37.17
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
18using std::endl;
19
20#undef ZYPP_BASE_LOGGER_LOGGROUP
21#define ZYPP_BASE_LOGGER_LOGGROUP "zypp::plugin"
22
24namespace zypp
25{
26
28 //
29 // CLASS NAME : PluginFrame::Impl
30 //
33 {
34 public:
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, bool escapeColon=true ) {
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 bsc#1231043: We do allow a literal ":" in the header value(!) in order to stay
89 compatible with with plugin implementations (like zypp-plugin) which are not
90 prepared to unescape a ":" there. And in fact it should not be necessary because
91 the 1st colon separates header and value.
92 */
93 for ( auto c = val.begin (); c!= val.end(); c++ ) {
94 switch( *c ) {
95 case '\n': {
96 escaped.push_back('\\');
97 escaped.push_back('n');
98 break;
99 }
100 case '\r': {
101 escaped.push_back('\\');
102 escaped.push_back('r');
103 break;
104 }
105 case '\\': {
106 escaped.push_back('\\');
107 escaped.push_back('\\');
108 break;
109 }
110 case ':': {
111 if ( escapeColon ) {
112 escaped.push_back('\\');
113 escaped.push_back('c');
114 break;
115 } else
116 [[fallthrough]];
117 }
118 default:
119 escaped.push_back (*c);
120 break;
121 }
122 }
123 return escaped;
124 }
125
126 static std::string unescapeHeader( std::string_view val ) {
127 std::string unescaped;
128 for ( auto c = val.begin (); c!= val.end(); ) {
129 if ( *c != '\\' ) {
130 unescaped.push_back (*c);
131 c++;
132 continue;
133 }
134
135 c++;
136 if ( c == val.end() )
137 ZYPP_THROW( PluginFrameException( "Invalid start of escape sequence" ) );
138
139 switch ( *c ) {
140 case 'n': {
141 unescaped.push_back('\n');
142 c++;
143 break;
144 }
145 case 'r': {
146 unescaped.push_back('\r');
147 c++;
148 break;
149 }
150 case '\\': {
151 unescaped.push_back('\\');
152 c++;
153 break;
154 }
155 case 'c': {
156 unescaped.push_back(':');
157 c++;
158 break;
159 }
160 default:
161 ZYPP_THROW( PluginFrameException( "Unknown escape sequence" ) );
162 break;
163 }
164 }
165 return unescaped;
166 }
167
168 public:
169 using constKeyRange = std::pair<HeaderListIterator, HeaderListIterator>;
170 using KeyRange = std::pair<HeaderList::iterator, HeaderList::iterator>;
171
173 { return _header; }
174
175 const HeaderList & headerList() const
176 { return _header; }
177
178 const std::string & getHeader( const std::string & key_r ) const
179 {
180 constKeyRange r( _header.equal_range( key_r ) );
181 if ( r.first == r.second )
182 ZYPP_THROW( PluginFrameException( "No value for key", key_r ) );
183 const std::string & ret( r.first->second );
184 if ( ++r.first != r.second )
185 ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
186 return ret;
187 }
188
189 const std::string & getHeader( const std::string & key_r, const std::string & default_r ) const
190 {
191 constKeyRange r( _header.equal_range( key_r ) );
192 if ( r.first == r.second )
193 return default_r;
194 const std::string & ret( r.first->second );
195 if ( ++r.first != r.second )
196 ZYPP_THROW( PluginFrameException( "Multiple values for key", key_r ) );
197 return ret;
198 }
199
200 const std::string & getHeaderNT( const std::string & key_r, const std::string & default_r ) const
201 {
202 HeaderListIterator iter( _header.find( key_r ) );
203 return iter != _header.end() ? iter->second : default_r;
204 }
205
206 HeaderList::value_type mkHeaderPair( const std::string & key_r, const std::string & value_r )
207 {
208 return HeaderList::value_type( key_r, value_r );
209 }
210
211 void setHeader( const std::string & key_r, const std::string & value_r )
212 {
213 clearHeader( key_r );
214 addHeader( key_r, value_r );
215 }
216
217 void addHeader( const std::string & key_r, const std::string & value_r )
218 {
219 _header.insert( mkHeaderPair( key_r, value_r ) );
220 }
221
223 {
224 for ( const auto & el : contents_r )
225 addHeader( el.first, el.second );
226 }
227
228 void addRawHeader ( const std::string_view data )
229 {
230 std::string::size_type sep( data.find( ':') );
231 if ( sep == std::string::npos )
232 ZYPP_THROW( PluginFrameException( "Missing colon in header" ) );
233
234 _header.insert( HeaderList::value_type( unescapeHeader(data.substr(0,sep)), unescapeHeader(data.substr(sep+1)) ) );
235 }
236
237 void clearHeader( const std::string & key_r )
238 {
239 _header.erase( key_r );
240 }
241
242 public:
243 std::ostream & writeTo( std::ostream & stream_r ) const;
244
245 private:
246 std::string _command;
249
250 public:
253 {
254 static shared_ptr<Impl> _nullimpl( new Impl );
255 return _nullimpl;
256 }
257 private:
258 friend Impl * rwcowClone<Impl>( const Impl * rhs );
260 Impl * clone() const
261 { return new Impl( *this ); }
262 };
263
264
266 inline std::ostream & operator<<( std::ostream & str, const PluginFrame::Impl & obj )
267 {
268 return str << "PluginFrame[" << obj.command() << "](" << obj.headerList().size() << "){" << obj.body().size() << "}";
269 }
270
271 PluginFrame::Impl::Impl( std::istream & stream_r )
272 {
273 // ATTENTION: Remember to also update the parser logic in zypp-core/zyppng/rpc/stompframestream.cc
274 // if code here is changed or features are added.
275
276 //DBG << "Parse from " << stream_r << endl;
277 if ( ! stream_r )
278 ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
279
280 // JFYI: stream status after getline():
281 // Bool | Bits
282 // ------|---------------
283 // true | [g___] >FOO< : FOO line was \n-terminated
284 // true | [_e__] >BAA< : BAA before EOF, but not \n-terminated
285 // false | [_eF_] >< : No valid data to consume
286
287 //command
288 _command = str::getline( stream_r );
289 if ( ! stream_r.good() )
290 ZYPP_THROW( PluginFrameException( "Missing NL after command" ) );
291
292 // header
293 do {
294 std::string data = str::getline( stream_r );
295 if ( ! stream_r.good() )
296 ZYPP_THROW( PluginFrameException( "Missing NL after header" ) );
297
298 if ( data.empty() )
299 break; // --> empty line sep. header and body
300
301 addRawHeader( data );
302
303 } while ( true );
304
305
306 // check for content-length header
307 std::optional<uint64_t> cLen;
308 {
309 const auto &contentLen = getHeaderNT( zypp::PluginFrame::contentLengthHeader(), std::string() );
310 if ( !contentLen.empty() ) {
311 cLen = zyppng::str::safe_strtonum<uint64_t>(contentLen);
312 if ( !cLen ) {
313 ERR << "Received malformed message from peer: Invalid value for " << zypp::PluginFrame::contentLengthHeader() << ":" << contentLen << std::endl;
314 ZYPP_THROW( PluginFrameException( "Invalid value for content-length." ) );
315 }
316
317 // do not keep the header, we regenerate it again when writing the frame anyway
319 }
320
321 }
322
323 // data
324 if ( cLen ) {
325 _body.resize ( (*cLen)+1, '\0' );
326 stream_r.read ( _body.data(), (*cLen)+1 );
327
328 if ( ! stream_r.good() )
329 ZYPP_THROW( PluginFrameException( "Missing data in stream" ) );
330 if ( _body.back() != '\0' )
331 ZYPP_THROW( PluginFrameException( "Missing NUL after body" ) );
332
333 _body.pop_back (); // get rid of \0
334
335 } else {
336 const auto &data = str::receiveUpTo( stream_r, '\0' );
337 _body = ByteArray( data.c_str(), data.size() );
338 if ( ! stream_r.good() )
339 ZYPP_THROW( PluginFrameException( "Missing NUL after body" ) );
340 }
341 }
342
343 std::ostream & PluginFrame::Impl::writeTo( std::ostream & stream_r ) const
344 {
345 //DBG << "Write " << *this << " to " << stream_r << endl;
346 if ( ! stream_r )
347 ZYPP_THROW( PluginFrameException( "Bad Stream" ) );
348
349 // command
350 stream_r << _command << "\n";
351
352 // STOMP recommends sending a content-length header
353 stream_r << contentLengthHeader() << ':' << str::numstring( _body.size() ) << "\n";
354
355 // header
356 // bsc#1231043: We do allow a literal ":" in the header value(!) in order to stay
357 // compatible with with plugin implementations (like zypp-plugin) which are not
358 // prepared to unescape a ":" there. And in fact it should not be necessary because
359 // the 1st colon separates header and value.
360 for_( it, _header.begin(), _header.end() )
361 stream_r << escapeHeader(it->first) << ':' << escapeHeader(it->second,/*escapeColon=*/false) << "\n";
362
363 // header end
364 stream_r << "\n";
365
366 // body
367 stream_r.write( _body.data(), _body.size() );
368
369 // body end
370 stream_r << '\0';
371 stream_r.flush();
372
373 if ( ! stream_r )
374 ZYPP_THROW( PluginFrameException( "Write error" ) );
375 return stream_r;
376 }
377
379 //
380 // CLASS NAME : PluginFrame
381 //
383
384 const std::string & PluginFrame::ackCommand()
385 {
386 static std::string _val( "ACK" );
387 return _val;
388 }
389
390 const std::string & PluginFrame::errorCommand()
391 {
392 static std::string _val( "ERROR" );
393 return _val;
394 }
395
396 const std::string & PluginFrame::enomethodCommand()
397 {
398 static std::string _val( "_ENOMETHOD" );
399 return _val;
400 }
401
403 {
404 static std::string _val("content-length");
405 return _val;
406 }
407
409 : _pimpl( Impl::nullimpl() )
410 {}
411
412 PluginFrame::PluginFrame( const std::string & command_r )
413 : _pimpl( new Impl( command_r ) )
414 {}
415
416 PluginFrame::PluginFrame(const std::string & command_r, std::string body_r )
417 : _pimpl( new Impl( command_r, ByteArray(body_r) ) )
418 {}
419
420 PluginFrame::PluginFrame(const std::string & command_r, ByteArray body_r )
421 : _pimpl( new Impl( command_r, std::move(body_r) ) )
422 {}
423
424 PluginFrame::PluginFrame( const std::string & command_r, HeaderInitializerList contents_r )
425 : _pimpl( new Impl( command_r, contents_r ) )
426 {}
427
428 PluginFrame::PluginFrame(const std::string & command_r, ByteArray body_r, HeaderInitializerList contents_r )
429 : _pimpl( new Impl( command_r, std::move(body_r), contents_r ) )
430 {}
431
432 PluginFrame::PluginFrame( std::istream & stream_r )
433 : _pimpl( new Impl( stream_r ) )
434 {}
435
437 { return _pimpl->empty(); }
438
439 const std::string & PluginFrame::command() const
440 { return _pimpl->command(); }
441
442 void PluginFrame::setCommand( const std::string & command_r )
443 { _pimpl->setCommand( command_r ); }
444
446 { return _pimpl->body(); }
447
449 { return _pimpl->bodyRef(); }
450
451 void PluginFrame::setBody( const std::string & body_r )
452 { _pimpl->setBody( ByteArray(body_r.data(), body_r.size()) ); }
453
454 void PluginFrame::setBody( const ByteArray & body_r )
455 { _pimpl->setBody( ByteArray(body_r) ); }
456
458 { _pimpl->setBody( std::move(body_r) ); }
459
460 std::ostream & PluginFrame::writeTo( std::ostream & stream_r ) const
461 { return _pimpl->writeTo( stream_r ); }
462
464 { return _pimpl->headerList(); }
465
467 { return _pimpl->headerList(); }
468
469 const std::string & PluginFrame::getHeader( const std::string & key_r ) const
470 { return _pimpl->getHeader( key_r ); }
471
472 const std::string & PluginFrame::getHeader( const std::string & key_r, const std::string & default_r ) const
473 { return _pimpl->getHeader( key_r, default_r ); }
474
475 const std::string & PluginFrame::getHeaderNT( const std::string & key_r, const std::string & default_r ) const
476 { return _pimpl->getHeaderNT( key_r, default_r ); }
477
478 void PluginFrame::setHeader( const std::string & key_r, const std::string & value_r )
479 { _pimpl->setHeader( key_r, value_r ); }
480
481 void PluginFrame::addHeader( const std::string & key_r, const std::string & value_r )
482 { _pimpl->addHeader( key_r, value_r ); }
483
485 { _pimpl->addHeader( contents_r ); }
486
488 {
489 _pimpl->addRawHeader( header.asStringView() );
490 }
491
492 void PluginFrame::clearHeader( const std::string & key_r )
493 { _pimpl->clearHeader( key_r ); }
494
496
497 std::ostream & operator<<( std::ostream & str, const PluginFrame & obj )
498 { return str << *obj._pimpl; }
499
500 bool operator==( const PluginFrame & lhs, const PluginFrame & rhs )
501 {
502 return ( lhs._pimpl == rhs._pimpl )
503 || (( lhs.command() == rhs.command() ) && ( lhs.headerList() == rhs.headerList() ) && ( lhs.body() == rhs.body() ));
504 }
505
507} // namespace zypp
Base class for PluginFrame Exception.
static const std::string & ackCommand()
"ACK" command.
ByteArray & bodyRef()
Return a reference to the frame body.
void setCommand(const std::string &command_r)
Set the frame command.
HeaderList::const_iterator HeaderListIterator
Header list iterator.
void setBody(const std::string &body_r)
Set the frame body.
const std::initializer_list< std::pair< std::string, std::string > > & HeaderInitializerList
Definition PluginFrame.h:46
PluginFrame()
Default ctor (empty frame)
void clearHeader(const std::string &key_r)
Remove all headers for key_r.
static const std::string & contentLengthHeader()
"content-lenght" header name
bool empty() const
Whether this is an empty frame.
const ByteArray & body() const
Return the frame body.
static const std::string & enomethodCommand()
"_ENOMETHOD" command.
const std::string & command() const
Return the frame command.
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.
RWCOW_pointer< Impl > _pimpl
Pointer to implementation.
static const std::string & errorCommand()
"ERROR" command.
const std::string & getHeader(const std::string &key_r) const
Return header value for key_r.
std::multimap< std::string, std::string > HeaderList
The header list.
HeaderList & headerList()
Modifyalble header list for internal use only.
void addRawHeader(const ByteArray &header)
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.
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.
std::ostream & writeTo(std::ostream &stream_r) const
Write frame to stream.
Definition Arch.h:364
String related utilities and Regular expression matching.
std::string numstring(char n, int w=0)
Definition String.h:290
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,...
Definition String.cc:491
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition String.cc:481
Easy-to use interface to the ZYPP dependency resolver.
bool operator==(const SetRelation::Enum &lhs, const SetCompare &rhs)
This is an overloaded member function, provided for convenience. It differs from the above function o...
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
std::optional< T > safe_strtonum(const std::string_view &val)
Definition string.h:23
PluginFrame implementation.
std::ostream & writeTo(std::ostream &stream_r) const
void setCommand(const std::string &command_r)
Impl(const std::string &command_r, HeaderInitializerList contents_r)
const std::string & getHeader(const std::string &key_r) const
static shared_ptr< Impl > nullimpl()
Offer default Impl.
const std::string & command() const
const std::string & getHeaderNT(const std::string &key_r, const std::string &default_r) const
Impl(const std::string &command_r, ByteArray &&body_r)
HeaderList & headerList()
friend Impl * rwcowClone(const Impl *rhs)
static std::string unescapeHeader(std::string_view val)
Impl(const std::string &command_r)
const HeaderList & headerList() const
std::pair< HeaderListIterator, HeaderListIterator > constKeyRange
const std::string & getHeader(const std::string &key_r, const std::string &default_r) const
void setHeader(const std::string &key_r, const std::string &value_r)
void clearHeader(const std::string &key_r)
void addHeader(const std::string &key_r, const std::string &value_r)
static std::string escapeHeader(std::string_view val, bool escapeColon=true)
std::pair< HeaderList::iterator, HeaderList::iterator > KeyRange
Impl * clone() const
clone for RWCOW_pointer
void addRawHeader(const std::string_view data)
void addHeader(HeaderInitializerList contents_r)
HeaderList::value_type mkHeaderPair(const std::string &key_r, const std::string &value_r)
void setBody(ByteArray &&body_r)
std::ostream & operator<<(std::ostream &str, const PluginFrame::Impl &obj)
Stream output.
Impl(const std::string &command_r, ByteArray &&body_r, HeaderInitializerList contents_r)
const ByteArray & body() const
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition Easy.h:27
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:459
#define ERR
Definition Logger.h:102