ResponderAdapter stuff

Renamed AICurlInterface::Responder to AICurlInterface::ResponderBase,
but without the virtual 'event' methods.
Derived from that: Responder and ReponderWithCompleted, where the
first defines result = 0, ErrorWithContent and error, and the latter
completedRaw and completed.
Added HttpClient::IgnoreBody, derived from Responder and implementing
'result' doing nothing; HttpClient::Ignore is now derived from
IgnoreBody and defines the still pure virtual getHTTPTimeoutPolicy.

Added ResponderBase::decode_body, which is now the sole place
where the code makes the decision wether some response data might be
LLSD or not based on the http status result. Before it just tried
to decode everything as LLSD, which seems a bit nonsense.

ResponderWithCompleted::completed no longer does anything, since
classes derived from ResponderWithCompleted are expected to override it,
or never call it by overriding completedRaw.

Entry point is now ResponderBase::finished = 0, instead of
completedRaw, where ResponderWithCompleted implements finished by
called completedRaw, but Responder doesn't: that directly calls
result/errorWithContent/error. Or, for the hack ResponderAdapter,
the entry points are pubResult/pubErrorWithContent.
Those are now the ONLY public methods, so more confusion.
mFinished is now set in all cases.

As a result of all that, it is no longer possible to accidently
pass a responder to ResponderAdapter that would break because it
expects completed() and completedRaw() to be called.

Added LLBufferArray::writeChannelTo.

Fixed bug for BlockingResponder::body (returned reference to temporary).

LLSDMessage::ResponderAdapter now allows a "timeoutpolicy" name
to be passed (not doing so results in the default timings), so
that the timeout policy of the used responder is retained.

Fixed llfasttimerview.cpp to test LLSDSerialize::fromXML() to return
a positive value instead of non-zero, because it may return -1 when the
parsing fails (three places).

Removed LLHTTPClient::Responder as base class from
LLFloaterRegionDebugConsole completely: it isn't a responder!

Several other responder classes were simplified a bit in order to
compile again with the above changes.
This commit is contained in:
Aleric Inglewood
2012-10-26 03:47:05 +02:00
parent 33809f56c5
commit 2a88f7d7c4
27 changed files with 244 additions and 169 deletions

View File

@@ -430,50 +430,92 @@ std::string strerror(CURLcode errorcode)
}
//-----------------------------------------------------------------------------
// class Responder
// class ResponderBase
//
Responder::Responder(void) : mReferenceCount(0), mFinished(false)
ResponderBase::ResponderBase(void) : mReferenceCount(0), mFinished(false)
{
DoutEntering(dc::curl, "AICurlInterface::Responder() with this = " << (void*)this);
}
Responder::~Responder()
ResponderBase::~ResponderBase()
{
DoutEntering(dc::curl, "AICurlInterface::Responder::~Responder() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
DoutEntering(dc::curl, "AICurlInterface::ResponderBase::~ResponderBase() with this = " << (void*)this << "; mReferenceCount = " << mReferenceCount);
llassert(mReferenceCount == 0);
}
void Responder::setURL(std::string const& url)
void ResponderBase::setURL(std::string const& url)
{
// setURL is called from llhttpclient.cpp (request()), before calling any of the below (of course).
// We don't need locking here therefore; it's a case of initializing before use.
mURL = url;
}
AIHTTPTimeoutPolicy const& Responder::getHTTPTimeoutPolicy(void) const
AIHTTPTimeoutPolicy const& ResponderBase::getHTTPTimeoutPolicy(void) const
{
return AIHTTPTimeoutPolicy::getDebugSettingsCurlTimeout();
}
void ResponderBase::decode_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, LLSD& content)
{
// If the status indicates success (and we get here) then we expect the body to be LLSD.
bool const should_be_llsd = (200 <= status && status < 300);
if (should_be_llsd)
{
LLBufferStream istr(channels, buffer.get());
if (LLSDSerialize::fromXML(content, istr) == LLSDParser::PARSE_FAILURE)
{
// Unfortunately we can't show the body of the message... I think this is a pretty serious error
// though, so if this ever happens it has to be investigated by making a copy of the buffer
// before serializing it, as is done below.
llwarns << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
}
// LLSDSerialize::fromXML destructed buffer, we can't initialize content now.
return;
}
// Put the body in content as-is.
std::stringstream ss;
buffer->writeChannelTo(ss, channels.in());
content = ss.str();
#ifdef SHOW_ASSERT
if (!should_be_llsd)
{
// Make sure that the server indeed never returns LLSD as body when the http status is an error.
LLSD dummy;
bool server_sent_llsd_with_http_error = LLSDSerialize::fromXML(dummy, ss) > 0;
if (server_sent_llsd_with_http_error)
{
llwarns << "The server sent us a response with http status " << status << " and LLSD(!) body: \"" << ss.str() << "\"!" << llendl;
}
llassert(!server_sent_llsd_with_http_error);
}
#endif
}
// Called with HTML body.
// virtual
void Responder::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, LLIOPipe::buffer_ptr_t const& buffer)
void ResponderWithCompleted::completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
LLSD content;
LLBufferStream istr(channels, buffer.get());
if (!LLSDSerialize::fromXML(content, istr))
{
llinfos << "Failed to deserialize LLSD. " << mURL << " [" << status << "]: " << reason << llendl;
}
decode_body(status, reason, channels, buffer, content);
// Allow derived class to override at this point.
completed(status, reason, content);
}
// virtual
void Responder::completed(U32 status, std::string const& reason, LLSD const& content)
void ResponderWithCompleted::completed(U32 status, std::string const& reason, LLSD const& content)
{
// Either completedRaw() or this method must be overridden by the derived class. Hence, we should never get here.
llassert_always(false);
}
// virtual
void Responder::finished(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
LLSD content;
decode_body(status, reason, channels, buffer, content);
// HTML status good?
if (200 <= status && status < 300)
{
@@ -485,6 +527,7 @@ void Responder::completed(U32 status, std::string const& reason, LLSD const& con
// Allow derived class to override at this point.
errorWithContent(status, reason, content);
}
mFinished = true;
}
// virtual
@@ -500,20 +543,14 @@ void Responder::error(U32 status, std::string const& reason)
llinfos << mURL << " [" << status << "]: " << reason << llendl;
}
// virtual
void Responder::result(LLSD const&)
{
// Nothing.
}
// Friend functions.
void intrusive_ptr_add_ref(Responder* responder)
void intrusive_ptr_add_ref(ResponderBase* responder)
{
responder->mReferenceCount++;
}
void intrusive_ptr_release(Responder* responder)
void intrusive_ptr_release(ResponderBase* responder)
{
if (--responder->mReferenceCount == 0)
{
@@ -528,7 +565,7 @@ void LegacyPolledResponder::completed_headers(U32 status, std::string const& rea
mStatus = status;
mReason = reason;
// Call base class implementation.
Responder::completed_headers(status, reason, code, info);
ResponderBase::completed_headers(status, reason, code, info);
}
} // namespace AICurlInterface

View File

@@ -200,13 +200,16 @@ void setCAPath(std::string const& file);
// destructed. Also, if any of those are destructed then the Responder is automatically
// destructed too.
//
class Responder : public AICurlResponderBufferEvents {
class ResponderBase : public AICurlResponderBufferEvents {
public:
typedef boost::shared_ptr<LLBufferArray> buffer_ptr_t;
protected:
Responder(void);
virtual ~Responder();
ResponderBase(void);
virtual ~ResponderBase();
// Read body from buffer and put it into content. If status indicates success, interpret it as LLSD, otherwise copy it as-is.
void decode_body(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer, LLSD& content);
protected:
// Associated URL, used for debug output.
@@ -227,11 +230,7 @@ class Responder : public AICurlResponderBufferEvents {
std::string const& getURL(void) const { return mURL; }
// Called by CurlResponderBuffer::timed_out or CurlResponderBuffer::processOutput.
void finished(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
completedRaw(status, reason, channels, buffer);
mFinished = true;
}
virtual void finished(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer) = 0;
// Return true if the curl thread is done with this transaction.
// If this returns true then it is guaranteed that none of the
@@ -279,6 +278,25 @@ class Responder : public AICurlResponderBufferEvents {
// The default does nothing.
}
private:
// Used by ResponderPtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(ResponderBase* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<ResponderBase> is made.
friend void intrusive_ptr_release(ResponderBase* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<ResponderBase> is destroyed.
// This function must delete the ResponderBase object when the reference count reaches zero.
};
class ResponderWithCompleted : public ResponderBase {
protected:
virtual void finished(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer)
{
// Allow classes derived from ResponderBase to override completedRaw
// (if not they should override completed or be derived from Responder instead).
completedRaw(status, reason, channels, buffer);
mFinished = true;
}
// Derived classes can override this to get the raw data of the body of the HTML message that was received.
// The default is to interpret the content as LLSD and call completed().
virtual void completedRaw(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);
@@ -287,9 +305,25 @@ class Responder : public AICurlResponderBufferEvents {
// The default is to call result() (or errorWithContent() in case of a HTML status indicating an error).
virtual void completed(U32 status, std::string const& reason, LLSD const& content);
#ifdef SHOW_ASSERT
// Responders derived from this class must override either completedRaw or completed.
// They may not attempt to override any of the virual functions defined by Responder.
// Define those functions here with different parameter in order to cause a compile
// warning when a class accidently tries to override them.
enum YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS { };
virtual void result(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void errorWithContent(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void error(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
#endif
};
class Responder : public ResponderBase {
protected:
virtual void finished(U32 status, std::string const& reason, LLChannelDescriptors const& channels, buffer_ptr_t const& buffer);
// ... or, derived classes can override this to received the content of a body upon success.
// The default does nothing.
virtual void result(LLSD const& content);
virtual void result(LLSD const& content) = 0;
// Derived classes can override this to get informed when a bad HTML status code is received.
// The default calls error().
@@ -303,27 +337,29 @@ class Responder : public AICurlResponderBufferEvents {
// Called from LLSDMessage::ResponderAdapter::listener.
// LLSDMessage::ResponderAdapter is a hack, showing among others by fact that these functions need to be public.
void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); }
void pubResult(LLSD const& content) { result(content); }
void pubErrorWithContent(U32 status, std::string const& reason, LLSD const& content) { errorWithContent(status, reason, content); mFinished = true; }
void pubResult(LLSD const& content) { result(content); mFinished = true; }
private:
// Used by ResponderPtr. Object is deleted when reference count reaches zero.
LLAtomicU32 mReferenceCount;
friend void intrusive_ptr_add_ref(Responder* p); // Called by boost::intrusive_ptr when a new copy of a boost::intrusive_ptr<Responder> is made.
friend void intrusive_ptr_release(Responder* p); // Called by boost::intrusive_ptr when a boost::intrusive_ptr<Responder> is destroyed.
// This function must delete the Responder object when the reference count reaches zero.
#ifdef SHOW_ASSERT
// Responders derived from this class must override result, and either errorWithContent or error.
// They may not attempt to override any of the virual functions defined by ResponderWithCompleted.
// Define those functions here with different parameter in order to cause a compile
// warning when a class accidently tries to override them.
enum YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS { };
virtual void completedRaw(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
virtual void completed(YOU_ARE_DERIVING_FROM_THE_WRONG_CLASS) { }
#endif
};
// A Responder is passed around as ResponderPtr, which causes it to automatically
// destruct when there are no pointers left pointing to it.
typedef boost::intrusive_ptr<Responder> ResponderPtr;
typedef boost::intrusive_ptr<ResponderBase> ResponderPtr;
// Same as above except that this class stores the result, allowing old polling
// code to poll if the transaction finished by calling is_finished() (from the
// main the thread) and then access the results-- as opposed to immediately
// digesting the results when any of the virtual functions are called.
class LegacyPolledResponder : public Responder {
class LegacyPolledResponder : public ResponderWithCompleted {
protected:
CURLcode mCode;
U32 mStatus;