diff options
author | Christian Goetze <cg@lindenlab.com> | 2007-12-01 01:29:28 +0000 |
---|---|---|
committer | Christian Goetze <cg@lindenlab.com> | 2007-12-01 01:29:28 +0000 |
commit | 7b7dd4e6f61aa731ef7f9f255014f5b76d5b9fec (patch) | |
tree | 08bf4cb9354f975940b83cd695a2aa8bb8d74870 | |
parent | 5c53c187fff573ef73a643949b3bded1084b0983 (diff) |
svn merge -r74808:74832 svn+ssh://svn/svn/linden/branches/qa-dpo-9a
-rw-r--r-- | etc/message.xml | 2 | ||||
-rw-r--r-- | indra/lib/python/indra/base/lluuid.py | 4 | ||||
-rw-r--r-- | indra/lib/python/indra/ipc/mysql_pool.py | 2 | ||||
-rw-r--r-- | indra/lib/python/indra/util/named_query.py | 387 | ||||
-rw-r--r-- | indra/llcommon/llsd.cpp | 39 | ||||
-rw-r--r-- | indra/llcommon/llsd.h | 10 | ||||
-rw-r--r-- | indra/llmessage/llservicebuilder.cpp | 99 | ||||
-rw-r--r-- | indra/llmessage/llurlrequest.cpp | 1 | ||||
-rw-r--r-- | indra/llmessage/llurlrequest.h | 2 | ||||
-rw-r--r-- | indra/llmessage/message.cpp | 11 | ||||
-rw-r--r-- | indra/llmessage/message.h | 7 | ||||
-rw-r--r-- | indra/newview/llstartup.cpp | 2 | ||||
-rw-r--r-- | indra/newview/llviewermessage.cpp | 36 | ||||
-rw-r--r-- | indra/newview/llviewermessage.h | 2 | ||||
-rw-r--r-- | indra/newview/llviewerprecompiledheaders.h | 1 | ||||
-rw-r--r-- | indra/test/llservicebuilder_tut.cpp | 61 |
16 files changed, 577 insertions, 89 deletions
diff --git a/etc/message.xml b/etc/message.xml index 1e6e30ec6c..18f61198ce 100644 --- a/etc/message.xml +++ b/etc/message.xml @@ -362,7 +362,7 @@ <boolean>false</boolean> </map> - <key>avatarpickrequest</key> + <key>avatarpicksrequest</key> <map> <key>service_name</key> <string>avatar-pick</string> diff --git a/indra/lib/python/indra/base/lluuid.py b/indra/lib/python/indra/base/lluuid.py index eb0c557b04..609c8cc261 100644 --- a/indra/lib/python/indra/base/lluuid.py +++ b/indra/lib/python/indra/base/lluuid.py @@ -212,6 +212,10 @@ class UUID(object): _int2binstr(v3,4) + \ _int2binstr(v4,4) + +# module-level null constant +NULL = UUID() + def printTranslatedMemory(four_hex_uints): """ We expect to get the string as four hex units. eg: diff --git a/indra/lib/python/indra/ipc/mysql_pool.py b/indra/lib/python/indra/ipc/mysql_pool.py index 4a265a1f2e..6288a4c732 100644 --- a/indra/lib/python/indra/ipc/mysql_pool.py +++ b/indra/lib/python/indra/ipc/mysql_pool.py @@ -97,7 +97,7 @@ class ConnectionPool(Pool): except (AttributeError, DeadProcess), e: conn = self.create() # TODO figure out if we're still connected to the database - if conn: + if conn is not None: Pool.put(self, conn) else: self.current_size -= 1 diff --git a/indra/lib/python/indra/util/named_query.py b/indra/lib/python/indra/util/named_query.py index 019eb6306b..c3bdd046fd 100644 --- a/indra/lib/python/indra/util/named_query.py +++ b/indra/lib/python/indra/util/named_query.py @@ -28,20 +28,33 @@ THE SOFTWARE. $/LicenseInfo$ """ +import errno import MySQLdb +import MySQLdb.cursors import os import os.path +import re import time +#import sys # *TODO: remove. only used in testing. +#import pprint # *TODO: remove. only used in testing. + +try: + set = set +except NameError: + from sets import Set as set + from indra.base import llsd from indra.base import config -from indra.ipc import russ _g_named_manager = None -# this function is entirely intended for testing purposes, -# because it's tricky to control the config from inside a test def _init_g_named_manager(sql_dir = None): + """Initializes a global NamedManager object to point at a + specified named queries hierarchy. + + This function is intended entirely for testing purposes, + because it's tricky to control the config from inside a test.""" if sql_dir is None: sql_dir = config.get('named-query-base-dir') global _g_named_manager @@ -49,14 +62,14 @@ def _init_g_named_manager(sql_dir = None): os.path.abspath(os.path.realpath(sql_dir))) def get(name): - "@brief get the named query object to be used to perform queries" + "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) -def sql(name, params): +def sql(connection, name, params): # use module-global NamedQuery object to perform default substitution - return get(name).sql(params) + return get(name).sql(connection, params) def run(connection, name, params, expect_rows = None): """\ @@ -72,66 +85,243 @@ Note that this function will fetch ALL rows. return get(name).run(connection, params, expect_rows) class ExpectationFailed(Exception): + """ Exception that is raised when an expectation for an sql query + is not met.""" def __init__(self, message): + Exception.__init__(self, message) self.message = message class NamedQuery(object): def __init__(self, name, filename): - self._stat_interval = 5000 # 5 seconds + """ Construct a NamedQuery object. The name argument is an + arbitrary name as a handle for the query, and the filename is + a path to a file containing an llsd named query document.""" + self._stat_interval_seconds = 5 # 5 seconds self._name = name self._location = filename + self._alternative = dict() + self._last_mod_time = 0 + self._last_check_time = 0 + self.deleted = False self.load_contents() def name(self): + """ The name of the query. """ return self._name def get_modtime(self): - return os.path.getmtime(self._location) + """ Returns the mtime (last modified time) of the named query + file, if such exists.""" + if self._location: + return os.path.getmtime(self._location) + return 0 def load_contents(self): - self._contents = llsd.parse(open(self._location).read()) + """ Loads and parses the named query file into self. Does + nothing if self.location is nonexistant.""" + if self._location: + self._reference_contents(llsd.parse(open(self._location).read())) + # Check for alternative implementations + try: + for name, alt in self._contents['alternative'].items(): + nq = NamedQuery(name, None) + nq._reference_contents(alt) + self._alternative[name] = nq + except KeyError, e: + pass + self._last_mod_time = self.get_modtime() + self._last_check_time = time.time() + + def _reference_contents(self, contents): + "Helper method which builds internal structure from parsed contents" + self._contents = contents self._ttl = int(self._contents.get('ttl', 0)) self._return_as_map = bool(self._contents.get('return_as_map', False)) self._legacy_dbname = self._contents.get('legacy_dbname', None) - self._legacy_query = self._contents.get('legacy_query', None) - self._options = self._contents.get('options', {}) - self._base_query = self._contents['base_query'] - self._last_mod_time = self.get_modtime() - self._last_check_time = time.time() + # reset these before doing the sql conversion because we will + # read them there. reset these while loading so we pick up + # changes. + self._around = set() + self._append = set() + self._integer = set() + self._options = self._contents.get('dynamic_where', {}) + for key in self._options: + if isinstance(self._options[key], basestring): + self._options[key] = self._convert_sql(self._options[key]) + elif isinstance(self._options[key], list): + lines = [] + for line in self._options[key]: + lines.append(self._convert_sql(line)) + self._options[key] = lines + else: + moreopt = {} + for kk in self._options[key]: + moreopt[kk] = self._convert_sql(self._options[key][kk]) + self._options[key] = moreopt + self._base_query = self._convert_sql(self._contents['base_query']) + self._query_suffix = self._convert_sql( + self._contents.get('query_suffix', '')) + + def _convert_sql(self, sql): + """convert the parsed sql into a useful internal structure. + + This function has to turn the named query format into a pyformat + style. It also has to look for %:name% and :name% and + ready them for use in LIKE statements""" + if sql: + #print >>sys.stderr, "sql:",sql + expr = re.compile("(%?):([a-zA-Z][a-zA-Z0-9_-]*)%") + sql = expr.sub(self._prepare_like, sql) + expr = re.compile("#:([a-zA-Z][a-zA-Z0-9_-]*)") + sql = expr.sub(self._prepare_integer, sql) + expr = re.compile(":([a-zA-Z][a-zA-Z0-9_-]*)") + sql = expr.sub("%(\\1)s", sql) + return sql + + def _prepare_like(self, match): + """This function changes LIKE statement replace behavior + + It works by turning %:name% to %(_name_around)s and :name% to + %(_name_append)s. Since a leading '_' is not a valid keyname + input (enforced via unit tests), it will never clash with + existing keys. Then, when building the statement, the query + runner will generate corrected strings.""" + if match.group(1) == '%': + # there is a leading % so this is treated as prefix/suffix + self._around.add(match.group(2)) + return "%(" + self._build_around_key(match.group(2)) + ")s" + else: + # there is no leading %, so this is suffix only + self._append.add(match.group(2)) + return "%(" + self._build_append_key(match.group(2)) + ")s" + + def _build_around_key(self, key): + return "_" + key + "_around" + + def _build_append_key(self, key): + return "_" + key + "_append" + + def _prepare_integer(self, match): + """This function adjusts the sql for #:name replacements + + It works by turning #:name to %(_name_as_integer)s. Since a + leading '_' is not a valid keyname input (enforced via unit + tests), it will never clash with existing keys. Then, when + building the statement, the query runner will generate + corrected strings.""" + self._integer.add(match.group(1)) + return "%(" + self._build_integer_key(match.group(1)) + ")s" + + def _build_integer_key(self, key): + return "_" + key + "_as_integer" + + def _strip_wildcards_to_list(self, value): + """Take string, and strip out the LIKE special characters. + + Technically, this is database dependant, but postgresql and + mysql use the same wildcards, and I am not aware of a general + way to handle this. I think you need a sql statement of the + form: + + LIKE_STRING( [ANY,ONE,str]... ) + + which would treat ANY as their any string, and ONE as their + single glyph, and str as something that needs database + specific encoding to not allow any % or _ to affect the query. + + As it stands, I believe it's impossible to write a named query + style interface which uses like to search the entire space of + text available. Imagine the query: + + % of brain used by average linden + + In order to search for %, it must be escaped, so once you have + escaped the string to not do wildcard searches, and be escaped + for the database, and then prepended the wildcard you come + back with one of: + + 1) %\% of brain used by average linden + 2) %%% of brain used by average linden + + Then, when passed to the database to be escaped to be database + safe, you get back: + + 1) %\\% of brain used by average linden + : which means search for any character sequence, followed by a + backslash, followed by any sequence, followed by ' of + brain...' + 2) %%% of brain used by average linden + : which (I believe) means search for a % followed by any + character sequence followed by 'of brain...' + + Neither of which is what we want! + + So, we need a vendor (or extention) for LIKE_STRING. Anyone + want to write it?""" + utf8_value = unicode(value, "utf-8") + esc_list = [] + remove_chars = set(u"%_") + for glyph in utf8_value: + if glyph in remove_chars: + continue + esc_list.append(glyph.encode("utf-8")) + return esc_list + + def delete(self): + """ Makes this query unusable by deleting all the members and + setting the deleted member. This is desired when the on-disk + query has been deleted but the in-memory copy remains.""" + # blow away all members except _name, _location, and deleted + name, location = self._name, self._location + for key in self.__dict__.keys(): + del self.__dict__[key] + self.deleted = True + self._name, self._location = name, location def ttl(self): + """ Estimated time to live of this query. Used for web + services to set the Expires header.""" return self._ttl def legacy_dbname(self): return self._legacy_dbname - def legacy_query(self): - return self._legacy_query - def return_as_map(self): + """ Returns true if this query is configured to return its + results as a single map (as opposed to a list of maps, the + normal behavior).""" + return self._return_as_map - def run(self, connection, params, expect_rows = None, use_dictcursor = True): - """\ -@brief given a connection, run a named query with the params + def for_schema(self, db_name): + "Look trough the alternates and return the correct query" + try: + return self._alternative[db_name] + except KeyError, e: + pass + return self -Note that this function will fetch ALL rows. We do this because it -opens and closes the cursor to generate the values, and this isn't a generator so the -cursor has no life beyond the method call. -@param cursor The connection to use (this generates its own cursor for the query) -@param name The name of the query to run -@param params The parameters passed into the query -@param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. -@param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts. -@return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict. + def run(self, connection, params, expect_rows = None, use_dictcursor = True): + """given a connection, run a named query with the params + + Note that this function will fetch ALL rows. We do this because it + opens and closes the cursor to generate the values, and this + isn't a generator so the cursor has no life beyond the method call. + + @param cursor The connection to use (this generates its own cursor for the query) + @param name The name of the query to run + @param params The parameters passed into the query + @param expect_rows The number of rows expected. Set to 1 if return_as_map is true. Raises ExpectationFailed if the number of returned rows doesn't exactly match. Kind of a hack. + @param use_dictcursor Set to false to use a normal cursor and manually convert the rows to dicts. + @return Returns the result set as a list of dicts, or, if the named query has return_as_map set to true, returns a single dict. """ if use_dictcursor: cursor = connection.cursor(MySQLdb.cursors.DictCursor) else: cursor = connection.cursor() - statement = self.sql(params) + statement = self.sql(connection, params) #print "SQL:", statement rows = cursor.execute(statement) @@ -169,47 +359,152 @@ cursor has no life beyond the method call. return result_set[0] return result_set - def sql(self, params): + def sql(self, connection, params): + """ Generates an SQL statement from the named query document + and a dictionary of parameters. + + """ self.refresh() # build the query from the options available and the params base_query = [] base_query.append(self._base_query) + #print >>sys.stderr, "base_query:",base_query for opt, extra_where in self._options.items(): - if opt in params and (params[opt] == 0 or params[opt]): - if type(extra_where) in (dict, list, tuple): + if type(extra_where) in (dict, list, tuple): + if opt in params: base_query.append(extra_where[params[opt]]) - else: + else: + if opt in params and params[opt]: base_query.append(extra_where) - + if self._query_suffix: + base_query.append(self._query_suffix) + #print >>sys.stderr, "base_query:",base_query full_query = '\n'.join(base_query) - - # do substitution - sql = russ.format(full_query, params) + + # Go through the query and rewrite all of the ones with the + # @:name syntax. + rewrite = _RewriteQueryForArray(params) + expr = re.compile("@%\(([a-zA-Z][a-zA-Z0-9_-]*)\)s") + full_query = expr.sub(rewrite.operate, full_query) + params.update(rewrite.new_params) + + # build out the params for like. We only have to do this + # parameters which were detected to have ued the where syntax + # during load. + # + # * treat the incoming string as utf-8 + # * strip wildcards + # * append or prepend % as appropriate + new_params = {} + for key in params: + if key in self._around: + new_value = ['%'] + new_value.extend(self._strip_wildcards_to_list(params[key])) + new_value.append('%') + new_params[self._build_around_key(key)] = ''.join(new_value) + if key in self._append: + new_value = self._strip_wildcards_to_list(params[key]) + new_value.append('%') + new_params[self._build_append_key(key)] = ''.join(new_value) + if key in self._integer: + new_params[self._build_integer_key(key)] = int(params[key]) + params.update(new_params) + + # do substitution using the mysql (non-standard) 'literal' + # function to do the escaping. + sql = full_query % connection.literal(params) return sql def refresh(self): - # only stat the file every so often + """ Refresh self from the file on the filesystem. + + This is optimized to be callable as frequently as you wish, + without adding too much load. It does so by only stat-ing the + file every N seconds, where N defaults to 5 and is + configurable through the member _stat_interval_seconds. If the stat + reveals that the file has changed, refresh will re-parse the + contents of the file and use them to update the named query + instance. If the stat reveals that the file has been deleted, + refresh will call self.delete to make the in-memory + representation unusable.""" now = time.time() - if(now - self._last_check_time > self._stat_interval): + if(now - self._last_check_time > self._stat_interval_seconds): self._last_check_time = now - modtime = self.get_modtime() - if(modtime > self._last_mod_time): - self.load_contents() + try: + modtime = self.get_modtime() + if(modtime > self._last_mod_time): + self.load_contents() + except OSError, e: + if e.errno == errno.ENOENT: # file not found + self.delete() # clean up self + raise # pass the exception along to the caller so they know that this query disappeared class NamedQueryManager(object): + """ Manages the lifespan of NamedQuery objects, drawing from a + directory hierarchy of named query documents. + + In practice this amounts to a memory cache of NamedQuery objects.""" + def __init__(self, named_queries_dir): + """ Initializes a manager to look for named queries in a + directory.""" self._dir = os.path.abspath(os.path.realpath(named_queries_dir)) self._cached_queries = {} - def sql(self, name, params): + def sql(self, connection, name, params): nq = self.get(name) - return nq.sql(params) + return nq.sql(connection, params) def get(self, name): - # new up/refresh a NamedQuery based on the name + """ Returns a NamedQuery instance based on the name, either + from memory cache, or by parsing from disk. + + The name is simply a relative path to the directory associated + with the manager object. Before returning the instance, the + NamedQuery object is cached in memory, so that subsequent + accesses don't have to read from disk or do any parsing. This + means that NamedQuery objects returned by this method are + shared across all users of the manager object. + NamedQuery.refresh is used to bring the NamedQuery objects in + sync with the actual files on disk.""" nq = self._cached_queries.get(name) if nq is None: nq = NamedQuery(name, os.path.join(self._dir, name)) self._cached_queries[name] = nq + else: + try: + nq.refresh() + except OSError, e: + if e.errno == errno.ENOENT: # file not found + del self._cached_queries[name] + raise # pass exception along to caller so they know that the query disappeared + return nq + +class _RewriteQueryForArray(object): + "Helper class for rewriting queries with the @:name syntax" + def __init__(self, params): + self.params = params + self.new_params = dict() + + def operate(self, match): + "Given a match, return the string that should be in use" + key = match.group(1) + value = self.params[key] + if type(value) in (list,tuple): + rv = [] + for idx in range(len(value)): + new_key = "_" + key + "_" + str(idx) + self.new_params[new_key] = value[idx] + rv.append("%(" + new_key + ")s") + return ','.join(rv) + else: + # not something that can be expanded, so just drop the + # leading @ in the front of the match. This will mean that + # the single value we have, be it a string, int, whatever + # (other than dict) will correctly show up, eg: + # + # where foo in (@:foobar) -- foobar is a string, so we get + # where foo in (:foobar) + return match.group(0)[1:] diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 829ea25e38..ecca4c9b71 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -35,6 +35,7 @@ #include "llerror.h" #include "../llmath/llmath.h" #include "llformat.h" +#include "llsdserialize.h" #ifndef LL_RELEASE_FOR_DOWNLOAD #define NAME_UNNAMED_NAMESPACE @@ -765,6 +766,44 @@ const LLSD& LLSD::operator[](Integer i) const U32 LLSD::allocationCount() { return Impl::sAllocationCount; } U32 LLSD::outstandingCount() { return Impl::sOutstandingCount; } +static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat) +{ + // sStorage is used to hold the string representation of the llsd last + // passed into this function. If this function is never called (the + // normal case when not debugging), nothing is allocated. Otherwise + // sStorage will point to the result of the last call. This will actually + // be one leak, but since this is used only when running under the + // debugger, it should not be an issue. + static char *sStorage = NULL; + delete[] sStorage; + std::string out_string; + { + std::ostringstream out; + if (useXMLFormat) + out << LLSDXMLStreamer(llsd); + else + out << LLSDNotationStreamer(llsd); + out_string = out.str(); + } + int len = out_string.length(); + sStorage = new char[len + 1]; + memcpy(sStorage, out_string.c_str(), len); + sStorage[len] = '\0'; + return sStorage; +} + +/// Returns XML version of llsd -- only to be called from debugger +const char *LLSD::dumpXML(const LLSD &llsd) +{ + return llsd_dump(llsd, true); +} + +/// Returns Notation version of llsd -- only to be called from debugger +const char *LLSD::dump(const LLSD &llsd) +{ + return llsd_dump(llsd, false); +} + LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); } LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); } LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); } diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 65ba7ddc4f..1ba57b1e95 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -331,6 +331,16 @@ public: static U32 allocationCount(); ///< how many Impls have been made static U32 outstandingCount(); ///< how many Impls are still alive //@} + +private: + /** @name Debugging Interface */ + //@{ + /// Returns XML version of llsd -- only to be called from debugger + static const char *dumpXML(const LLSD &llsd); + + /// Returns Notation version of llsd -- only to be called from debugger + static const char *dump(const LLSD &llsd); + //@} }; struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean> diff --git a/indra/llmessage/llservicebuilder.cpp b/indra/llmessage/llservicebuilder.cpp index 4fb3530c15..22e5c4af43 100644 --- a/indra/llmessage/llservicebuilder.cpp +++ b/indra/llmessage/llservicebuilder.cpp @@ -116,6 +116,104 @@ std::string LLServiceBuilder::buildServiceURI( // Find the Service Name if(!service_url.empty() && option_map.isMap()) { + // throw in a ridiculously large limiter to make sure we don't + // loop forever with bad input. + int iterations = 100; + bool keep_looping = true; + while(keep_looping) + { + if(0 == --iterations) + { + keep_looping = false; + } + + int depth = 0; + int deepest = 0; + bool find_match = false; + std::string::iterator iter(service_url.begin()); + std::string::iterator end(service_url.end()); + std::string::iterator deepest_node(service_url.end()); + std::string::iterator deepest_node_end(service_url.end()); + for(; iter != end; ++iter) + { + switch(*iter) + { + case '{': + ++depth; + if(depth > deepest) + { + deepest = depth; + deepest_node = iter; + find_match = true; + } + break; + case '}': + --depth; + if(find_match) + { + deepest_node_end = iter; + find_match = false; + } + break; + default: + break; + } + } + if((deepest_node == end) || (deepest_node_end == end)) + { + break; + } + + // *NOTE: since the c++ implementation only understands + // params and straight string substitution, so it's a + // known distance of 2 to skip the directive. + std::string key(deepest_node + 2, deepest_node_end); + LLSD value = option_map[key]; + switch(*(deepest_node + 1)) + { + case '$': + if(value.isDefined()) + { + service_url.replace( + deepest_node, + deepest_node_end + 1, + value.asString()); + } + else + { + llinfos << "Unknown key: " << key << llendl; + keep_looping = false; + } + break; + case '%': + { + std::string query_str = LLURI::mapToQueryString(value); + service_url.replace( + deepest_node, + deepest_node_end + 1, + query_str); + } + break; + default: + llinfos << "Unknown directive: " << *(deepest_node + 1) + << llendl; + keep_looping = false; + break; + } + } + } + if (service_url.find('{') != std::string::npos) + { + llwarns << "Constructed a likely bogus service URL: " << service_url + << llendl; + } + return service_url; +} + + + +// Old, not as good implementation. Phoenix 2007-10-15 +#if 0 // Do brace replacements - NOT CURRENTLY RECURSIVE for(LLSD::map_const_iterator option_itr = option_map.beginMap(); option_itr != option_map.endMap(); @@ -157,3 +255,4 @@ std::string LLServiceBuilder::buildServiceURI( return service_url; } +#endif diff --git a/indra/llmessage/llurlrequest.cpp b/indra/llmessage/llurlrequest.cpp index 857f804859..6a09a8bbec 100644 --- a/indra/llmessage/llurlrequest.cpp +++ b/indra/llmessage/llurlrequest.cpp @@ -480,6 +480,7 @@ bool LLURLRequest::configure() mDetail->mHeaders); } curl_easy_setopt(mDetail->mCurl, CURLOPT_URL, mDetail->mURL); + lldebugs << "URL: " << mDetail->mURL << llendl; curl_multi_add_handle(mDetail->mCurlMulti, mDetail->mCurl); mDetail->mNeedToRemoveEasyHandle = true; } diff --git a/indra/llmessage/llurlrequest.h b/indra/llmessage/llurlrequest.h index f335316ab6..7cb6b6a618 100644 --- a/indra/llmessage/llurlrequest.h +++ b/indra/llmessage/llurlrequest.h @@ -42,6 +42,7 @@ #include <string> #include "lliopipe.h" #include "llchainio.h" +#include "llerror.h" class LLURLRequestDetail; @@ -62,6 +63,7 @@ class LLURLRequestComplete; */ class LLURLRequest : public LLIOPipe { + LOG_CLASS(LLURLRequest); public: /** * @brief This enumeration is for specifying the type of request. diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index d446730c33..a1b63ead75 100644 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -1100,6 +1100,17 @@ void LLMessageSystem::forwardReliable(const U32 circuit_code) sendReliable(findHost(circuit_code)); } +S32 LLMessageSystem::forwardReliable( const LLHost &host, + S32 retries, + BOOL ping_based_timeout, + F32 timeout, + void (*callback)(void **,S32), + void ** callback_data) +{ + copyMessageRtoS(); + return sendReliable(host, retries, ping_based_timeout, timeout, callback, callback_data); +} + S32 LLMessageSystem::flushSemiReliable(const LLHost &host, void (*callback)(void **,S32), void ** callback_data) { F32 timeout; diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 3381ece222..4debcddf99 100644 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -464,6 +464,13 @@ public: void forwardMessage(const LLHost &host); void forwardReliable(const LLHost &host); void forwardReliable(const U32 circuit_code); + S32 forwardReliable( + const LLHost &host, + S32 retries, + BOOL ping_based_timeout, + F32 timeout, + void (*callback)(void **,S32), + void ** callback_data); LLHTTPClient::ResponderPtr createResponder(const std::string& name); S32 sendMessage(const LLHost &host); diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index a6a9447e39..c319ef97af 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -2117,7 +2117,6 @@ BOOL idle_startup() msg->setHandlerFuncFast(_PREHASH_PreloadSound, process_preload_sound); msg->setHandlerFuncFast(_PREHASH_AttachedSound, process_attached_sound); msg->setHandlerFuncFast(_PREHASH_AttachedSoundGainChange, process_attached_sound_gain_change); - //msg->setHandlerFuncFast(_PREHASH_AttachedSoundCutoffRadius, process_attached_sound_cutoff_radius); llinfos << "Initialization complete" << llendl; @@ -2771,7 +2770,6 @@ void register_viewer_callbacks(LLMessageSystem* msg) msg->setHandlerFuncFast(_PREHASH_MeanCollisionAlert, process_mean_collision_alert_message, NULL); msg->setHandlerFunc("ViewerFrozenMessage", process_frozen_message); - //msg->setHandlerFuncFast(_PREHASH_RequestAvatarInfo, process_avatar_info_request); msg->setHandlerFuncFast(_PREHASH_NameValuePair, process_name_value); msg->setHandlerFuncFast(_PREHASH_RemoveNameValuePair, process_remove_name_value); msg->setHandlerFuncFast(_PREHASH_AvatarAnimation, process_avatar_animation); diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 2011275bdb..7f349fe65b 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -3398,29 +3398,6 @@ void process_attached_sound_gain_change(LLMessageSystem *mesgsys, void **user_da objectp->adjustAudioGain(gain); } -/* Unused July 2006 -void process_attached_sound_cutoff_radius(LLMessageSystem *mesgsys, void **user_data) -{ - F32 radius = 0; - LLUUID object_guid; - LLViewerObject *objectp = NULL; - - mesgsys->getUUIDFast(_PREHASH_DataBlock, _PREHASH_ObjectID, object_guid); - - if (!((objectp = gObjectList.findObject(object_guid)))) - { - // we don't know about this object, just bail - return; - } - - mesgsys->getF32Fast(_PREHASH_DataBlock, _PREHASH_Radius, radius); - - if (gAudiop) - { -// gAudiop->attachToObject(sound_guid, object_guid, gain, priority, flags); - } -} -*/ void process_health_message(LLMessageSystem *mesgsys, void **user_data) { @@ -3566,19 +3543,6 @@ void process_sim_stats(LLMessageSystem *msg, void **user_data) } -// This info is requested by the simulator when the agent first logs in -// or when it moves into a simulator in which it did not already have -// a child agent. -/* -void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data) -{ - llinfos << "process_avatar_info_request()" << llendl; - - // Send the avatar appearance (parameters and texture entry UUIDs) - gAgent.sendAgentSetAppearance(); - send_agent_update(TRUE, TRUE); -}*/ - void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data) { diff --git a/indra/newview/llviewermessage.h b/indra/newview/llviewermessage.h index 2c5005c167..f7739f0871 100644 --- a/indra/newview/llviewermessage.h +++ b/indra/newview/llviewermessage.h @@ -87,12 +87,10 @@ void process_sound_trigger(LLMessageSystem *mesgsys, void **user_data); void process_preload_sound( LLMessageSystem *mesgsys, void **user_data); void process_attached_sound( LLMessageSystem *mesgsys, void **user_data); void process_attached_sound_gain_change( LLMessageSystem *mesgsys, void **user_data); -//void process_attached_sound_cutoff_radius( LLMessageSystem *mesgsys, void **user_data); void process_energy_statistics(LLMessageSystem *mesgsys, void **user_data); void process_health_message(LLMessageSystem *mesgsys, void **user_data); void process_sim_stats(LLMessageSystem *mesgsys, void **user_data); void process_shooter_agent_hit(LLMessageSystem* msg, void** user_data); -void process_avatar_info_request(LLMessageSystem *mesgsys, void **user_data); void process_avatar_animation(LLMessageSystem *mesgsys, void **user_data); void process_avatar_appearance(LLMessageSystem *mesgsys, void **user_data); void process_camera_constraint(LLMessageSystem *mesgsys, void **user_data); diff --git a/indra/newview/llviewerprecompiledheaders.h b/indra/newview/llviewerprecompiledheaders.h index 0669f34c2f..0e0265d30b 100644 --- a/indra/newview/llviewerprecompiledheaders.h +++ b/indra/newview/llviewerprecompiledheaders.h @@ -180,7 +180,6 @@ #include "llinstantmessage.h" #include "llinvite.h" //#include "llloginflags.h" -#include "lllogtextmessage.h" #include "llmail.h" #include "llmessagethrottle.h" #include "llnamevalue.h" diff --git a/indra/test/llservicebuilder_tut.cpp b/indra/test/llservicebuilder_tut.cpp index 127a2a1220..14f3774f7c 100644 --- a/indra/test/llservicebuilder_tut.cpp +++ b/indra/test/llservicebuilder_tut.cpp @@ -113,5 +113,66 @@ namespace tut test_url , "/proc/do/something/useful?estate_id=1&query=public"); } + + template<> template<> + void ServiceBuilderTestObject::test<6>() + { + LLSD test_block; + test_block["service-builder"] = "Which way to the {${$baz}}?"; + mServiceBuilder.createServiceDefinition( + "ServiceBuilderTest", + test_block["service-builder"]); + + LLSD data_map; + data_map["foo"] = "bar"; + data_map["baz"] = "foo"; + std::string test_url = mServiceBuilder.buildServiceURI( + "ServiceBuilderTest", + data_map); + ensure_equals( + "recursive url creation", + test_url , + "Which way to the bar?"); + } + + template<> template<> + void ServiceBuilderTestObject::test<7>() + { + LLSD test_block; + test_block["service-builder"] = "Which way to the {$foo}?"; + mServiceBuilder.createServiceDefinition( + "ServiceBuilderTest", + test_block["service-builder"]); + + LLSD data_map; + data_map["baz"] = "foo"; + std::string test_url = mServiceBuilder.buildServiceURI( + "ServiceBuilderTest", + data_map); + ensure_equals( + "fails to do replacement", + test_url , + "Which way to the {$foo}?"); + } + + template<> template<> + void ServiceBuilderTestObject::test<8>() + { + LLSD test_block; + test_block["service-builder"] = "/proc/{$proc}{%params}"; + mServiceBuilder.createServiceDefinition( + "ServiceBuilderTest", + test_block["service-builder"]); + LLSD data_map; + data_map["proc"] = "do/something/useful"; + data_map["params"] = LLSD(); + std::string test_url = mServiceBuilder.buildServiceURI( + "ServiceBuilderTest", + data_map); + ensure_equals( + "strip params", + test_url , + "/proc/do/something/useful"); + } } |