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 } |