summaryrefslogtreecommitdiff
path: root/indra/llcorehttp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcorehttp')
-rw-r--r--indra/llcorehttp/CMakeLists.txt31
-rw-r--r--indra/llcorehttp/_httpoprequest.cpp12
-rw-r--r--indra/llcorehttp/httpcommon.cpp9
-rw-r--r--indra/llcorehttp/httpcommon.h6
-rwxr-xr-x[-rw-r--r--]indra/llcorehttp/tests/llcorehttp_test.cpp3
-rw-r--r--indra/llcorehttp/tests/test_httprequest.hpp71
-rw-r--r--indra/llcorehttp/tests/test_httpstatus.hpp2
-rwxr-xr-xindra/llcorehttp/tests/test_llcorehttp_peer.py41
-rwxr-xr-xindra/llcorehttp/tests/testrunner.py265
9 files changed, 98 insertions, 342 deletions
diff --git a/indra/llcorehttp/CMakeLists.txt b/indra/llcorehttp/CMakeLists.txt
index 0bb0348d26..7482fc577f 100644
--- a/indra/llcorehttp/CMakeLists.txt
+++ b/indra/llcorehttp/CMakeLists.txt
@@ -93,6 +93,7 @@ target_link_libraries(
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
)
# tests
@@ -129,8 +130,8 @@ if (LL_TESTS)
${CURL_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
- ${BOOST_SYSTEM_LIBRARY}
${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
)
# If http_proxy is in the current environment (e.g. to fetch s3-proxy
@@ -147,7 +148,7 @@ if (LL_TESTS)
if (DARWIN)
# Path inside the app bundle where we'll need to copy libraries
set(LL_TEST_DESTINATION_DIR
- ${CMAKE_SOURCE_DIR}/../build-darwin-i386/sharedlibs/Resources
+ ${CMAKE_BINARY_DIR}/sharedlibs/Resources
)
# Create the Contents/Resources directory
@@ -163,21 +164,23 @@ if (DARWIN)
# Copy the required libraries to the package app
add_custom_command(TARGET INTEGRATION_TEST_llcorehttp PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libapr-1.0.dylib ${LL_TEST_DESTINATION_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libapr-1.0.dylib
- )
- add_custom_command(TARGET INTEGRATION_TEST_llcorehttp PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libaprutil-1.0.dylib ${LL_TEST_DESTINATION_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libaprutil-1.0.dylib
+ COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libapr-1.0.dylib ${LL_TEST_DESTINATION_DIR}
+ DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libapr-1.0.dylib
)
add_custom_command(TARGET INTEGRATION_TEST_llcorehttp PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexception_handler.dylib ${LL_TEST_DESTINATION_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexception_handler.dylib
+ COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libaprutil-1.0.dylib ${LL_TEST_DESTINATION_DIR}
+ DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libaprutil-1.0.dylib
)
add_custom_command(TARGET INTEGRATION_TEST_llcorehttp PRE_BUILD
- COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexpat.1.5.2.dylib ${LL_TEST_DESTINATION_DIR}
- DEPENDS ${CMAKE_SOURCE_DIR}/../build-darwin-i386/packages/lib/release/libexpat.1.5.2.dylib
+ COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/libexception_handler.dylib ${LL_TEST_DESTINATION_DIR}
+ DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/libexception_handler.dylib
)
+ foreach(expat ${EXPAT_COPY})
+ add_custom_command(TARGET INTEGRATION_TEST_llcorehttp PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E copy ${AUTOBUILD_INSTALL_DIR}/lib/release/${expat} ${LL_TEST_DESTINATION_DIR}
+ DEPENDS ${AUTOBUILD_INSTALL_DIR}/lib/release/${expat}
+ )
+ endforeach(expat)
endif (DARWIN)
@@ -197,8 +200,8 @@ endif (DARWIN)
${CURL_LIBRARIES}
${OPENSSL_LIBRARIES}
${CRYPTO_LIBRARIES}
- ${BOOST_SYSTEM_LIBRARY}
${BOOST_THREAD_LIBRARY}
+ ${BOOST_SYSTEM_LIBRARY}
)
add_executable(http_texture_load
@@ -213,7 +216,7 @@ endif (DARWIN)
# The following come from LLAddBuildTest.cmake's INTEGRATION_TEST_xxxx target.
set_target_properties(http_texture_load
PROPERTIES
- LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:CONSOLE ${TCMALLOC_LINK_FLAGS}"
+ LINK_FLAGS "/debug /NODEFAULTLIB:LIBCMT /SUBSYSTEM:CONSOLE"
LINK_FLAGS_DEBUG "/NODEFAULTLIB:\"LIBCMT;LIBCMTD;MSVCRT\" /INCREMENTAL:NO"
LINK_FLAGS_RELEASE ""
)
diff --git a/indra/llcorehttp/_httpoprequest.cpp b/indra/llcorehttp/_httpoprequest.cpp
index db57869a1b..07cc0e4625 100644
--- a/indra/llcorehttp/_httpoprequest.cpp
+++ b/indra/llcorehttp/_httpoprequest.cpp
@@ -568,7 +568,17 @@ HttpStatus HttpOpRequest::prepareRequest(HttpService * service)
// Use the viewer-based thread-safe API which has a
// fast/safe check for proxy enable. Would like to
// encapsulate this someway...
- LLProxy::getInstance()->applyProxySettings(mCurlHandle);
+ if (LLProxy::instanceExists())
+ {
+ // Make sure proxy won't be initialized from here,
+ // it might conflict with LLStartUp::startLLProxy()
+ LLProxy::getInstance()->applyProxySettings(mCurlHandle);
+ }
+ else
+ {
+ LL_WARNS() << "Proxy is not initialized!" << LL_ENDL;
+ }
+
}
else if (gpolicy.mHttpProxy.size())
{
diff --git a/indra/llcorehttp/httpcommon.cpp b/indra/llcorehttp/httpcommon.cpp
index c423047bb0..1829062af6 100644
--- a/indra/llcorehttp/httpcommon.cpp
+++ b/indra/llcorehttp/httpcommon.cpp
@@ -50,11 +50,12 @@ HttpStatus::type_enum_t EXT_CURL_EASY;
HttpStatus::type_enum_t EXT_CURL_MULTI;
HttpStatus::type_enum_t LLCORE;
-HttpStatus::operator unsigned long() const
+HttpStatus::operator U32() const
{
- static const int shift(sizeof(unsigned long) * 4);
+ // Effectively, concatenate mType (high) with mStatus (low).
+ static const int shift(sizeof(mDetails->mStatus) * 8);
- unsigned long result(((unsigned long)mDetails->mType) << shift | (unsigned long)(int)mDetails->mStatus);
+ U32 result(U32(mDetails->mType) << shift | U32((int)mDetails->mStatus));
return result;
}
@@ -64,7 +65,7 @@ std::string HttpStatus::toHex() const
std::ostringstream result;
result.width(8);
result.fill('0');
- result << std::hex << operator unsigned long();
+ result << std::hex << operator U32();
return result.str();
}
diff --git a/indra/llcorehttp/httpcommon.h b/indra/llcorehttp/httpcommon.h
index b2db01d038..ea0c38abd7 100644
--- a/indra/llcorehttp/httpcommon.h
+++ b/indra/llcorehttp/httpcommon.h
@@ -382,10 +382,10 @@ struct HttpStatus
/// creates an ambiguous second path to integer conversion
/// which tends to find programming errors such as formatting
/// the status to a stream (operator<<).
- operator unsigned long() const;
- unsigned long toULong() const
+ operator U32() const;
+ U32 toULong() const
{
- return operator unsigned long();
+ return operator U32();
}
/// And to convert to a hex string.
diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp
index bef762f5ce..a310fc0508 100644..100755
--- a/indra/llcorehttp/tests/llcorehttp_test.cpp
+++ b/indra/llcorehttp/tests/llcorehttp_test.cpp
@@ -46,6 +46,7 @@
#include "test_httprequestqueue.hpp"
#include "llproxy.h"
+#include "llcleanup.h"
unsigned long ssl_thread_id_callback(void);
void ssl_locking_callback(int mode, int type, const char * file, int line);
@@ -101,7 +102,7 @@ void init_curl()
void term_curl()
{
- LLProxy::cleanupClass();
+ SUBSYSTEM_CLEANUP(LLProxy);
CRYPTO_set_locking_callback(NULL);
for (int i(0); i < ssl_mutex_count; ++i)
diff --git a/indra/llcorehttp/tests/test_httprequest.hpp b/indra/llcorehttp/tests/test_httprequest.hpp
index 463e55dd7e..a9c192e141 100644
--- a/indra/llcorehttp/tests/test_httprequest.hpp
+++ b/indra/llcorehttp/tests/test_httprequest.hpp
@@ -729,7 +729,7 @@ void HttpRequestTestObjectType::test<7>()
#if 0 // defined(WIN32)
// Can't do this on any platform anymore, the LL logging system holds
// on to memory and produces what looks like memory leaks...
-
+
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
#endif
@@ -1459,21 +1459,21 @@ void HttpRequestTestObjectType::test<14>()
// references to it after completion of this method.
// Create before memory record as the string copy will bump numbers.
TestHandler2 handler(this, "handler");
- LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
- std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
-
+ LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
+ std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
+
// record the total amount of dynamically allocated memory
mMemTotal = GetMemTotal();
mHandlerCalls = 0;
HttpRequest * req = NULL;
HttpOptions::ptr_t opts;
-
+
try
{
- // Get singletons created
+ // Get singletons created
HttpRequest::createService();
-
+
// Start threading early so that thread memory is invariant
// over the test.
HttpRequest::startThread();
@@ -1482,10 +1482,10 @@ void HttpRequestTestObjectType::test<14>()
req = new HttpRequest();
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
- opts = HttpOptions::ptr_t(new HttpOptions);
- opts->setRetries(0); // Don't retry
+ opts = HttpOptions::ptr_t(new HttpOptions);
+ opts->setRetries(0); // Don't retry
opts->setTimeout(2);
-
+
// Issue a GET that sleeps
mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
@@ -1494,8 +1494,8 @@ void HttpRequestTestObjectType::test<14>()
0,
0,
opts,
- HttpHeaders::ptr_t(),
- handlerp);
+ HttpHeaders::ptr_t(),
+ handlerp);
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
// Run the notification pump.
@@ -1513,7 +1513,7 @@ void HttpRequestTestObjectType::test<14>()
mStatus = HttpStatus();
handle = req->requestStopThread(handlerp);
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
-
+
// Run the notification pump again
count = 0;
limit = LOOP_COUNT_LONG;
@@ -1535,30 +1535,29 @@ void HttpRequestTestObjectType::test<14>()
ensure("Thread actually stopped running", HttpService::isStopped());
// release options
- opts.reset();
-
+ opts.reset();
+
// release the request object
delete req;
req = NULL;
// Shut down service
HttpRequest::destroyService();
-
+
ensure("Two handler calls on the way out", 2 == mHandlerCalls);
-#if defined(WIN32)
- // Can only do this memory test on Windows. On other platforms,
- // the LL logging system holds on to memory and produces what looks
- // like memory leaks...
-
- // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
+#if 0 // defined(WIN32)
+ // Can't do this on any platform anymore, the LL logging system holds
+ // on to memory and produces what looks like memory leaks...
+
+ // printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
#endif
}
catch (...)
{
stop_thread(req);
- opts.reset();
+ opts.reset();
delete req;
HttpRequest::destroyService();
throw;
@@ -3065,12 +3064,11 @@ void HttpRequestTestObjectType::test<22>()
// Shut down service
HttpRequest::destroyService();
-
-#if defined(WIN32)
- // Can only do this memory test on Windows. On other platforms,
- // the LL logging system holds on to memory and produces what looks
- // like memory leaks...
-
+
+#if 0 // defined(WIN32)
+ // Can't do this on any platform anymore, the LL logging system holds
+ // on to memory and produces what looks like memory leaks...
+
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
#endif
@@ -3091,6 +3089,10 @@ void HttpRequestTestObjectType::test<23>()
set_test_name("HttpRequest GET 503s with 'Retry-After'");
+#if LL_WINDOWS && ADDRESS_SIZE == 64
+ skip("llcorehttp 503-with-retry test hangs on Windows 64");
+#endif
+
// This tests mainly that the code doesn't fall over if
// various well- and mis-formed Retry-After headers are
// sent along with the response. Direct inspection of
@@ -3195,12 +3197,11 @@ void HttpRequestTestObjectType::test<23>()
// Shut down service
HttpRequest::destroyService();
-
-#if defined(WIN32)
- // Can only do this memory test on Windows. On other platforms,
- // the LL logging system holds on to memory and produces what looks
- // like memory leaks...
-
+
+#if 0 // defined(WIN32)
+ // Can't do this on any platform anymore, the LL logging system holds
+ // on to memory and produces what looks like memory leaks...
+
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
#endif
diff --git a/indra/llcorehttp/tests/test_httpstatus.hpp b/indra/llcorehttp/tests/test_httpstatus.hpp
index 4502d32fe1..cbe3f574d4 100644
--- a/indra/llcorehttp/tests/test_httpstatus.hpp
+++ b/indra/llcorehttp/tests/test_httpstatus.hpp
@@ -244,7 +244,7 @@ void HttpStatusTestObjectType::test<7>()
HttpStatus status(404);
std::string msg = status.toHex();
// std::cout << "Result: " << msg << std::endl;
- ensure(msg == "01940001");
+ ensure_equals(msg, "01940001");
}
diff --git a/indra/llcorehttp/tests/test_llcorehttp_peer.py b/indra/llcorehttp/tests/test_llcorehttp_peer.py
index 04cde651c4..493143641b 100755
--- a/indra/llcorehttp/tests/test_llcorehttp_peer.py
+++ b/indra/llcorehttp/tests/test_llcorehttp_peer.py
@@ -34,18 +34,19 @@ import sys
import time
import select
import getopt
-from threading import Thread
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-from SocketServer import ThreadingMixIn
-mydir = os.path.dirname(__file__) # expected to be .../indra/llcorehttp/tests/
-sys.path.insert(0, os.path.join(mydir, os.pardir, os.pardir, "lib", "python"))
-from indra.util.fastest_elementtree import parse as xml_parse
-from indra.base import llsd
+from llbase.fastest_elementtree import parse as xml_parse
+from llbase import llsd
+
+# we're in llcorehttp/tests ; testrunner.py is found in llmessage/tests
+sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
+ "llmessage", "tests"))
+
from testrunner import freeport, run, debug, VERBOSE
class TestHTTPRequestHandler(BaseHTTPRequestHandler):
@@ -271,7 +272,7 @@ class TestHTTPRequestHandler(BaseHTTPRequestHandler):
# Suppress error output as well
pass
-class Server(ThreadingMixIn, HTTPServer):
+class Server(HTTPServer):
# This pernicious flag is on by default in HTTPServer. But proper
# operation of freeport() absolutely depends on it being off.
allow_reuse_address = False
@@ -295,22 +296,26 @@ if __name__ == "__main__":
if option == "-V" or option == "--valgrind":
do_valgrind = True
- # Instantiate a Server(TestHTTPRequestHandler) on the first free port
- # in the specified port range. Doing this inline is better than in a
- # daemon thread: if it blows up here, we'll get a traceback. If it blew up
- # in some other thread, the traceback would get eaten and we'd run the
- # subject test program anyway.
- httpd, port = freeport(xrange(8000, 8020),
- lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler))
+ # function to make a server with specified port
+ make_server = lambda port: Server(('127.0.0.1', port), TestHTTPRequestHandler)
+
+ if not sys.platform.startswith("win"):
+ # Instantiate a Server(TestHTTPRequestHandler) on a port chosen by the
+ # runtime.
+ httpd = make_server(0)
+ else:
+ # "Then there's Windows"
+ # Instantiate a Server(TestHTTPRequestHandler) on the first free port
+ # in the specified port range.
+ httpd, port = freeport(xrange(8000, 8020), make_server)
# Pass the selected port number to the subject test program via the
# environment. We don't want to impose requirements on the test program's
# command-line parsing -- and anyway, for C++ integration tests, that's
# performed in TUT code rather than our own.
- os.environ["LL_TEST_PORT"] = str(port)
- debug("$LL_TEST_PORT = %s", port)
+ os.environ["LL_TEST_PORT"] = str(httpd.server_port)
+ debug("$LL_TEST_PORT = %s", httpd.server_port)
if do_valgrind:
args = ["valgrind", "--log-file=./valgrind.log"] + args
path_search = True
- sys.exit(run(server=Thread(name="httpd", target=httpd.serve_forever), use_path=path_search, *args))
-
+ sys.exit(run(server_inst=httpd, use_path=path_search, *args))
diff --git a/indra/llcorehttp/tests/testrunner.py b/indra/llcorehttp/tests/testrunner.py
deleted file mode 100755
index 9a2de71142..0000000000
--- a/indra/llcorehttp/tests/testrunner.py
+++ /dev/null
@@ -1,265 +0,0 @@
-#!/usr/bin/env python
-"""\
-@file testrunner.py
-@author Nat Goodspeed
-@date 2009-03-20
-@brief Utilities for writing wrapper scripts for ADD_COMM_BUILD_TEST unit tests
-
-$LicenseInfo:firstyear=2009&license=viewerlgpl$
-Second Life Viewer Source Code
-Copyright (C) 2010, Linden Research, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Lesser General Public
-License as published by the Free Software Foundation;
-version 2.1 of the License only.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public
-License along with this library; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
-$/LicenseInfo$
-"""
-
-from __future__ import with_statement
-
-import os
-import sys
-import re
-import errno
-import socket
-
-VERBOSE = os.environ.get("INTEGRATION_TEST_VERBOSE", "0") # default to quiet
-# Support usage such as INTEGRATION_TEST_VERBOSE=off -- distressing to user if
-# that construct actually turns on verbosity...
-VERBOSE = not re.match(r"(0|off|false|quiet)$", VERBOSE, re.IGNORECASE)
-
-if VERBOSE:
- def debug(fmt, *args):
- print fmt % args
- sys.stdout.flush()
-else:
- debug = lambda *args: None
-
-def freeport(portlist, expr):
- """
- Find a free server port to use. Specifically, evaluate 'expr' (a
- callable(port)) until it stops raising EADDRINUSE exception.
-
- Pass:
-
- portlist: an iterable (e.g. xrange()) of ports to try. If you exhaust the
- range, freeport() lets the socket.error exception propagate. If you want
- unbounded, you could pass itertools.count(baseport), though of course in
- practice the ceiling is 2^16-1 anyway. But it seems prudent to constrain
- the range much more sharply: if we're iterating an absurd number of times,
- probably something else is wrong.
-
- expr: a callable accepting a port number, specifically one of the items
- from portlist. If calling that callable raises socket.error with
- EADDRINUSE, freeport() retrieves the next item from portlist and retries.
-
- Returns: (expr(port), port)
-
- port: the value from portlist for which expr(port) succeeded
-
- Raises:
-
- Any exception raised by expr(port) other than EADDRINUSE.
-
- socket.error if, for every item from portlist, expr(port) raises
- socket.error. The exception you see is the one from the last item in
- portlist.
-
- StopIteration if portlist is completely empty.
-
- Example:
-
- class Server(HTTPServer):
- # If you use BaseHTTPServer.HTTPServer, turning off this flag is
- # essential for proper operation of freeport()!
- allow_reuse_address = False
- # ...
- server, port = freeport(xrange(8000, 8010),
- lambda port: Server(("localhost", port),
- MyRequestHandler))
- # pass 'port' to client code
- # call server.serve_forever()
- """
- try:
- # If portlist is completely empty, let StopIteration propagate: that's an
- # error because we can't return meaningful values. We have no 'port',
- # therefore no 'expr(port)'.
- portiter = iter(portlist)
- port = portiter.next()
-
- while True:
- try:
- # If this value of port works, return as promised.
- value = expr(port)
-
- except socket.error, err:
- # Anything other than 'Address already in use', propagate
- if err.args[0] != errno.EADDRINUSE:
- raise
-
- # Here we want the next port from portiter. But on StopIteration,
- # we want to raise the original exception rather than
- # StopIteration. So save the original exc_info().
- type, value, tb = sys.exc_info()
- try:
- try:
- port = portiter.next()
- except StopIteration:
- raise type, value, tb
- finally:
- # Clean up local traceback, see docs for sys.exc_info()
- del tb
-
- else:
- debug("freeport() returning %s on port %s", value, port)
- return value, port
-
- # Recap of the control flow above:
- # If expr(port) doesn't raise, return as promised.
- # If expr(port) raises anything but EADDRINUSE, propagate that
- # exception.
- # If portiter.next() raises StopIteration -- that is, if the port
- # value we just passed to expr(port) was the last available -- reraise
- # the EADDRINUSE exception.
- # If we've actually arrived at this point, portiter.next() delivered a
- # new port value. Loop back to pass that to expr(port).
-
- except Exception, err:
- debug("*** freeport() raising %s: %s", err.__class__.__name__, err)
- raise
-
-def run(*args, **kwds):
- """All positional arguments collectively form a command line, executed as
- a synchronous child process.
- In addition, pass server=new_thread_instance as an explicit keyword (to
- differentiate it from an additional command-line argument).
- new_thread_instance should be an instantiated but not yet started Thread
- subclass instance, e.g.:
- run("python", "-c", 'print "Hello, world!"', server=TestHTTPServer(name="httpd"))
- """
- # If there's no server= keyword arg, don't start a server thread: simply
- # run a child process.
- try:
- thread = kwds.pop("server")
- except KeyError:
- pass
- else:
- # Start server thread. Note that this and all other comm server
- # threads should be daemon threads: we'll let them run "forever,"
- # confident that the whole process will terminate when the main thread
- # terminates, which will be when the child process terminates.
- thread.setDaemon(True)
- thread.start()
- # choice of os.spawnv():
- # - [v vs. l] pass a list of args vs. individual arguments,
- # - [no p] don't use the PATH because we specifically want to invoke the
- # executable passed as our first arg,
- # - [no e] child should inherit this process's environment.
- debug("Running %s...", " ".join(args))
- if kwds.get("use_path", False):
- rc = os.spawnvp(os.P_WAIT, args[0], args)
- else:
- rc = os.spawnv(os.P_WAIT, args[0], args)
- debug("%s returned %s", args[0], rc)
- return rc
-
-# ****************************************************************************
-# test code -- manual at this point, see SWAT-564
-# ****************************************************************************
-def test_freeport():
- # ------------------------------- Helpers --------------------------------
- from contextlib import contextmanager
- # helper Context Manager for expecting an exception
- # with exc(SomeError):
- # raise SomeError()
- # raises AssertionError otherwise.
- @contextmanager
- def exc(exception_class, *args):
- try:
- yield
- except exception_class, err:
- for i, expected_arg in enumerate(args):
- assert expected_arg == err.args[i], \
- "Raised %s, but args[%s] is %r instead of %r" % \
- (err.__class__.__name__, i, err.args[i], expected_arg)
- print "Caught expected exception %s(%s)" % \
- (err.__class__.__name__, ', '.join(repr(arg) for arg in err.args))
- else:
- assert False, "Failed to raise " + exception_class.__class__.__name__
-
- # helper to raise specified exception
- def raiser(exception):
- raise exception
-
- # the usual
- def assert_equals(a, b):
- assert a == b, "%r != %r" % (a, b)
-
- # ------------------------ Sanity check the above ------------------------
- class SomeError(Exception): pass
- # Without extra args, accept any err.args value
- with exc(SomeError):
- raiser(SomeError("abc"))
- # With extra args, accept only the specified value
- with exc(SomeError, "abc"):
- raiser(SomeError("abc"))
- with exc(AssertionError):
- with exc(SomeError, "abc"):
- raiser(SomeError("def"))
- with exc(AssertionError):
- with exc(socket.error, errno.EADDRINUSE):
- raiser(socket.error(errno.ECONNREFUSED, 'Connection refused'))
-
- # ----------- freeport() without engaging socket functionality -----------
- # If portlist is empty, freeport() raises StopIteration.
- with exc(StopIteration):
- freeport([], None)
-
- assert_equals(freeport([17], str), ("17", 17))
-
- # This is the magic exception that should prompt us to retry
- inuse = socket.error(errno.EADDRINUSE, 'Address already in use')
- # Get the iterator to our ports list so we can check later if we've used all
- ports = iter(xrange(5))
- with exc(socket.error, errno.EADDRINUSE):
- freeport(ports, lambda port: raiser(inuse))
- # did we entirely exhaust 'ports'?
- with exc(StopIteration):
- ports.next()
-
- ports = iter(xrange(2))
- # Any exception but EADDRINUSE should quit immediately
- with exc(SomeError):
- freeport(ports, lambda port: raiser(SomeError()))
- assert_equals(ports.next(), 1)
-
- # ----------- freeport() with platform-dependent socket stuff ------------
- # This is what we should've had unit tests to begin with (see CHOP-661).
- def newbind(port):
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('127.0.0.1', port))
- return sock
-
- bound0, port0 = freeport(xrange(7777, 7780), newbind)
- assert_equals(port0, 7777)
- bound1, port1 = freeport(xrange(7777, 7780), newbind)
- assert_equals(port1, 7778)
- bound2, port2 = freeport(xrange(7777, 7780), newbind)
- assert_equals(port2, 7779)
- with exc(socket.error, errno.EADDRINUSE):
- bound3, port3 = freeport(xrange(7777, 7780), newbind)
-
-if __name__ == "__main__":
- test_freeport()