Result of svn merge -r119432:120464 svn+ssh://svn/svn/linden/branches/http_database/merge-03 into trunk. QAR-1462
parent
0257214763
commit
6df2755ba6
|
|
@ -10,7 +10,7 @@ values = (
|
|||
'&<>',
|
||||
u'\u81acj',
|
||||
llsd.uri('http://foo<'),
|
||||
lluuid.LLUUID(),
|
||||
lluuid.UUID(),
|
||||
llsd.LLSD(['thing']),
|
||||
1,
|
||||
myint(31337),
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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 _log(header, table, data, dest):
|
||||
def set_destination(dest):
|
||||
"""Set the destination of metrics logs for this process.
|
||||
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -40,8 +40,7 @@
|
|||
|
||||
// Forward declarations
|
||||
class LLErrorThread;
|
||||
class LLApp;
|
||||
|
||||
class LLLiveFile;
|
||||
|
||||
typedef void (*LLAppErrorHandler)();
|
||||
typedef void (*LLAppChildCallback)(int pid, bool exited, int status);
|
||||
|
|
@ -127,6 +126,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.
|
||||
*
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,8 @@ LLCrashLogger::LLCrashLogger() :
|
|||
mSentCrashLogs(false),
|
||||
mCrashHost("")
|
||||
{
|
||||
|
||||
// Set up generic error handling
|
||||
setupErrorHandling();
|
||||
}
|
||||
|
||||
LLCrashLogger::~LLCrashLogger()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
// * 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");
|
||||
|
||||
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);
|
||||
// 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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
Loading…
Reference in New Issue