diff options
29 files changed, 633 insertions, 188 deletions
diff --git a/indra/lib/python/indra/base/cllsd_test.py b/indra/lib/python/indra/base/cllsd_test.py index 3af59e741a..0b20d99d80 100644 --- a/indra/lib/python/indra/base/cllsd_test.py +++ b/indra/lib/python/indra/base/cllsd_test.py @@ -10,7 +10,7 @@ values = (      '&<>',      u'\u81acj',      llsd.uri('http://foo<'), -    lluuid.LLUUID(), +    lluuid.UUID(),      llsd.LLSD(['thing']),      1,      myint(31337), diff --git a/indra/lib/python/indra/base/llsd.py b/indra/lib/python/indra/base/llsd.py index 9534d5935e..1190d88663 100644 --- a/indra/lib/python/indra/base/llsd.py +++ b/indra/lib/python/indra/base/llsd.py @@ -72,8 +72,11 @@ BOOL_FALSE = ('0', '0.0', 'false', '')  def format_datestr(v): -    """ Formats a datetime object into the string format shared by xml and notation serializations.""" -    return v.isoformat() + 'Z' +    """ Formats a datetime or date object into the string format shared by xml and notation serializations.""" +    if hasattr(v, 'microsecond'): +        return v.isoformat() + 'Z' +    else: +        return v.strftime('%Y-%m-%dT%H:%M:%SZ')  def parse_datestr(datestr):      """Parses a datetime object from the string format shared by xml and notation serializations.""" @@ -183,6 +186,7 @@ class LLSDXMLFormatter(object):              unicode : self.STRING,              uri : self.URI,              datetime.datetime : self.DATE, +            datetime.date : self.DATE,              list : self.ARRAY,              tuple : self.ARRAY,              types.GeneratorType : self.ARRAY, @@ -347,6 +351,7 @@ class LLSDNotationFormatter(object):              unicode : self.STRING,              uri : self.URI,              datetime.datetime : self.DATE, +            datetime.date : self.DATE,              list : self.ARRAY,              tuple : self.ARRAY,              types.GeneratorType : self.ARRAY, @@ -924,12 +929,13 @@ def _format_binary_recurse(something):                  (type(something), something)) -def parse_binary(something): -    header = '<?llsd/binary?>\n' -    if not something.startswith(header): -        raise LLSDParseError('LLSD binary encoding header not found') -    return LLSDBinaryParser().parse(something[len(header):]) -     +def parse_binary(binary): +    if binary.startswith('<?llsd/binary?>'): +        just_binary = binary.split('\n', 1)[1] +    else: +        just_binary = binary +    return LLSDBinaryParser().parse(just_binary) +  def parse_xml(something):      try:          return to_python(fromstring(something)[0]) diff --git a/indra/lib/python/indra/base/metrics.py b/indra/lib/python/indra/base/metrics.py index 8f2a85cf0e..ff8380265f 100644 --- a/indra/lib/python/indra/base/metrics.py +++ b/indra/lib/python/indra/base/metrics.py @@ -29,25 +29,93 @@ $/LicenseInfo$  """  import sys -from indra.base import llsd +try: +    import syslog +except ImportError: +    # Windows +    import sys +    class syslog(object): +        # wrap to a lame syslog for windows +        _logfp = sys.stderr +        def syslog(msg): +            _logfp.write(msg) +            if not msg.endswith('\n'): +                _logfp.write('\n') +        syslog = staticmethod(syslog) -_sequence_id = 0 +from indra.base.llsd import format_notation -def record_metrics(table, stats, dest=None): +def record_metrics(table, stats):      "Write a standard metrics log" -    _log("LLMETRICS", table, stats, dest) +    _log("LLMETRICS", table, stats) -def record_event(table, data, dest=None): +def record_event(table, data):      "Write a standard logmessage log" -    _log("LLLOGMESSAGE", table, data, dest) +    _log("LLLOGMESSAGE", table, data) + +def set_destination(dest): +    """Set the destination of metrics logs for this process. -def _log(header, table, data, dest): +    If you do not call this function prior to calling a logging +    method, that function will open sys.stdout as a destination. +    Attempts to set dest to None will throw a RuntimeError. +    @param dest a file-like object which will be the destination for logs."""      if dest is None: -        # do this check here in case sys.stdout changes at some -        # point. as a default parameter, it will never be -        # re-evaluated. -        dest = sys.stdout +        raise RuntimeError("Attempt to unset metrics destination.") +    global _destination +    _destination = dest + +def destination(): +    """Get the destination of the metrics logs for this process. +    Returns None if no destination is set""" +    global _destination +    return _destination + +class SysLogger(object): +    "A file-like object which writes to syslog." +    def __init__(self, ident='indra', logopt = None, facility = None): +        try: +            if logopt is None: +                logopt = syslog.LOG_CONS | syslog.LOG_PID +            if facility is None: +                facility = syslog.LOG_LOCAL0 +            syslog.openlog(ident, logopt, facility) +            import atexit +            atexit.register(syslog.closelog) +        except AttributeError: +            # No syslog module on Windows +            pass + +    def write(str): +        syslog.syslog(str) +    write = staticmethod(write) + +    def flush(): +        pass +    flush = staticmethod(flush) + +# +# internal API +# +_sequence_id = 0 +_destination = None + +def _next_id():      global _sequence_id -    print >>dest, header, "(" + str(_sequence_id) + ")", -    print >>dest, table, llsd.format_notation(data) +    next = _sequence_id      _sequence_id += 1 +    return next + +def _dest(): +    global _destination +    if _destination is None: +        # this default behavior is documented in the metrics functions above. +        _destination = sys.stdout +    return _destination +     +def _log(header, table, data): +    log_line = "%s (%d) %s %s" \ +               % (header, _next_id(), table, format_notation(data)) +    dest = _dest() +    dest.write(log_line) +    dest.flush() diff --git a/indra/lib/python/indra/ipc/servicebuilder.py b/indra/lib/python/indra/ipc/servicebuilder.py index cb43bcb26f..0a0ce2b4e2 100644 --- a/indra/lib/python/indra/ipc/servicebuilder.py +++ b/indra/lib/python/indra/ipc/servicebuilder.py @@ -39,6 +39,12 @@ except:      pass  _g_builder = None +def _builder(): +    global _g_builder +    if _g_builder is None: +        _g_builder = ServiceBuilder() +    return _g_builder +  def build(name, context={}, **kwargs):      """ Convenience method for using a global, singleton, service builder.  Pass arguments either via a dict or via python keyword arguments, or both! @@ -56,6 +62,11 @@ def build(name, context={}, **kwargs):          _g_builder = ServiceBuilder()      return _g_builder.buildServiceURL(name, context, **kwargs) +def build_path(name, context={}, **kwargs): +    context = context.copy()  # shouldn't modify the caller's dictionary +    context.update(kwargs) +    return _builder().buildPath(name, context) +  class ServiceBuilder(object):      def __init__(self, services_definition = services_config):          """\ @@ -73,12 +84,21 @@ class ServiceBuilder(object):                  continue              if isinstance(service_builder, dict):                  # We will be constructing several builders -                for name, builder in service_builder.items(): +                for name, builder in service_builder.iteritems():                      full_builder_name = service['name'] + '-' + name                      self.builders[full_builder_name] = builder              else:                  self.builders[service['name']] = service_builder +    def buildPath(self, name, context): +        """\ +        @brief given the environment on construction, return a service path. +        @param name The name of the service. +        @param context A dict of name value lookups for the service. +        @returns Returns the  +        """ +        return russ.format(self.builders[name], context) +      def buildServiceURL(self, name, context={}, **kwargs):          """\          @brief given the environment on construction, return a service URL. diff --git a/indra/lib/python/indra/ipc/siesta.py b/indra/lib/python/indra/ipc/siesta.py index b206f181c4..d867e71537 100644 --- a/indra/lib/python/indra/ipc/siesta.py +++ b/indra/lib/python/indra/ipc/siesta.py @@ -1,3 +1,32 @@ +"""\ +@file siesta.py +@brief A tiny llsd based RESTful web services framework + +$LicenseInfo:firstyear=2008&license=mit$ + +Copyright (c) 2008, Linden Research, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +$/LicenseInfo$ +""" + +from indra.base import config  from indra.base import llsd  from webob import exc  import webob @@ -37,11 +66,11 @@ def mime_type(content_type):      return content_type.split(';', 1)[0].strip().lower()  class BodyLLSD(object): -    '''Give a webob Request or Response an llsd property. +    '''Give a webob Request or Response an llsd based "content" property. -    Getting the llsd property parses the body, and caches the result. +    Getting the content property parses the body, and caches the result. -    Setting the llsd property formats a payload, and the body property +    Setting the content property formats a payload, and the body property      is set.'''      def _llsd__get(self): @@ -80,7 +109,7 @@ class BodyLLSD(object):          if hasattr(self, '_llsd'):              del self._llsd -    llsd = property(_llsd__get, _llsd__set, _llsd__del) +    content = property(_llsd__get, _llsd__set, _llsd__del)  class Response(webob.Response, BodyLLSD): @@ -114,10 +143,10 @@ class Request(webob.Request, BodyLLSD):      Sensible content type and accept headers are used by default. -    Setting the llsd property also sets the body.  Getting the llsd +    Setting the content property also sets the body. Getting the content      property parses the body if necessary. -    If you set the body property directly, the llsd property will be +    If you set the body property directly, the content property will be      deleted.'''      default_content_type = 'application/llsd+xml' @@ -149,11 +178,11 @@ class Request(webob.Request, BodyLLSD):      body = property(webob.Request._body__get, _body__set,                      webob.Request._body__del, webob.Request._body__get.__doc__) -    def create_response(self, llsd=None, status='200 OK', +    def create_response(self, content=None, status='200 OK',                          conditional_response=webob.NoDefault):          resp = self.ResponseClass(status=status, request=self,                                    conditional_response=conditional_response) -        resp.llsd = llsd +        resp.content = content          return resp      def curl(self): @@ -196,12 +225,18 @@ llsd_formatters = {      'application/xml': llsd.format_xml,      } +formatter_qualities = ( +    ('application/llsd+xml', 1.0), +    ('application/llsd+notation', 0.5), +    ('application/llsd+binary', 0.4), +    ('application/xml', 0.3), +    ('application/json', 0.2), +    )  def formatter_for_mime_type(mime_type):      '''Return a formatter that encodes to the given MIME type.      The result is a pair of function and MIME type.''' -      try:          return llsd_formatters[mime_type], mime_type      except KeyError: @@ -214,21 +249,19 @@ def formatter_for_request(req):      '''Return a formatter that encodes to the preferred type of the client.      The result is a pair of function and actual MIME type.''' - -    for ctype in req.accept.best_matches('application/llsd+xml'): -        try: -            return llsd_formatters[ctype], ctype -        except KeyError: -            pass -    else: +    ctype = req.accept.best_match(formatter_qualities) +    try: +        return llsd_formatters[ctype], ctype +    except KeyError:          raise exc.HTTPNotAcceptable().exception  def wsgi_adapter(func, environ, start_response):      '''Adapt a Siesta callable to act as a WSGI application.''' - +    # Process the request as appropriate.      try:          req = Request(environ) +        #print req.urlvars          resp = func(req, **req.urlvars)          if not isinstance(resp, webob.Response):              try: @@ -281,7 +314,8 @@ def llsd_class(cls):              allowed = [m for m in http11_methods                         if hasattr(instance, 'handle_' + m.lower())]              raise exc.HTTPMethodNotAllowed( -                headers={'Allowed': ', '.join(allowed)}).exception +                headers={'Allow': ', '.join(allowed)}).exception +        #print "kwargs: ", kwargs          return handler(req, **kwargs)      def replacement(environ, start_response): @@ -336,7 +370,7 @@ def curl(reqs):  route_re = re.compile(r'''      \{                 # exact character "{" -    (\w+)              # variable name (restricted to a-z, 0-9, _) +    (\w*)              # "config" or variable (restricted to a-z, 0-9, _)      (?:([:~])([^}]+))? # optional :type or ~regex part      \}                 # exact character "}"      ''', re.VERBOSE) @@ -344,27 +378,37 @@ route_re = re.compile(r'''  predefined_regexps = {      'uuid': r'[a-f0-9][a-f0-9-]{31,35}',      'int': r'\d+', +    'host': r'[a-z0-9][a-z0-9\-\.]*',      }  def compile_route(route):      fp = StringIO()      last_pos = 0      for match in route_re.finditer(route): +        #print "matches: ", match.groups()          fp.write(re.escape(route[last_pos:match.start()]))          var_name = match.group(1)          sep = match.group(2)          expr = match.group(3) -        if expr: -            if sep == ':': -                expr = predefined_regexps[expr] -            # otherwise, treat what follows '~' as a regexp +        if var_name == 'config': +            expr = re.escape(str(config.get(var_name)))          else: -            expr = '[^/]+' -        expr = '(?P<%s>%s)' % (var_name, expr) +            if expr: +                if sep == ':': +                    expr = predefined_regexps[expr] +                # otherwise, treat what follows '~' as a regexp +            else: +                expr = '[^/]+' +            if var_name != '': +                expr = '(?P<%s>%s)' % (var_name, expr) +            else: +                expr = '(%s)' % (expr,)          fp.write(expr)          last_pos = match.end()      fp.write(re.escape(route[last_pos:])) -    return '^%s$' % fp.getvalue() +    compiled_route = '^%s$' % fp.getvalue() +    #print route, "->", compiled_route +    return compiled_route  class Router(object):      '''WSGI routing class.  Parses a URL and hands off a request to @@ -372,21 +416,43 @@ class Router(object):      responds with a 404.'''      def __init__(self): -        self.routes = [] -        self.paths = [] +        self._new_routes = [] +        self._routes = [] +        self._paths = []      def add(self, route, app, methods=None): -        self.paths.append(route) -        self.routes.append((re.compile(compile_route(route)), app, -                            methods and dict.fromkeys(methods))) +        self._new_routes.append((route, app, methods)) + +    def _create_routes(self): +        for route, app, methods in self._new_routes: +            self._paths.append(route) +            self._routes.append( +                (re.compile(compile_route(route)), +                 app, +                 methods and dict.fromkeys(methods))) +        self._new_routes = []      def __call__(self, environ, start_response): +        # load up the config from the config file. Only needs to be +        # done once per interpreter. This is the entry point of all +        # siesta applications, so this is where we trap it. +        _conf = config.get_config() +        if _conf is None: +            import os.path +            fname = os.path.join( +                environ.get('ll.config_dir', '/local/linden/etc'), +                'indra.xml') +            config.load(fname) + +        # proceed with handling the request +        self._create_routes()          path_info = environ['PATH_INFO']          request_method = environ['REQUEST_METHOD']          allowed = [] -        for regex, app, methods in self.routes: +        for regex, app, methods in self._routes:              m = regex.match(path_info)              if m: +                #print "groupdict:",m.groupdict()                  if not methods or request_method in methods:                      environ['paste.urlvars'] = m.groupdict()                      return app(environ, start_response) @@ -396,7 +462,7 @@ class Router(object):              allowed = dict.fromkeys(allows).keys()              allowed.sort()              resp = exc.HTTPMethodNotAllowed( -                headers={'Allowed': ', '.join(allowed)}) +                headers={'Allow': ', '.join(allowed)})          else:              resp = exc.HTTPNotFound()          return resp(environ, start_response) diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py index 59c37a7218..cdce8237c8 100644 --- a/indra/lib/python/indra/util/named_query.py +++ b/indra/lib/python/indra/util/named_query.py @@ -47,10 +47,8 @@ except NameError:  from indra.base import llsd  from indra.base import config -DEBUG = False - -NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') -NQ_FILE_SUFFIX_LEN  = len(NQ_FILE_SUFFIX) +NQ_FILE_SUFFIX = None +NQ_FILE_SUFFIX_LEN = None  _g_named_manager = None @@ -60,6 +58,11 @@ def _init_g_named_manager(sql_dir = None):      This function is intended entirely for testing purposes,      because it's tricky to control the config from inside a test.""" +    global NQ_FILE_SUFFIX +    NQ_FILE_SUFFIX = config.get('named-query-file-suffix', '.nq') +    global NQ_FILE_SUFFIX_LEN +    NQ_FILE_SUFFIX_LEN  = len(NQ_FILE_SUFFIX) +      if sql_dir is None:          sql_dir = config.get('named-query-base-dir') @@ -73,11 +76,11 @@ def _init_g_named_manager(sql_dir = None):      _g_named_manager = NamedQueryManager(          os.path.abspath(os.path.realpath(sql_dir))) -def get(name): +def get(name, schema = None):      "Get the named query object to be used to perform queries"      if _g_named_manager is None:          _init_g_named_manager() -    return _g_named_manager.get(name) +    return _g_named_manager.get(name).for_schema(schema)  def sql(connection, name, params):      # use module-global NamedQuery object to perform default substitution @@ -330,6 +333,8 @@ class NamedQuery(object):      def for_schema(self, db_name):          "Look trough the alternates and return the correct query" +        if db_name is None: +            return self          try:              return self._alternative[db_name]          except KeyError, e: @@ -359,10 +364,10 @@ class NamedQuery(object):          if DEBUG:              print "SQL:", self.sql(connection, params)          rows = cursor.execute(full_query, params) -         +          # *NOTE: the expect_rows argument is a very cheesy way to get some          # validation on the result set.  If you want to add more expectation -        # logic, do something more object-oriented and flexible.  Or use an ORM. +        # logic, do something more object-oriented and flexible. Or use an ORM.          if(self._return_as_map):              expect_rows = 1          if expect_rows is not None and rows != expect_rows: diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 3f14be6e18..d6a9e10707 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -32,7 +32,6 @@ set(llcommon_SOURCE_FILES      llformat.cpp      llframetimer.cpp      llheartbeat.cpp -    llindraconfigfile.cpp      llliveappconfig.cpp      lllivefile.cpp      lllog.cpp @@ -118,7 +117,6 @@ set(llcommon_HEADER_FILES      llheartbeat.h      llhttpstatuscodes.h      llindexedqueue.h -    llindraconfigfile.h      llkeythrottle.h      lllinkedqueue.h      llliveappconfig.h diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 199315f34e..968b92d1e7 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -38,7 +38,9 @@  #include "llerrorcontrol.h"  #include "llerrorthread.h"  #include "llframetimer.h" +#include "lllivefile.h"  #include "llmemory.h" +#include "llstl.h" // for DeletePointer()  #include "lltimer.h"  // @@ -91,7 +93,6 @@ LLAppChildCallback LLApp::sDefaultChildCallback = NULL;  LLApp::LLApp() : mThreadErrorp(NULL)  {  	commonCtor(); -	startErrorThread();  }  void LLApp::commonCtor() @@ -106,9 +107,6 @@ void LLApp::commonCtor()  	sSigChildCount = new LLAtomicU32(0);  #endif -	// Setup error handling -	setupErrorHandling(); -  	// initialize the options structure. We need to make this an array  	// because the structured data will not auto-allocate if we  	// reference an invalid location with the [] operator. @@ -141,6 +139,11 @@ LLApp::~LLApp()  	delete sSigChildCount;  	sSigChildCount = NULL;  #endif + +	// reclaim live file memory +	std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer()); +	mLiveFiles.clear(); +  	setStopped();  	// HACK: wait for the error thread to clean itself  	ms_sleep(20); @@ -214,6 +217,15 @@ bool LLApp::parseCommandOptions(int argc, char** argv)  	return true;  } + +void LLApp::manageLiveFile(LLLiveFile* livefile) +{ +	if(!livefile) return; +	livefile->checkAndReload(); +	livefile->addToEventTimer(); +	mLiveFiles.push_back(livefile); +} +  bool LLApp::setOptionData(OptionPriority level, LLSD data)  {  	if((level < 0) @@ -275,6 +287,7 @@ void LLApp::setupErrorHandling()  #endif +	startErrorThread();  }  void LLApp::startErrorThread() @@ -283,10 +296,13 @@ void LLApp::startErrorThread()  	// Start the error handling thread, which is responsible for taking action  	// when the app goes into the APP_STATUS_ERROR state  	// -	llinfos << "Starting error thread" << llendl; -	mThreadErrorp = new LLErrorThread(); -	mThreadErrorp->setUserData((void *) this); -	mThreadErrorp->start();	 +	if(!mThreadErrorp) +	{ +		llinfos << "Starting error thread" << llendl; +		mThreadErrorp = new LLErrorThread(); +		mThreadErrorp->setUserData((void *) this); +		mThreadErrorp->start(); +	}  }  void LLApp::setErrorHandler(LLAppErrorHandler handler) diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index f8a593c33d..cc60ba0b80 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -40,8 +40,7 @@  // Forward declarations  class LLErrorThread; -class LLApp; - +class LLLiveFile;  typedef void (*LLAppErrorHandler)();  typedef void (*LLAppChildCallback)(int pid, bool exited, int status); @@ -128,6 +127,19 @@ public:  	bool parseCommandOptions(int argc, char** argv);  	/** +	 * @brief Keep track of live files automatically. +	 * +	 * *TODO: it currently uses the <code>addToEventTimer()</code> API +	 * instead of the runner. I should probalby use the runner. +	 * +	 * *NOTE: DO NOT add the livefile instance to any kind of check loop. +	 * +	 * @param livefile A valid instance of an LLLiveFile. This LLApp +	 * instance will delete the livefile instance. +	 */ +	void manageLiveFile(LLLiveFile* livefile); + +	/**  	 * @brief Set the options at the specified priority.  	 *  	 * This function completely replaces the options at the priority @@ -194,11 +206,26 @@ public:  #endif  	static int getPid(); -	// -	// Error handling methods -	// +	/** @name Error handling methods */ +	//@{ +	/** +	 * @brief Do our generic platform-specific error-handling setup -- +	 * signals on unix, structured exceptions on windows. +	 *  +	 * DO call this method if your app will either spawn children or be +	 * spawned by a launcher. +	 * Call just after app object construction. +	 * (Otherwise your app will crash when getting signals, +	 * and will not core dump.) +	 * +	 * DO NOT call this method if your application has specialized +	 * error handling code. +	 */ +	void setupErrorHandling(); +  	void setErrorHandler(LLAppErrorHandler handler);  	void setSyncErrorHandler(LLAppErrorHandler handler); +	//@}  #if !LL_WINDOWS  	// @@ -214,8 +241,9 @@ public:  	void setDefaultChildCallback(LLAppChildCallback callback);       // Fork and do the proper signal handling/error handling mojo -	// WARNING: You need to make sure your signal handling callback is correct after -	// you fork, because not all threads are duplicated when you fork! +	// *NOTE: You need to make sure your signal handling callback is +	// correct after you fork, because not all threads are duplicated +	// when you fork!  	pid_t fork();   #endif @@ -255,7 +283,6 @@ protected:  private:  	void startErrorThread(); -	void setupErrorHandling();		// Do platform-specific error-handling setup (signals, structured exceptions)  	static void runErrorHandler(); // run shortly after we detect an error, ran in the relatively robust context of the LLErrorThread - preferred.  	static void runSyncErrorHandler(); // run IMMEDIATELY when we get an error, ran in the context of the faulting thread. @@ -278,6 +305,8 @@ private:  	// The application options.  	LLSD mOptions; +	// The live files for this application +	std::vector<LLLiveFile*> mLiveFiles;  	//@}  private: diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index e8c95d0a76..d671decccb 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -289,7 +289,7 @@ namespace  	public:  		static LogControlFile& fromDirectory(const std::string& dir); -		virtual void loadFile(); +		virtual bool loadFile();  	private:  		LogControlFile(const std::string &filename) @@ -317,7 +317,7 @@ namespace  			// NB: This instance is never freed  	} -	void LogControlFile::loadFile() +	bool LogControlFile::loadFile()  	{  		LLSD configuration; @@ -333,12 +333,13 @@ namespace  				llwarns << filename() << " missing, ill-formed,"  							" or simply undefined; not changing configuration"  						<< llendl; -				return; +				return false;  			}  		}  		LLError::configure(configuration);  		llinfos << "logging reconfigured from " << filename() << llendl; +		return true;  	} diff --git a/indra/llcommon/llliveappconfig.cpp b/indra/llcommon/llliveappconfig.cpp index e1bfc11a03..75bdfee8b7 100644 --- a/indra/llcommon/llliveappconfig.cpp +++ b/indra/llcommon/llliveappconfig.cpp @@ -38,9 +38,12 @@  #include "llsd.h"  #include "llsdserialize.h" -LLLiveAppConfig::LLLiveAppConfig(LLApp* app, const std::string& filename, F32 refresh_period) -:	LLLiveFile(filename, refresh_period), -	mApp(app) +LLLiveAppConfig::LLLiveAppConfig( +	const std::string& filename, +	F32 refresh_period, +	LLApp::OptionPriority priority) : +	LLLiveFile(filename, refresh_period), +	mPriority(priority)  { } @@ -48,7 +51,7 @@ LLLiveAppConfig::~LLLiveAppConfig()  { }  // virtual  -void LLLiveAppConfig::loadFile() +bool LLLiveAppConfig::loadFile()  {  	llinfos << "LLLiveAppConfig::loadFile(): reading from "  		<< filename() << llendl; @@ -59,12 +62,25 @@ void LLLiveAppConfig::loadFile()          LLSDSerialize::fromXML(config, file);  		if(!config.isMap())  		{ -			llinfos << "LLDataserverConfig::loadFile(): not an map!" +			llwarns << "Live app config not an map in " << filename()  				<< " Ignoring the data." << llendl; -			return; +			return false;  		}  		file.close();      } -	mApp->setOptionData( -		LLApp::PRIORITY_SPECIFIC_CONFIGURATION, config); +	else +	{ +		llinfos << "Live file " << filename() << " does not exit." << llendl; +	} +	// *NOTE: we do not handle the else case here because we would not +	// have attempted to load the file unless LLLiveFile had +	// determined there was a reason to load it. This only happens +	// when either the file has been updated or it is either suddenly +	// in existence or has passed out of existence. Therefore, we want +	// to set the config to an empty config, and return that it +	// changed. + +	LLApp* app = LLApp::instance(); +	if(app) app->setOptionData(mPriority, config); +	return true;  } diff --git a/indra/llcommon/llliveappconfig.h b/indra/llcommon/llliveappconfig.h index 55d84a4778..a6ece6e8b3 100644 --- a/indra/llcommon/llliveappconfig.h +++ b/indra/llcommon/llliveappconfig.h @@ -33,25 +33,43 @@  #ifndef LLLIVEAPPCONFIG_H  #define LLLIVEAPPCONFIG_H +#include "llapp.h"  #include "lllivefile.h" -class LLApp; +/** + * @class LLLiveAppConfig + * @see LLLiveFile + * + * To use this, instantiate a LLLiveAppConfig object inside your main + * loop.  The traditional name for it is live_config.  Be sure to call + * <code>live_config.checkAndReload()</code> periodically. + */  class LLLiveAppConfig : public LLLiveFile  {  public: -	// To use this, instantiate a LLLiveAppConfig object inside your main loop. -	// The traditional name for it is live_config. -	// Be sure to call live_config.checkAndReload() periodically. -	LLLiveAppConfig(LLApp* app, const std::string& filename, F32 refresh_period); -	~LLLiveAppConfig(); +	/** +	 * @brief Constructor +	 * +	 * @param filename. The name of the file for periodically checking +	 * configuration. +	 * @param refresh_period How often the internal timer should +	 * bother checking the filesystem. +	 * @param The application priority level of that configuration file. +	 */ +	LLLiveAppConfig( +		const std::string& filename, +		F32 refresh_period, +		LLApp::OptionPriority priority); + +	~LLLiveAppConfig(); ///< Destructor  protected: -	/*virtual*/ void loadFile(); +	/*virtual*/ bool loadFile();  private: -	LLApp* mApp; +	LLApp::OptionPriority mPriority;  };  #endif diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp index b6f458cb3e..effda6c49c 100644 --- a/indra/llcommon/lllivefile.cpp +++ b/indra/llcommon/lllivefile.cpp @@ -35,14 +35,17 @@  #include "llframetimer.h"  #include "lltimer.h" +const F32 DEFAULT_CONFIG_FILE_REFRESH = 5.0f; + +  class LLLiveFile::Impl  {  public: -	Impl(const std::string &filename, const F32 refresh_period); +	Impl(const std::string& filename, const F32 refresh_period);  	~Impl();  	bool check(); -	 +	void changed();  	bool mForceCheck;  	F32 mRefreshPeriod; @@ -50,16 +53,19 @@ public:  	std::string mFilename;  	time_t mLastModTime; +	time_t mLastStatTime;  	bool mLastExists;  	LLEventTimer* mEventTimer;  }; -LLLiveFile::Impl::Impl(const std::string &filename, const F32 refresh_period) -	: mForceCheck(true), +LLLiveFile::Impl::Impl(const std::string& filename, const F32 refresh_period) +	: +	mForceCheck(true),  	mRefreshPeriod(refresh_period),  	mFilename(filename),  	mLastModTime(0), +	mLastStatTime(0),  	mLastExists(false),  	mEventTimer(NULL)  { @@ -70,7 +76,7 @@ LLLiveFile::Impl::~Impl()  	delete mEventTimer;  } -LLLiveFile::LLLiveFile(const std::string &filename, const F32 refresh_period) +LLLiveFile::LLLiveFile(const std::string& filename, const F32 refresh_period)  	: impl(* new Impl(filename, refresh_period))  {  } @@ -121,17 +127,30 @@ bool LLLiveFile::Impl::check()  	// We want to read the file.  Update status info for the file.  	mLastExists = true; -	mLastModTime = stat_data.st_mtime; -	 +	mLastStatTime = stat_data.st_mtime;  	return true;  } +void LLLiveFile::Impl::changed() +{ +	// we wanted to read this file, and we were successful. +	mLastModTime = mLastStatTime; +} +  bool LLLiveFile::checkAndReload()  {  	bool changed = impl.check();  	if (changed)  	{ -		loadFile(); +		if(loadFile()) +		{ +			impl.changed(); +			this->changed(); +		} +		else +		{ +			changed = false; +		}  	}  	return changed;  } diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h index a3a9cf49ab..89b5d95e44 100644 --- a/indra/llcommon/lllivefile.h +++ b/indra/llcommon/lllivefile.h @@ -33,29 +33,65 @@  #ifndef LL_LLLIVEFILE_H  #define LL_LLLIVEFILE_H -const F32 configFileRefreshRate = 5.0; // seconds +extern const F32 DEFAULT_CONFIG_FILE_REFRESH;  class LLLiveFile  {  public: -	LLLiveFile(const std::string &filename, const F32 refresh_period = 5.f); +	LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f);  	virtual ~LLLiveFile(); +	/** +	 * @brief Check to see if this live file should reload. +	 * +	 * Call this before using anything that was read & cached +	 * from the file. +	 * +	 * This method calls the <code>loadFile()</code> method if +	 * any of: +	 *   file has a new modify time since the last check +	 *   file used to exist and now does not +	 *   file used to not exist but now does +	 * @return Returns true if the file was reloaded. +	 */  	bool checkAndReload(); -		// Returns true if the file changed in any way -		// Call this before using anything that was read & cached from the file +	  	std::string filename() const; +	/** +	 * @brief Add this live file to an automated recheck. +	 * +	 * Normally, just calling checkAndReload() is enough. In some +	 * cases though, you may need to let the live file periodically +	 * check itself. +	 */  	void addToEventTimer(); -		// Normally, just calling checkAndReload() is enough.  In some cases -		// though, you may need to let the live file periodically check itself.  	void setRefreshPeriod(F32 seconds);  protected: -	virtual void loadFile() = 0; // Implement this to load your file if it changed +	/** +	 * @breif Implement this to load your file if it changed. +	 * +	 * This method is called automatically by <code>checkAndReload()</code>, +	 * so though you must implement this in derived classes, you do +	 * not need to call it manually. +	 * @return Returns true if the file was successfully loaded. +	 */ +	virtual bool loadFile() = 0; + +	/** +	 * @brief Implement this method if you want to get a change callback. +	 * +	 * This virtual function will be called automatically at the end +	 * of <code>checkAndReload()</code> if a new configuration was +	 * loaded. This does not track differences between the current and +	 * newly loaded file, so any successful load event will trigger a +	 * <code>changed()</code> callback. Default is to do nothing. +	 */ +	virtual void changed() {}  private:  	class Impl; diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp index e411a1c798..291b019616 100644 --- a/indra/llcommon/llstat.cpp +++ b/indra/llcommon/llstat.cpp @@ -62,7 +62,7 @@ public:      static std::string filename();  protected: -    /* virtual */ void loadFile(); +    /* virtual */ bool loadFile();  public:      void init(LLPerfStats* statsp); @@ -94,12 +94,12 @@ LLStatsConfigFile& LLStatsConfigFile::instance()  /* virtual */  // Load and parse the stats configuration file -void LLStatsConfigFile::loadFile() +bool LLStatsConfigFile::loadFile()  {      if (!mStatsp)      {          llwarns << "Tries to load performance configure file without initializing LPerfStats" << llendl; -        return; +        return false;      }      mChanged = true; @@ -113,7 +113,7 @@ void LLStatsConfigFile::loadFile()              {                  llinfos << "Performance statistics configuration file ill-formed, not recording statistics" << llendl;                  mStatsp->setReportPerformanceDuration( 0.f ); -                return; +                return false;              }          }          else  @@ -123,7 +123,7 @@ void LLStatsConfigFile::loadFile()                  llinfos << "Performance statistics configuration file deleted, not recording statistics" << llendl;                  mStatsp->setReportPerformanceDuration( 0.f );              } -            return; +            return true;          }      } @@ -159,6 +159,7 @@ void LLStatsConfigFile::loadFile()      {          llinfos << "Performance stats recording turned off" << llendl;      } +	return true;  } diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 99a9b9e269..6ba665b8d2 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -228,7 +228,25 @@ public:  	// True if this is the head of s.  	static BOOL	isHead( const std::basic_string<T>& string, const T* s );  -	 + +	/** +	 * @brief Returns true if string starts with substr +	 * +	 * If etither string or substr are empty, this method returns false. +	 */ +	static bool startsWith( +		const std::basic_string<T>& string, +		const std::basic_string<T>& substr); + +	/** +	 * @brief Returns true if string ends in substr +	 * +	 * If etither string or substr are empty, this method returns false. +	 */ +	static bool endsWith( +		const std::basic_string<T>& string, +		const std::basic_string<T>& substr); +  	static void	addCRLF(std::basic_string<T>& string);  	static void	removeCRLF(std::basic_string<T>& string); @@ -335,7 +353,7 @@ public:   * This function works on bytes rather than glyphs, so this will   * incorrectly truncate non-single byte strings.   * Use utf8str_truncate() for utf8 strings - * @return a copy of in string minus the trailing count characters. + * @return a copy of in string minus the trailing count bytes.   */  inline std::string chop_tail_copy(  	const std::string& in, @@ -1065,6 +1083,30 @@ BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s  	}  } +// static +template<class T>  +bool LLStringUtilBase<T>::startsWith( +	const std::basic_string<T>& string, +	const std::basic_string<T>& substr) +{ +	if(string.empty() || (substr.empty())) return false; +	if(0 == string.find(substr)) return true; +	return false; +} + +// static +template<class T>  +bool LLStringUtilBase<T>::endsWith( +	const std::basic_string<T>& string, +	const std::basic_string<T>& substr) +{ +	if(string.empty() || (substr.empty())) return false; +	std::string::size_type idx = string.rfind(substr); +	if(std::string::npos == idx) return false; +	return (idx == (string.size() - substr.size())); +} + +  template<class T>   BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)  { diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index 3dbc837875..f6e8f01f0e 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -162,11 +162,10 @@ namespace  		{ return LLURI::escape(s, unreserved() + ":@!$'()*+,="); }	// sub_delims - "&;" + ":@"  } -// *TODO: Consider using curl. After http textures gets merged everywhere. -// static +//static  std::string LLURI::escape(const std::string& str)  { -	static std::string default_allowed(unreserved() + ":@!$'()*+,=/?&#;"); +	static std::string default_allowed = unreserved();  	static bool initialized = false;  	if(!initialized)  	{ diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index 156d80b97e..8e46e2e89e 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -127,27 +127,16 @@ public:  	/** @name Escaping Utilities */  	//@{  	/** -	 * @brief Escape a raw url with a reasonable set of allowed characters. -	 * -	 * The default set was chosen to match HTTP urls and general -     *  guidelines for naming resources. Passing in a raw url does not -     *  produce well defined results because you really need to know -     *  which segments are path parts because path parts are supposed -     *  to be escaped individually. The default set chosen is: +	 * @brief Escape the string passed except for unreserved  	 *  	 *  ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz  	 *  0123456789  	 *  -._~ -	 *  :@!$'()*+,=/?&#;  	 * -	 * *NOTE: This API is basically broken because it does not -     *  allow you to specify significant path characters. For example, -     *  if the filename actually contained a /, then you cannot use -     *  this function to generate the serialized url for that -     *  resource. +	 * @see http://www.ietf.org/rfc/rfc1738.txt  	 *  	 * @param str The raw URI to escape. -	 * @return Returns the escaped uri or an empty string. +	 * @return Returns the rfc 1738 escaped uri or an empty string.  	 */  	static std::string escape(const std::string& str); diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 78c4f8e742..2fd37e848e 100755 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -89,7 +89,8 @@ LLCrashLogger::LLCrashLogger() :  	mSentCrashLogs(false),  	mCrashHost("")  { - +	// Set up generic error handling +	setupErrorHandling();  }  LLCrashLogger::~LLCrashLogger() diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 9c27476b0a..a0b27c788f 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -175,7 +175,7 @@ void LLParcel::init(const LLUUID &owner_id,  	mSaleTimerExpires.stop();  	mGraceExtension = 0;  	//mExpireAction = STEA_REVERT; -	mRecordTransaction = FALSE; +	//mRecordTransaction = FALSE;  	mAuctionID = 0;  	mInEscrow = false; diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index 6f5ae87ebd..40bbb7b2e0 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -413,12 +413,6 @@ public:  	void completeSale(U32& type, U8& flags, LLUUID& to_id);  	void clearSale(); -	// this function returns TRUE if the parcel needs conversion to a -	// lease from a non-owned-status state. -	BOOL getRecordTransaction() const { return mRecordTransaction; } -	void setRecordTransaction(BOOL record) { mRecordTransaction = record; } - -  	// more accessors  	U32		getParcelFlags() const			{ return mParcelFlags; } @@ -596,8 +590,6 @@ protected:  	ELandingType mLandingType;  	LLTimer mSaleTimerExpires;  	S32 mGraceExtension; -	BOOL mRecordTransaction; -	  	// This value is non-zero if there is an auction associated with  	// the parcel. diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 307d9b92fa..8b90a4c5ca 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -224,6 +224,10 @@ static void request(  	LLURLRequest* req = new LLURLRequest(method, url);  	req->checkRootCertificate(true); +	 +	lldebugs << LLURLRequest::actionAsVerb(method) << " " << url << " " +		<< headers << llendl; +      // Insert custom headers is the caller sent any      if (headers.isMap())      { @@ -375,72 +379,140 @@ private:  	std::string mBuffer;  }; -// *TODO: Deprecate (only used by dataserver) -// This call is blocking! This is probably usually bad. :( -LLSD LLHTTPClient::blockingGet(const std::string& url) +// These calls are blocking! This is usually bad, unless you're a dataserver. Then it's awesome. + +/** +	@brief does a blocking request on the url, returning the data or bad status. + +	@param url URI to verb on. +	@param method the verb to hit the URI with. +	@param body the body of the call (if needed - for instance not used for GET and DELETE, but is for POST and PUT) +	@param headers HTTP headers to use for the request. +	@param timeout Curl timeout to use. Defaults to 5. Rationale: +	Without this timeout, blockingGet() calls have been observed to take +	up to 90 seconds to complete.  Users of blockingGet() already must  +	check the HTTP return code for validity, so this will not introduce +	new errors.  A 5 second timeout will succeed > 95% of the time (and  +	probably > 99% of the time) based on my statistics. JC + +	@returns an LLSD map: {status: integer, body: map} +  */ +static LLSD blocking_request( +	const std::string& url, +	LLURLRequest::ERequestAction method, +	const LLSD& body, +	const LLSD& headers = LLSD(), +	const F32 timeout = 5 +)  { -	llinfos << "blockingGet of " << url << llendl; - -	// Returns an LLSD map: {status: integer, body: map} -	char curl_error_buffer[CURL_ERROR_SIZE]; +	lldebugs << "blockingRequest of " << url << llendl; +	char curl_error_buffer[CURL_ERROR_SIZE] = "\0";  	CURL* curlp = curl_easy_init(); -  	LLHTTPBuffer http_buffer; - -	// Without this timeout, blockingGet() calls have been observed to take -	// up to 90 seconds to complete.  Users of blockingGet() already must  -	// check the HTTP return code for validity, so this will not introduce -	// new errors.  A 5 second timeout will succeed > 95% of the time (and  -	// probably > 99% of the time) based on my statistics. JC +	std::string body_str; +	 +	// other request method checks root cert first, we skip? +	//req->checkRootCertificate(true); +	 +	// * Set curl handle options  	curl_easy_setopt(curlp, CURLOPT_NOSIGNAL, 1);	// don't use SIGALRM for timeouts -	curl_easy_setopt(curlp, CURLOPT_TIMEOUT, 5);	// seconds - +	curl_easy_setopt(curlp, CURLOPT_TIMEOUT, timeout);	// seconds, see warning at top of function.  	curl_easy_setopt(curlp, CURLOPT_WRITEFUNCTION, LLHTTPBuffer::curl_write);  	curl_easy_setopt(curlp, CURLOPT_WRITEDATA, &http_buffer);  	curl_easy_setopt(curlp, CURLOPT_URL, url.c_str());  	curl_easy_setopt(curlp, CURLOPT_ERRORBUFFER, curl_error_buffer); -	curl_easy_setopt(curlp, CURLOPT_FAILONERROR, 1); - -	struct curl_slist *header_list = NULL; -	header_list = curl_slist_append(header_list, "Accept: application/llsd+xml"); -	CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, header_list); +	 +	// * Setup headers (don't forget to free them after the call!) +	curl_slist* headers_list = NULL; +	if (headers.isMap()) +	{ +		LLSD::map_const_iterator iter = headers.beginMap(); +		LLSD::map_const_iterator end  = headers.endMap(); +		for (; iter != end; ++iter) +		{ +			std::ostringstream header; +			header << iter->first << ": " << iter->second.asString() ; +			lldebugs << "header = " << header.str() << llendl; +			headers_list = curl_slist_append(headers_list, header.str().c_str()); +		} +	} +	 +	// * Setup specific method / "verb" for the URI (currently only GET and POST supported + poppy) +	if (method == LLURLRequest::HTTP_GET) +	{ +		curl_easy_setopt(curlp, CURLOPT_HTTPGET, 1); +	} +	else if (method == LLURLRequest::HTTP_POST) +	{ +		curl_easy_setopt(curlp, CURLOPT_POST, 1); +		//serialize to ostr then copy to str - need to because ostr ptr is unstable :( +		std::ostringstream ostr; +		LLSDSerialize::toXML(body, ostr); +		body_str = ostr.str(); +		curl_easy_setopt(curlp, CURLOPT_POSTFIELDS, body_str.c_str()); +		//copied from PHP libs, correct? +		headers_list = curl_slist_append(headers_list, "Content-Type: application/llsd+xml"); + +		// copied from llurlrequest.cpp +		// it appears that apache2.2.3 or django in etch is busted. If +		// we do not clear the expect header, we get a 500. May be +		// limited to django/mod_wsgi. +		headers_list = curl_slist_append(headers_list, "Expect:"); +	} +	 +	// * Do the action using curl, handle results +	lldebugs << "HTTP body: " << body_str << llendl; +	headers_list = curl_slist_append(headers_list, "Accept: application/llsd+xml"); +	CURLcode curl_result = curl_easy_setopt(curlp, CURLOPT_HTTPHEADER, headers_list);  	if ( curl_result != CURLE_OK )  	{ -		llinfos << "Curl is hosed - can't add Accept header for llsd+xml" << llendl; +		llinfos << "Curl is hosed - can't add headers" << llendl;  	}  	LLSD response = LLSD::emptyMap(); -  	S32 curl_success = curl_easy_perform(curlp); -  	S32 http_status = 499; -	curl_easy_getinfo(curlp,CURLINFO_RESPONSE_CODE, &http_status); - +	curl_easy_getinfo(curlp, CURLINFO_RESPONSE_CODE, &http_status);  	response["status"] = http_status; - -	if (curl_success != 0  -		&& http_status != 404)  // We expect 404s, don't spam for them. +	// if we get a non-404 and it's not a 200 OR maybe it is but you have error bits, +	if ( http_status != 404 && (http_status != 200 || curl_success != 0) )  	{ +		// We expect 404s, don't spam for them. +		llwarns << "CURL REQ URL: " << url << llendl; +		llwarns << "CURL REQ METHOD TYPE: " << method << llendl; +		llwarns << "CURL REQ HEADERS: " << headers.asString() << llendl; +		llwarns << "CURL REQ BODY: " << body_str << llendl; +		llwarns << "CURL HTTP_STATUS: " << http_status << llendl;  		llwarns << "CURL ERROR: " << curl_error_buffer << llendl; -		 +		llwarns << "CURL ERROR BODY: " << http_buffer.asString() << llendl;  		response["body"] = http_buffer.asString();  	}  	else  	{  		response["body"] = http_buffer.asLLSD(); +		lldebugs << "CURL response: " << http_buffer.asString() << llendl;  	} -	if(header_list) +	if(headers_list)  	{	// free the header list   -		curl_slist_free_all(header_list);  -		header_list = NULL; +		curl_slist_free_all(headers_list);   	} +	// * Cleanup  	curl_easy_cleanup(curlp); -  	return response;  } +LLSD LLHTTPClient::blockingGet(const std::string& url) +{ +	return blocking_request(url, LLURLRequest::HTTP_GET, LLSD()); +} + +LLSD LLHTTPClient::blockingPost(const std::string& url, const LLSD& body) +{ +	return blocking_request(url, LLURLRequest::HTTP_POST, body); +} +  void LLHTTPClient::put(  	const std::string& url,  	const LLSD& body, diff --git a/indra/llmessage/llhttpclient.h b/indra/llmessage/llhttpclient.h index a0c9fac77f..3d0646e5fe 100644 --- a/indra/llmessage/llhttpclient.h +++ b/indra/llmessage/llhttpclient.h @@ -142,6 +142,14 @@ public:  	 */  	static LLSD blockingGet(const std::string& url); +	/** +	 * @brief Blocking HTTP POST that returns an LLSD map of status and body. +	 * +	 * @param url the complete serialized (and escaped) url to get +	 * @param body the LLSD post body +	 * @return An LLSD of { 'status':status (an int), 'body':payload (an LLSD) } +	 */ +	static LLSD blockingPost(const std::string& url, const LLSD& body);  	static void setPump(LLPumpIO& pump); diff --git a/indra/llmessage/llmessageconfig.cpp b/indra/llmessage/llmessageconfig.cpp index d4279354b6..dff0a3844c 100644 --- a/indra/llmessage/llmessageconfig.cpp +++ b/indra/llmessage/llmessageconfig.cpp @@ -66,7 +66,7 @@ public:  	static LLMessageConfigFile& instance();  		// return the singleton configuration file -	/* virtual */ void loadFile(); +	/* virtual */ bool loadFile();  	void loadServerDefaults(const LLSD& data);  	void loadMaxQueuedEvents(const LLSD& data);  	void loadMessages(const LLSD& data); @@ -98,7 +98,7 @@ LLMessageConfigFile& LLMessageConfigFile::instance()  }  // virtual -void LLMessageConfigFile::loadFile() +bool LLMessageConfigFile::loadFile()  {  	LLSD data;      { @@ -115,7 +115,7 @@ void LLMessageConfigFile::loadFile()              LL_INFOS("AppInit") << "LLMessageConfigFile::loadFile: file missing,"  				" ill-formed, or simply undefined; not changing the"  				" file" << LL_ENDL; -            return; +            return false;          }      }  	loadServerDefaults(data); @@ -123,6 +123,7 @@ void LLMessageConfigFile::loadFile()  	loadMessages(data);  	loadCapBans(data);  	loadMessageBans(data); +	return true;  }  void LLMessageConfigFile::loadServerDefaults(const LLSD& data) diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 46e976fe35..3ab8057abb 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -98,6 +98,26 @@ LLURLRequestDetail::~LLURLRequestDetail()   * class LLURLRequest   */ +// static +std::string LLURLRequest::actionAsVerb(LLURLRequest::ERequestAction action) +{ +	static const std::string VERBS[] = +	{ +		"(invalid)", +		"HEAD", +		"GET", +		"PUT", +		"POST", +		"DELETE", +		"MOVE" +	}; +	if(((S32)action <=0) || ((S32)action >= REQUEST_ACTION_COUNT)) +	{ +		return VERBS[0]; +	} +	return VERBS[action]; +} +  LLURLRequest::LLURLRequest(LLURLRequest::ERequestAction action) :  	mAction(action)  { diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index d1facbff0f..86ef71f085 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -81,6 +81,11 @@ public:  		REQUEST_ACTION_COUNT  	}; +	/** +	 * @brief Turn the requst action into an http verb. +	 */ +	static std::string actionAsVerb(ERequestAction action); +  	/**   	 * @brief Constructor.  	 * diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index b25b27eb0f..0f3576732d 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -509,6 +509,22 @@ private:  public:  	// BOOL	decodeData(const U8 *buffer, const LLHost &host); +	/** +	gets binary data from the current message. +	 +	@param blockname the name of the block in the message (from the message template) + +	@param varname  + +	@param datap +	 +	@param size expected size - set to zero to get any amount of data up to max_size. +	Make sure max_size is set in that case! + +	@param blocknum + +	@param max_size the max number of bytes to read +	*/  	void	getBinaryDataFast(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX);  	void	getBinaryData(const char *blockname, const char *varname, void *datap, S32 size, S32 blocknum = 0, S32 max_size = S32_MAX);  	void	getBOOLFast(	const char *block, const char *var, BOOL &data, S32 blocknum = 0); diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f2154a05dc..640b835da2 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -523,6 +523,7 @@ LLAppViewer::LLAppViewer() :  		llerrs << "Oh no! An instance of LLAppViewer already exists! LLAppViewer is sort of like a singleton." << llendl;  	} +	setupErrorHandling();  	sInstance = this;  } diff --git a/scripts/messages/message_template.msg b/scripts/messages/message_template.msg index a65b4a3071..f7230cfdf6 100644 --- a/scripts/messages/message_template.msg +++ b/scripts/messages/message_template.msg @@ -2720,7 +2720,7 @@ version 2.0  // end viewer to simulator section  { -       ViewerStats Low 131 NotTrusted Zerocoded +       ViewerStats Low 131 NotTrusted Zerocoded UDPDeprecated         {                 AgentData                       Single                 {       AgentID                 LLUUID      }  | 
