diff options
author | Chris Wilson <chris+github@qwirx.com> | 2014-03-02 08:59:39 +0000 |
---|---|---|
committer | Chris Wilson <chris+github@qwirx.com> | 2014-03-02 08:59:39 +0000 |
commit | c30a30907d26fb25f449fb3f03274418f935b0a7 (patch) | |
tree | ef9b22698b5ca5e035bfe234722a8854e4b357c5 /lib/server | |
parent | b88db70703097ae8e5894e6dd5af2c5b672799a9 (diff) |
Always flush any incoming stream on server side.
Otherwise the protocol might be broken and can't be used any more, even if
we made an effort to return an Error reply instead of throwing an exception.
This used to not be a problem because an Error reply would terminate the
connection anyway, but it no longer does. So if the client also didn't
terminate, but tried to handle the exception and keep using the connection,
then it might find that its next command fails because the protocol is broken.
Diffstat (limited to 'lib/server')
-rwxr-xr-x | lib/server/makeprotocol.pl.in | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/lib/server/makeprotocol.pl.in b/lib/server/makeprotocol.pl.in index 78ef57a1..1c8f6081 100755 --- a/lib/server/makeprotocol.pl.in +++ b/lib/server/makeprotocol.pl.in @@ -159,8 +159,9 @@ print CPP <<__E; #include "$filename_base.h" #include "CollectInBufferStream.h" -#include "SocketStream.h" #include "MemBlockStream.h" +#include "SelfFlushingStream.h" +#include "SocketStream.h" __E print H <<__E; @@ -227,6 +228,9 @@ class $message_base_class : public Message public: virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const; + virtual std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext, IOStream& rDataStream) const; + virtual bool HasStreamWithCommand() const = 0; }; class $reply_base_class @@ -259,6 +263,12 @@ std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_ba { THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) } + +std::auto_ptr<$message_base_class> $message_base_class\::DoCommand($replyable_base_class &rProtocol, + $context_class &rContext, IOStream& rDataStream) const +{ + THROW_EXCEPTION(ConnectionException, Conn_Protocol_TriedToExecuteReplyCommand) +} __E my %cmd_classes; @@ -317,14 +327,39 @@ __E print H "\tstd::string GetMessage() const;\n"; } - if(obj_is_type($cmd, 'Command')) + my $has_stream = obj_is_type($cmd, 'StreamWithCommand'); + + if(obj_is_type($cmd, 'Command') && $has_stream) + { + print H <<__E; + std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext, IOStream& rDataStream) const; // IMPLEMENT THIS\n + std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext) const + { + THROW_EXCEPTION_MESSAGE(CommonException, Internal, + "This command requires a stream parameter"); + } +__E + } + elsif(obj_is_type($cmd, 'Command') && !$has_stream) { print H <<__E; std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, $context_class &rContext) const; // IMPLEMENT THIS\n + std::auto_ptr<$message_base_class> DoCommand($replyable_base_class &rProtocol, + $context_class &rContext, IOStream& rDataStream) const + { + THROW_EXCEPTION_MESSAGE(CommonException, NotSupported, + "This command requires no stream parameter"); + } __E } + print H <<__E; + bool HasStreamWithCommand() const { return $has_stream; } +__E + # want to be able to read from streams? print H "\tvoid SetPropertiesFromStreamData(Protocol &rProtocol);\n"; @@ -1002,10 +1037,20 @@ void $server_or_client_class\::DoServer($context_class &rContext) { // Get an object from the conversation std::auto_ptr<$message_base_class> pobj = Receive(); + std::auto_ptr<$message_base_class> preply; // Run the command - std::auto_ptr<$message_base_class> preply = pobj->DoCommand(*this, rContext); - + if(pobj->HasStreamWithCommand()) + { + std::auto_ptr<IOStream> apDataStream = ReceiveStream(); + SelfFlushingStream autoflush(*apDataStream); + preply = pobj->DoCommand(*this, rContext, *apDataStream); + } + else + { + preply = pobj->DoCommand(*this, rContext); + } + // Send the reply Send(*preply); @@ -1052,7 +1097,7 @@ __E my $reply_class = $cmd_classes{$reply_msg}; my $reply_id = $cmd_id{$reply_msg}; my $has_stream = obj_is_type($cmd,'StreamWithCommand'); - my $argextra = $has_stream?', std::auto_ptr<IOStream> apStream':''; + my $argextra = $has_stream?', std::auto_ptr<IOStream> apDataStream':''; my $send_stream_extra = ''; my $send_stream_method = $writing_client ? "SendStream" : "SendStreamAfterCommand"; @@ -1068,7 +1113,7 @@ __E { $send_stream_extra = <<__E; // Send stream after the command - SendStream(*apStream); + SendStream(*apDataStream); __E } @@ -1078,37 +1123,29 @@ __E $send_stream_extra // Wait for the reply - std::auto_ptr<$message_base_class> preply = Receive(); + std::auto_ptr<$message_base_class> apReply = Receive(); __E } elsif($writing_local) { if($has_stream) { - $send_stream_extra = <<__E; - // Send stream after the command - SendStreamAfterCommand(apStream); + print CPP <<__E; + std::auto_ptr<$message_base_class> apReply = rQuery.DoCommand(*this, + mrContext, *apDataStream); __E } - - print CPP <<__E; - // Push streams to send, if any, into queue for retrieval by DoCommand. - $send_stream_extra - - // Execute the command and get the reply message - std::auto_ptr<$message_base_class> preply = rQuery.DoCommand(*this, mrContext); - - if(!mStreamsToSend.empty()) - { - THROW_EXCEPTION_MESSAGE(ConnectionException, - Protocol_StreamsNotConsumed, rQuery.ToString()); - } + else + { + print CPP <<__E; + std::auto_ptr<$message_base_class> apReply = rQuery.DoCommand(*this, mrContext); __E + } } # Common to both client and local print CPP <<__E; - CheckReply("$cmd", rQuery, *preply, $reply_id); + CheckReply("$cmd", rQuery, *apReply, $reply_id); // Correct response, if no exception thrown by CheckReply return std::auto_ptr<$reply_class>( |