PageRenderTime 39ms CodeModel.GetById 21ms app.highlight 13ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/tests/test_llsdmessage_peer.py

https://bitbucket.org/lindenlab/viewer-beta/
Python | 146 lines | 104 code | 3 blank | 39 comment | 0 complexity | 6970198ea1e08737fa7e58288b6e0919 MD5 | raw file
  1#!/usr/bin/env python
  2"""\
  3@file   test_llsdmessage_peer.py
  4@author Nat Goodspeed
  5@date   2008-10-09
  6@brief  This script asynchronously runs the executable (with args) specified on
  7        the command line, returning its result code. While that executable is
  8        running, we provide dummy local services for use by C++ tests.
  9
 10$LicenseInfo:firstyear=2008&license=viewerlgpl$
 11Second Life Viewer Source Code
 12Copyright (C) 2010, Linden Research, Inc.
 13
 14This library is free software; you can redistribute it and/or
 15modify it under the terms of the GNU Lesser General Public
 16License as published by the Free Software Foundation;
 17version 2.1 of the License only.
 18
 19This library is distributed in the hope that it will be useful,
 20but WITHOUT ANY WARRANTY; without even the implied warranty of
 21MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 22Lesser General Public License for more details.
 23
 24You should have received a copy of the GNU Lesser General Public
 25License along with this library; if not, write to the Free Software
 26Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 27
 28Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 29$/LicenseInfo$
 30"""
 31
 32import os
 33import sys
 34from threading import Thread
 35from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
 36
 37mydir = os.path.dirname(__file__)       # expected to be .../indra/llmessage/tests/
 38sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))
 39from indra.util.fastest_elementtree import parse as xml_parse
 40from indra.base import llsd
 41from testrunner import freeport, run, debug, VERBOSE
 42
 43class TestHTTPRequestHandler(BaseHTTPRequestHandler):
 44    """This subclass of BaseHTTPRequestHandler is to receive and echo
 45    LLSD-flavored messages sent by the C++ LLHTTPClient.
 46    """
 47    def read(self):
 48        # The following logic is adapted from the library module
 49        # SimpleXMLRPCServer.py.
 50        # Get arguments by reading body of request.
 51        # We read this in chunks to avoid straining
 52        # socket.read(); around the 10 or 15Mb mark, some platforms
 53        # begin to have problems (bug #792570).
 54        try:
 55            size_remaining = int(self.headers["content-length"])
 56        except (KeyError, ValueError):
 57            return ""
 58        max_chunk_size = 10*1024*1024
 59        L = []
 60        while size_remaining:
 61            chunk_size = min(size_remaining, max_chunk_size)
 62            chunk = self.rfile.read(chunk_size)
 63            L.append(chunk)
 64            size_remaining -= len(chunk)
 65        return ''.join(L)
 66        # end of swiped read() logic
 67
 68    def read_xml(self):
 69        # This approach reads the entire POST data into memory first
 70        return llsd.parse(self.read())
 71##         # This approach attempts to stream in the LLSD XML from self.rfile,
 72##         # assuming that the underlying XML parser reads its input file
 73##         # incrementally. Unfortunately I haven't been able to make it work.
 74##         tree = xml_parse(self.rfile)
 75##         debug("Finished raw parse")
 76##         debug("parsed XML tree %s", tree)
 77##         debug("parsed root node %s", tree.getroot())
 78##         debug("root node tag %s", tree.getroot().tag)
 79##         return llsd.to_python(tree.getroot())
 80
 81    def do_GET(self):
 82        # Of course, don't attempt to read data.
 83        self.answer(dict(reply="success", status=500,
 84                         reason="Your GET operation requested failure"))
 85
 86    def do_POST(self):
 87        # Read the provided POST data.
 88        self.answer(self.read_xml())
 89
 90    def answer(self, data):
 91        debug("%s.answer(%s): self.path = %r", self.__class__.__name__, data, self.path)
 92        if "fail" not in self.path:
 93            response = llsd.format_xml(data.get("reply", llsd.LLSD("success")))
 94            debug("success: %s", response)
 95            self.send_response(200)
 96            self.send_header("Content-type", "application/llsd+xml")
 97            self.send_header("Content-Length", str(len(response)))
 98            self.end_headers()
 99            self.wfile.write(response)
100        else:                           # fail requested
101            status = data.get("status", 500)
102            # self.responses maps an int status to a (short, long) pair of
103            # strings. We want the longer string. That's why we pass a string
104            # pair to get(): the [1] will select the second string, whether it
105            # came from self.responses or from our default pair.
106            reason = data.get("reason",
107                               self.responses.get(status,
108                                                  ("fail requested",
109                                                   "Your request specified failure status %s "
110                                                   "without providing a reason" % status))[1])
111            debug("fail requested: %s: %r", status, reason)
112            self.send_error(status, reason)
113
114    if not VERBOSE:
115        # When VERBOSE is set, skip both these overrides because they exist to
116        # suppress output.
117
118        def log_request(self, code, size=None):
119            # For present purposes, we don't want the request splattered onto
120            # stderr, as it would upset devs watching the test run
121            pass
122
123        def log_error(self, format, *args):
124            # Suppress error output as well
125            pass
126
127class Server(HTTPServer):
128    # This pernicious flag is on by default in HTTPServer. But proper
129    # operation of freeport() absolutely depends on it being off.
130    allow_reuse_address = False
131
132if __name__ == "__main__":
133    # Instantiate a Server(TestHTTPRequestHandler) on the first free port
134    # in the specified port range. Doing this inline is better than in a
135    # daemon thread: if it blows up here, we'll get a traceback. If it blew up
136    # in some other thread, the traceback would get eaten and we'd run the
137    # subject test program anyway.
138    httpd, port = freeport(xrange(8000, 8020),
139                           lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler))
140    # Pass the selected port number to the subject test program via the
141    # environment. We don't want to impose requirements on the test program's
142    # command-line parsing -- and anyway, for C++ integration tests, that's
143    # performed in TUT code rather than our own.
144    os.environ["PORT"] = str(port)
145    debug("$PORT = %s", port)
146    sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), *sys.argv[1:]))