Bug 1316507
Summary: | [C++ client] Message::setContent("") does not work | ||
---|---|---|---|
Product: | Red Hat Enterprise MRG | Reporter: | Pavel Moravec <pmoravec> |
Component: | qpid-cpp | Assignee: | messaging-bugs <messaging-bugs> |
Status: | CLOSED ERRATA | QA Contact: | Messaging QE <messaging-qe-bugs> |
Severity: | high | Docs Contact: | |
Priority: | high | ||
Version: | 3.2 | CC: | gsim, jross, mcressma, tkratky, zkraus |
Target Milestone: | 3.2.2 | ||
Target Release: | --- | ||
Hardware: | All | ||
OS: | Linux | ||
Whiteboard: | |||
Fixed In Version: | qpid-cpp-0.34-10 | Doc Type: | Bug Fix |
Doc Text: |
Cause: The message implementation can hold two views of message content: the raw bytes or a decode object form. These operate independently, i.e. changes to the raw bytes do not alter the object content.
Consequence: When a message is automatically decoded into object form on receipt, then updated by setting the raw bytes in order to resend, the resent message used the original object content rather than the bytes.
Fix: On setting the raw bytes, any previously held object content view is reset (i.e. invalidated). (Additionally, a setting was added to allow the automatic decoding of messages into object form to be disabled by environment variable or config file).
Result: The message content reflects the applications actions.
|
Story Points: | --- |
Clone Of: | Environment: | ||
Last Closed: | 2016-10-11 07:36:14 UTC | Type: | Bug |
Regression: | --- | Mount Type: | --- |
Documentation: | --- | CRM: | |
Verified Versions: | Category: | --- | |
oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
Cloudforms Team: | --- | Target Upstream Version: | |
Embargoed: |
Description
Pavel Moravec
2016-03-10 11:21:52 UTC
A workaround is to setContentObject(std::string()), or getContentObject().reset(). Fixed upstream: https://svn.apache.org/r1734476 This bug was accidentally moved from POST to MODIFIED via an error in automation, please see mmccune with any questions The fix does not work and some more generic problem identified (let split to 2nd bug it if you feel a separate BZ needed for it). Reproducer: use amqp/map content type, copy such message, update content (to "" or to some non-empty string, shorter than original one), send to qpidd, receive - the received message will have the _original_ content, still Reproducer source code: #include <iostream> #include <string> #include <unistd.h> #include <qpid/messaging/Address.h> #include <qpid/messaging/Connection.h> #include <qpid/messaging/Session.h> #include <qpid/messaging/Message.h> #include <qpid/messaging/Sender.h> #include <qpid/messaging/Receiver.h> #include <qpid/types/Variant.h> using namespace std; using namespace qpid::messaging; using namespace qpid::types; const int fragSize = 102; const int msgSize = 107; int main( int argc, char *argv[]) { //enable it to test the content type amqp/map #if 1 Message message; qpid::types::Variant::Map params; params["payload"] = std::string(msgSize, 'a'); encode( params, message); message.setCorrelationId("123"); cout << "message content: " << message.getContent() << endl; #endif //enable it to test the content type empty #if 0 qpid::types::Variant::Map params; std::string content; for (int i = 0; i < msgSize; i++) content+="A"; Message message(content); cout << "message content: " << message.getContent() << endl; #endif Connection connection = Connection("localhost:5672"); connection.open(); Session session = connection.createSession(); Sender sender = session.createSender("amq.direct"); message.setSubject("IN"); // message.setContentType("amqp/map"); // already set cout << "message sent:\n size : " << message.getContentSize() << " type : " << message.getContentType() << " cID : " << message.getCorrelationId() << std::endl; sender.send(message); Message c_message; Connection c_connection("localhost:5672"); c_connection.open(); Session c_session = c_connection.createSession(); Receiver receiver = c_session.createReceiver("testQueueIn"); receiver.fetch(c_message, Duration::FOREVER); cout << "message received:\n size : " << c_message.getContentSize() << " type : " << c_message.getContentType() << " cID: " << c_message.getCorrelationId() << std::endl; c_connection.close(); Message fragTemplate(c_message); for( int sequenceNumber = 1; (sequenceNumber-1) * fragSize <= (signed int)c_message.getContentSize() ; sequenceNumber++) { Message fragment(fragTemplate); fragment.setContent(c_message.getContent().substr((sequenceNumber-1) * fragSize, fragSize)); if( sequenceNumber == 1 ) fragment.setCorrelationId( "1"); else fragment.setCorrelationId ("2"); fragment.setSubject("OUT"); // fragment.setContentType("amqp/map"); // already set cout << "fragment sent:\n size: " << fragment.getContentSize() << " type: " << fragment.getContentType() << " cID : " << fragment.getCorrelationId() << endl; cout << "fragment content: " << fragment.getContent() << endl; sender.send(fragment); session.sync(); } for( int i = 0; i < 2 ; ++i) { Message r_message; Connection r_connection("localhost:5672"); r_connection.open(); Session r_session = r_connection.createSession(); Receiver r_receiver = r_session.createReceiver("testQueueOut"); r_receiver.fetch(r_message, Duration::IMMEDIATE); r_session.acknowledge(true); cout << "message received:\n size : " << r_message.getContentSize() << " type : " << r_message.getContentType() << " cID : " << r_message.getCorrelationId() << std::endl; cout << "message content: " << r_message.getContent() << endl; r_connection.close(); } return 0; } Setup to reproduce it: qpid-config add queue testQueueIn qpid-config add queue testQueueOut qpid-config bind amq.direct testQueueIn IN qpid-config bind amq.direct testQueueOut OUT Running the reproducer on 0.34-8: message content: zpayload�kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa message sent: size : 126 type : amqp/map cID : 123 message received: size : 126 type : amqp/map cID: 123 fragment sent: size: 102 type: amqp/map cID : 1 fragment content: zpayload�kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa fragment sent: size: 24 type: amqp/map cID : 2 fragment content: aaaaaaaaaaaaaaaaaaaaaaaa message received: size : 126 type : amqp/map cID : 1 message content: zpayload�kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa message received: size : 126 type : amqp/map cID : 2 message content: zpayload�kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa See the "fragment content" is OK, but even sending it to the broker, the original content is used (run tcpdump to confirm). Making the scenario worse: having line 68: fragment.setContent(""); the program coredumps: message content: zpayload�kaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa message sent: size : 126 type : amqp/map cID : 123 message received: size : 126 type : amqp/map cID: 123 fragment sent: size: 0 type: amqp/map cID : 1 fragment content: fragment sent: size: 0 type: amqp/map cID : 2 fragment content: terminate called after throwing an instance of 'qpid::messaging::EncodingException' what(): illegal-argument: Not enough data for field table. (/builddir/build/BUILD/qpid-cpp-0.34/src/qpid/framing/FieldTable.cpp:295) Aborted (core dumped) FYI the reproducer works fine in upstream, so a difference between upstream (r1737000) and qpid-cpp 0.34-8 causes the problems. I see the same behaviour upstream as well as on 0.34-mrg, namely that the bytes actually sent are the original full map, not the fragments as would be expected. This is because the map is decoded in the receiver (in order for the behaviour between 0-10 and 1.0 to be uniform). There is a workaround, which is to set a connection option on c_connection and r_connection of "disable_auto_decode" to true. The fix is to remove the condition from the original fix, i.e. reset the content-object whenever the bytes are changed. However with that fix in place, if the auto-decode is not disabled then the fetch will fail as the message is considered malformed (it has a content-type that indicates it is a map, but the content itself is not a valid map). The fetch throws an exception, which if caught will prevent the core dump. So to summarise, there was a new behaviour introduced that will auto-decode maps/lists for AMQP 0-10. This can be disabled via a connection option. If disabled, it works around the bug exposed by the latest reproducer. Even if the bug is fixed, the new auto-decoding behaviour would cause problems if messages are ever sent with content that does not match the content type. One possibility would be to add a way to alter the default value for disable_auto_decode, so that could be set in an env var or conf file and would apply to all connections without modifying any code. Fixed[1] the bug that prevents updated bytes resetting the content-object (this is really an extension to the first fix made for this issue to cover all cases). Also added the ability to set the disable-auto-decode option via an environment variable or client config file[2]. [1] https://svn.apache.org/r1737093 [2] https://svn.apache.org/r1737094 Since the problem described in this bug report should be resolved in a recent advisory, it has been closed with a resolution of ERRATA. For information on the advisory, and where to find the updated files, follow the link below. If the solution does not work for you, open a new bug report. https://rhn.redhat.com/errata/RHBA-2016-2049.html |