summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorAaron Brashears <aaronb@lindenlab.com>2009-05-18 23:38:35 +0000
committerAaron Brashears <aaronb@lindenlab.com>2009-05-18 23:38:35 +0000
commit6df2755ba6b24d0cefd52ce175b0212dd46c9b10 (patch)
tree833bc29e7bd5438eb89f34119ae157efe6258b2c /indra
parent0257214763203708e8e29d09346e777b95cdfce6 (diff)
Result of svn merge -r119432:120464 svn+ssh://svn/svn/linden/branches/http_database/merge-03 into trunk. QAR-1462
Diffstat (limited to 'indra')
-rw-r--r--indra/lib/python/indra/base/cllsd_test.py2
-rw-r--r--indra/lib/python/indra/base/llsd.py22
-rw-r--r--indra/lib/python/indra/base/metrics.py94
-rw-r--r--indra/lib/python/indra/ipc/servicebuilder.py22
-rw-r--r--indra/lib/python/indra/ipc/siesta.py132
-rw-r--r--indra/lib/python/indra/util/named_query.py21
-rw-r--r--indra/llcommon/CMakeLists.txt2
-rw-r--r--indra/llcommon/llapp.cpp32
-rw-r--r--indra/llcommon/llapp.h45
-rw-r--r--indra/llcommon/llerror.cpp7
-rw-r--r--indra/llcommon/llliveappconfig.cpp32
-rw-r--r--indra/llcommon/llliveappconfig.h34
-rw-r--r--indra/llcommon/lllivefile.cpp35
-rw-r--r--indra/llcommon/lllivefile.h50
-rw-r--r--indra/llcommon/llstat.cpp11
-rw-r--r--indra/llcommon/llstring.h46
-rw-r--r--indra/llcommon/lluri.cpp5
-rw-r--r--indra/llcommon/lluri.h17
-rwxr-xr-xindra/llcrashlogger/llcrashlogger.cpp3
-rw-r--r--indra/llinventory/llparcel.cpp2
-rw-r--r--indra/llinventory/llparcel.h8
-rw-r--r--indra/llmessage/llhttpclient.cpp140
-rw-r--r--indra/llmessage/llhttpclient.h8
-rw-r--r--indra/llmessage/llmessageconfig.cpp7
-rw-r--r--indra/llmessage/llurlrequest.cpp20
-rw-r--r--indra/llmessage/llurlrequest.h5
-rw-r--r--indra/llmessage/message.h16
-rw-r--r--indra/newview/llappviewer.cpp1
28 files changed, 632 insertions, 187 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;
}