PageRenderTime 22ms CodeModel.GetById 11ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llsdmessage.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 170 lines | 104 code | 11 blank | 55 comment | 7 complexity | 2497ddeafb85cf4daf8df094a029be49 MD5 | raw file
  1/**
  2 * @file   llsdmessage.cpp
  3 * @author Nat Goodspeed
  4 * @date   2008-10-31
  5 * @brief  Implementation for llsdmessage.
  6 * 
  7 * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#if LL_WINDOWS
 30#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
 31#endif
 32
 33// Precompiled header
 34#include "linden_common.h"
 35// associated header
 36#include "llsdmessage.h"
 37// STL headers
 38// std headers
 39// external library headers
 40// other Linden headers
 41#include "llevents.h"
 42#include "llsdserialize.h"
 43#include "llhttpclient.h"
 44#include "llmessageconfig.h"
 45#include "llhost.h"
 46#include "message.h"
 47#include "llsdutil.h"
 48
 49// Declare a static LLSDMessage instance to ensure that we have a listener as
 50// soon as someone tries to post on our canonical LLEventPump name.
 51static LLSDMessage httpListener;
 52
 53LLSDMessage::LLSDMessage():
 54    // Instantiating our own local LLEventPump with a string name the
 55    // constructor is NOT allowed to tweak is a way of ensuring Singleton
 56    // semantics: attempting to instantiate a second LLSDMessage object would
 57    // throw LLEventPump::DupPumpName.
 58    mEventPump("LLHTTPClient")
 59{
 60    mEventPump.listen("self", boost::bind(&LLSDMessage::httpListener, this, _1));
 61}
 62
 63bool LLSDMessage::httpListener(const LLSD& request)
 64{
 65    // Extract what we want from the request object. We do it all up front
 66    // partly to document what we expect.
 67    LLSD::String url(request["url"]);
 68    LLSD payload(request["payload"]);
 69    LLSD::String reply(request["reply"]);
 70    LLSD::String error(request["error"]);
 71    LLSD::Real timeout(request["timeout"]);
 72    // If the LLSD doesn't even have a "url" key, we doubt it was intended for
 73    // this listener.
 74    if (url.empty())
 75    {
 76        std::ostringstream out;
 77        out << "request event without 'url' key to '" << mEventPump.getName() << "'";
 78        throw ArgError(out.str());
 79    }
 80    // Establish default timeout. This test relies on LLSD::asReal() returning
 81    // exactly 0.0 for an undef value.
 82    if (! timeout)
 83    {
 84        timeout = HTTP_REQUEST_EXPIRY_SECS;
 85    }
 86    LLHTTPClient::post(url, payload,
 87                       new LLSDMessage::EventResponder(LLEventPumps::instance(),
 88                                                       request,
 89                                                       url, "POST", reply, error),
 90                       LLSD(),      // headers
 91                       timeout);
 92    return false;
 93}
 94
 95void LLSDMessage::EventResponder::result(const LLSD& data)
 96{
 97    // If our caller passed an empty replyPump name, they're not
 98    // listening: this is a fire-and-forget message. Don't bother posting
 99    // to the pump whose name is "".
100    if (! mReplyPump.empty())
101    {
102        LLSD response(data);
103        mReqID.stamp(response);
104        mPumps.obtain(mReplyPump).post(response);
105    }
106    else                            // default success handling
107    {
108        LL_INFOS("LLSDMessage::EventResponder")
109            << "'" << mMessage << "' to '" << mTarget << "' succeeded"
110            << LL_ENDL;
111    }
112}
113
114void LLSDMessage::EventResponder::errorWithContent(U32 status, const std::string& reason, const LLSD& content)
115{
116    // If our caller passed an empty errorPump name, they're not
117    // listening: "default error handling is acceptable." Only post to an
118    // explicit pump name.
119    if (! mErrorPump.empty())
120    {
121        LLSD info(mReqID.makeResponse());
122        info["target"]  = mTarget;
123        info["message"] = mMessage;
124        info["status"]  = LLSD::Integer(status);
125        info["reason"]  = reason;
126        info["content"] = content;
127        mPumps.obtain(mErrorPump).post(info);
128    }
129    else                        // default error handling
130    {
131        // convention seems to be to use llinfos, but that seems a bit casual?
132        LL_WARNS("LLSDMessage::EventResponder")
133            << "'" << mMessage << "' to '" << mTarget
134            << "' failed with code " << status << ": " << reason << '\n'
135            << ll_pretty_print_sd(content)
136            << LL_ENDL;
137    }
138}
139
140LLSDMessage::ResponderAdapter::ResponderAdapter(LLHTTPClient::ResponderPtr responder,
141                                                const std::string& name):
142    mResponder(responder),
143    mReplyPump(name + ".reply", true), // tweak name for uniqueness
144    mErrorPump(name + ".error", true)
145{
146    mReplyPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, true));
147    mErrorPump.listen("self", boost::bind(&ResponderAdapter::listener, this, _1, false));
148}
149
150bool LLSDMessage::ResponderAdapter::listener(const LLSD& payload, bool success)
151{
152    if (success)
153    {
154        mResponder->result(payload);
155    }
156    else
157    {
158        mResponder->errorWithContent(payload["status"].asInteger(), payload["reason"], payload["content"]);
159    }
160
161    /*---------------- MUST BE LAST STATEMENT BEFORE RETURN ----------------*/
162    delete this;
163    // Destruction of mResponder will usually implicitly free its referent as well
164    /*------------------------- NOTHING AFTER THIS -------------------------*/
165    return false;
166}
167
168void LLSDMessage::link()
169{
170}