mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 899757: Make nsServerSocket::InitWithAddress provide more detailed error results. r=mayhemer, r=ted
I looked through the NSPR socket creation functions that InitWithAddress uses to see which errors they could return, and placed appropriate comments in ErrorAccordingToNSPR. The test coverage is not great; in particular, I wasn't able to find a way to elicit "address in use" errors from Windows (although I could from Linux); the web says that Windows is much more relaxed about binding listening sockets than Unix derivatives. I'm interested in suggestions.
This commit is contained in:
parent
742666beae
commit
28ebe45d1b
@ -167,6 +167,7 @@ XPC_MSG_DEF(NS_ERROR_UNKNOWN_PROXY_HOST , "The lookup of the proxy h
|
||||
XPC_MSG_DEF(NS_ERROR_UNKNOWN_SOCKET_TYPE , "The specified socket type does not exist")
|
||||
XPC_MSG_DEF(NS_ERROR_SOCKET_CREATE_FAILED , "The specified socket type could not be created")
|
||||
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED , "The specified socket address type is not supported")
|
||||
XPC_MSG_DEF(NS_ERROR_SOCKET_ADDRESS_IN_USE , "Some other socket is already using the specified address.")
|
||||
XPC_MSG_DEF(NS_ERROR_CACHE_KEY_NOT_FOUND , "Cache key could not be found")
|
||||
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_STREAM , "Cache data is a stream")
|
||||
XPC_MSG_DEF(NS_ERROR_CACHE_DATA_IS_NOT_STREAM , "Cache data is not a stream")
|
||||
|
@ -289,7 +289,7 @@ nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog)
|
||||
if (!mFD)
|
||||
{
|
||||
NS_WARNING("unable to create server socket");
|
||||
return NS_ERROR_FAILURE;
|
||||
return ErrorAccordingToNSPR(PR_GetError());
|
||||
}
|
||||
|
||||
PRSocketOptionData opt;
|
||||
@ -330,8 +330,9 @@ nsServerSocket::InitWithAddress(const PRNetAddr *aAddr, int32_t aBackLog)
|
||||
return NS_OK;
|
||||
|
||||
fail:
|
||||
nsresult rv = ErrorAccordingToNSPR(PR_GetError());
|
||||
Close();
|
||||
return NS_ERROR_FAILURE;
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -155,8 +155,13 @@ ErrorAccordingToNSPR(PRErrorCode errorCode)
|
||||
rv = NS_ERROR_NET_INTERRUPT;
|
||||
break;
|
||||
case PR_CONNECT_REFUSED_ERROR:
|
||||
case PR_NETWORK_UNREACHABLE_ERROR: // XXX need new nsresult for this!
|
||||
case PR_HOST_UNREACHABLE_ERROR: // XXX and this!
|
||||
// We lump the following NSPR codes in with PR_CONNECT_REFUSED_ERROR. We
|
||||
// could get better diagnostics by adding distinct XPCOM error codes for
|
||||
// each of these, but there are a lot of places in Gecko that check
|
||||
// specifically for NS_ERROR_CONNECTION_REFUSED, all of which would need to
|
||||
// be checked.
|
||||
case PR_NETWORK_UNREACHABLE_ERROR:
|
||||
case PR_HOST_UNREACHABLE_ERROR:
|
||||
case PR_ADDRESS_NOT_AVAILABLE_ERROR:
|
||||
// Treat EACCES as a soft error since (at least on Linux) connect() returns
|
||||
// EACCES when an IPv6 connection is blocked by a firewall. See bug 270784.
|
||||
@ -170,10 +175,68 @@ ErrorAccordingToNSPR(PRErrorCode errorCode)
|
||||
case PR_CONNECT_TIMEOUT_ERROR:
|
||||
rv = NS_ERROR_NET_TIMEOUT;
|
||||
break;
|
||||
case PR_OUT_OF_MEMORY_ERROR:
|
||||
// These really indicate that the descriptor table filled up, or that the
|
||||
// kernel ran out of network buffers - but nobody really cares which part of
|
||||
// the system ran out of memory.
|
||||
case PR_PROC_DESC_TABLE_FULL_ERROR:
|
||||
case PR_SYS_DESC_TABLE_FULL_ERROR:
|
||||
case PR_INSUFFICIENT_RESOURCES_ERROR:
|
||||
rv = NS_ERROR_OUT_OF_MEMORY;
|
||||
break;
|
||||
case PR_ADDRESS_IN_USE_ERROR:
|
||||
rv = NS_ERROR_SOCKET_ADDRESS_IN_USE;
|
||||
break;
|
||||
// These filename-related errors can arise when using Unix-domain sockets.
|
||||
case PR_FILE_NOT_FOUND_ERROR:
|
||||
rv = NS_ERROR_FILE_NOT_FOUND;
|
||||
break;
|
||||
case PR_IS_DIRECTORY_ERROR:
|
||||
rv = NS_ERROR_FILE_IS_DIRECTORY;
|
||||
break;
|
||||
case PR_LOOP_ERROR:
|
||||
rv = NS_ERROR_FILE_UNRESOLVABLE_SYMLINK;
|
||||
break;
|
||||
case PR_NAME_TOO_LONG_ERROR:
|
||||
rv = NS_ERROR_FILE_NAME_TOO_LONG;
|
||||
break;
|
||||
case PR_NO_DEVICE_SPACE_ERROR:
|
||||
rv = NS_ERROR_FILE_NO_DEVICE_SPACE;
|
||||
break;
|
||||
case PR_NOT_DIRECTORY_ERROR:
|
||||
rv = NS_ERROR_FILE_NOT_DIRECTORY;
|
||||
break;
|
||||
case PR_READ_ONLY_FILESYSTEM_ERROR:
|
||||
rv = NS_ERROR_FILE_READ_ONLY;
|
||||
break;
|
||||
default:
|
||||
if (IsNSSErrorCode(errorCode))
|
||||
rv = GetXPCOMFromNSSError(errorCode);
|
||||
break;
|
||||
|
||||
// NSPR's socket code can return these, but they're not worth breaking out
|
||||
// into their own error codes, distinct from NS_ERROR_FAILURE:
|
||||
//
|
||||
// PR_BAD_DESCRIPTOR_ERROR
|
||||
// PR_INVALID_ARGUMENT_ERROR
|
||||
// PR_NOT_SOCKET_ERROR
|
||||
// PR_NOT_TCP_SOCKET_ERROR
|
||||
// These would indicate a bug internal to the component.
|
||||
//
|
||||
// PR_PROTOCOL_NOT_SUPPORTED_ERROR
|
||||
// This means that we can't use the given "protocol" (like
|
||||
// IPPROTO_TCP or IPPROTO_UDP) with a socket of the given type. As
|
||||
// above, this indicates an internal bug.
|
||||
//
|
||||
// PR_IS_CONNECTED_ERROR
|
||||
// This indicates that we've applied a system call like 'bind' or
|
||||
// 'connect' to a socket that is already connected. The socket
|
||||
// components manage each file descriptor's state, and in some cases
|
||||
// handle this error result internally. We shouldn't be returning
|
||||
// this to our callers.
|
||||
//
|
||||
// PR_IO_ERROR
|
||||
// This is so vague that NS_ERROR_FAILURE is just as good.
|
||||
}
|
||||
SOCKET_LOG(("ErrorAccordingToNSPR [in=%d out=%x]\n", errorCode, rv));
|
||||
return rv;
|
||||
|
32
netwerk/test/unit/test_addr_in_use_error.js
Normal file
32
netwerk/test/unit/test_addr_in_use_error.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Opening a second listening socket on the same address as an extant
|
||||
// socket should elicit NS_ERROR_SOCKET_ADDRESS_IN_USE on non-Windows
|
||||
// machines.
|
||||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, Constructor: CC } = Components;
|
||||
|
||||
const ServerSocket = CC("@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"init");
|
||||
|
||||
function testAddrInUse()
|
||||
{
|
||||
// Windows lets us have as many sockets listening on the same address as
|
||||
// we like, evidently.
|
||||
if ("@mozilla.org/windows-registry-key;1" in Cc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create listening socket:
|
||||
// any port (-1), loopback only (true), default backlog (-1)
|
||||
let listener = ServerSocket(-1, true, -1);
|
||||
do_check_true(listener instanceof Ci.nsIServerSocket);
|
||||
|
||||
// Try to create another listening socket on the same port, whatever that was.
|
||||
do_check_throws_nsIException(() => ServerSocket(listener.port, true, -1),
|
||||
"NS_ERROR_SOCKET_ADDRESS_IN_USE");
|
||||
}
|
||||
|
||||
function run_test()
|
||||
{
|
||||
testAddrInUse();
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
head = head_channels.js head_cache.js
|
||||
tail =
|
||||
|
||||
[test_addr_in_use_error.js]
|
||||
[test_304_responses.js]
|
||||
# Bug 675039: test hangs on Android-armv6
|
||||
skip-if = os == "android"
|
||||
|
11
testing/xpcshell/example/unit/test_check_nsIException.js
Normal file
11
testing/xpcshell/example/unit/test_check_nsIException.js
Normal file
@ -0,0 +1,11 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function run_test() {
|
||||
let env = Components.classes["@mozilla.org/process/environment;1"]
|
||||
.getService(Components.interfaces.nsIEnvironment);
|
||||
do_check_throws_nsIException(function () {
|
||||
env.QueryInterface(Components.interfaces.nsIFile);
|
||||
}, "NS_NOINTERFACE");
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function run_test() {
|
||||
do_check_throws_nsIException(function () {
|
||||
throw Error("I find your relaxed dishabille unpalatable");
|
||||
}, "NS_NOINTERFACE");
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_check_nsIException.js]
|
||||
[test_check_nsIException_failing.js]
|
||||
fail-if = true
|
||||
|
||||
[test_do_get_tempdir.js]
|
||||
[test_execute_soon.js]
|
||||
[test_get_file.js]
|
||||
|
@ -868,6 +868,63 @@ function format_pattern_match_failure(diagnosis, indent="") {
|
||||
return indent + a;
|
||||
}
|
||||
|
||||
// Check that |func| throws an nsIException that has
|
||||
// |Components.results[resultName]| as the value of its 'result' property.
|
||||
function do_check_throws_nsIException(func, resultName,
|
||||
stack=Components.stack.caller, todo=false)
|
||||
{
|
||||
let expected = Components.results[resultName];
|
||||
if (typeof expected !== 'number') {
|
||||
do_throw("do_check_throws_nsIException requires a Components.results" +
|
||||
" property name, not " + uneval(resultName), stack);
|
||||
}
|
||||
|
||||
let msg = ("do_check_throws_nsIException: func should throw" +
|
||||
" an nsIException whose 'result' is Components.results." +
|
||||
resultName);
|
||||
|
||||
try {
|
||||
func();
|
||||
} catch (ex) {
|
||||
if (!(ex instanceof Components.interfaces.nsIException) ||
|
||||
ex.result !== expected) {
|
||||
do_report_result(false, msg + ", threw " + legible_exception(ex) +
|
||||
" instead", stack, todo);
|
||||
}
|
||||
|
||||
do_report_result(true, msg, stack, todo);
|
||||
return;
|
||||
}
|
||||
|
||||
// Call this here, not in the 'try' clause, so do_report_result's own
|
||||
// throw doesn't get caught by our 'catch' clause.
|
||||
do_report_result(false, msg + ", but returned normally", stack, todo);
|
||||
}
|
||||
|
||||
// Produce a human-readable form of |exception|. This looks up
|
||||
// Components.results values, tries toString methods, and so on.
|
||||
function legible_exception(exception)
|
||||
{
|
||||
switch (typeof exception) {
|
||||
case 'object':
|
||||
if (exception instanceof Components.interfaces.nsIException) {
|
||||
return "nsIException instance: " + uneval(exception.toString());
|
||||
}
|
||||
return exception.toString();
|
||||
|
||||
case 'number':
|
||||
for (let name in Components.results) {
|
||||
if (exception === Components.results[name]) {
|
||||
return "Components.results." + name;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall through.
|
||||
default:
|
||||
return uneval(exception);
|
||||
}
|
||||
}
|
||||
|
||||
function do_test_pending(aName) {
|
||||
++_tests_pending;
|
||||
|
||||
|
@ -253,6 +253,8 @@
|
||||
ERROR(NS_ERROR_SOCKET_CREATE_FAILED, FAILURE(52)),
|
||||
/* The operating system doesn't support the given type of address. */
|
||||
ERROR(NS_ERROR_SOCKET_ADDRESS_NOT_SUPPORTED, FAILURE(53)),
|
||||
/* The address to which we tried to bind the socket was busy. */
|
||||
ERROR(NS_ERROR_SOCKET_ADDRESS_IN_USE, FAILURE(54)),
|
||||
|
||||
/* Cache specific error codes: */
|
||||
ERROR(NS_ERROR_CACHE_KEY_NOT_FOUND, FAILURE(61)),
|
||||
|
Loading…
Reference in New Issue
Block a user