mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
Bug 1770019 - Make httpd.js support both IPv4 and IPv6, r=necko-reviewers,dragana
Differential Revision: https://phabricator.services.mozilla.com/D148696
This commit is contained in:
parent
5f8570e826
commit
5e9a1264ff
@ -66,6 +66,13 @@ interface nsIServerSocket : nsISupports
|
||||
in boolean aLoopbackOnly,
|
||||
in long aBackLog);
|
||||
|
||||
/**
|
||||
* Similar to init(), but initializes a server socket that supports
|
||||
* both IPv4 and IPv6.
|
||||
*/
|
||||
void initDualStack(in long aPort,
|
||||
in long aBackLog);
|
||||
|
||||
/**
|
||||
* initSpecialConnection
|
||||
*
|
||||
|
@ -14,8 +14,19 @@
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/net/DNS.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIFile.h"
|
||||
#if defined(XP_WIN)
|
||||
# include "private/pprio.h"
|
||||
# include <Winsock2.h>
|
||||
# include <mstcpip.h>
|
||||
|
||||
# ifndef IPV6_V6ONLY
|
||||
# define IPV6_V6ONLY 27
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -259,6 +270,16 @@ nsServerSocket::InitIPv6(int32_t aPort, bool aLoopbackOnly, int32_t aBackLog) {
|
||||
return InitWithAddress(&addr, aBackLog);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerSocket::InitDualStack(int32_t aPort, int32_t aBackLog) {
|
||||
if (aPort < 0) {
|
||||
aPort = 0;
|
||||
}
|
||||
PRNetAddr addr;
|
||||
PR_SetNetAddr(PR_IpAddrAny, PR_AF_INET6, aPort, &addr);
|
||||
return InitWithAddressInternal(&addr, aBackLog, true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerSocket::InitWithFilename(nsIFile* aPath, uint32_t aPermissions,
|
||||
int32_t aBacklog) {
|
||||
@ -329,6 +350,12 @@ nsServerSocket::InitSpecialConnection(int32_t aPort, nsServerSocketFlag aFlags,
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsServerSocket::InitWithAddress(const PRNetAddr* aAddr, int32_t aBackLog) {
|
||||
return InitWithAddressInternal(aAddr, aBackLog);
|
||||
}
|
||||
|
||||
nsresult nsServerSocket::InitWithAddressInternal(const PRNetAddr* aAddr,
|
||||
int32_t aBackLog,
|
||||
bool aDualStack) {
|
||||
NS_ENSURE_TRUE(mFD == nullptr, NS_ERROR_ALREADY_INITIALIZED);
|
||||
nsresult rv;
|
||||
|
||||
@ -342,6 +369,21 @@ nsServerSocket::InitWithAddress(const PRNetAddr* aAddr, int32_t aBackLog) {
|
||||
return ErrorAccordingToNSPR(PR_GetError());
|
||||
}
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// https://docs.microsoft.com/en-us/windows/win32/winsock/dual-stack-sockets
|
||||
// To create a Dual-Stack Socket, we have to disable IPV6_V6ONLY.
|
||||
if (aDualStack) {
|
||||
PROsfd osfd = PR_FileDesc2NativeHandle(mFD);
|
||||
if (osfd != -1) {
|
||||
int disable = 0;
|
||||
setsockopt(osfd, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&disable,
|
||||
sizeof(disable));
|
||||
}
|
||||
}
|
||||
#else
|
||||
mozilla::Unused << aDualStack;
|
||||
#endif
|
||||
|
||||
PR_SetFDInheritable(mFD, false);
|
||||
|
||||
PRSocketOptionData opt;
|
||||
|
@ -50,6 +50,9 @@ class nsServerSocket : public nsASocketHandler, public nsIServerSocket {
|
||||
// try attaching our socket (mFD) to the STS's poll list.
|
||||
nsresult TryAttach();
|
||||
|
||||
nsresult InitWithAddressInternal(const PRNetAddr* aAddr, int32_t aBackLog,
|
||||
bool aDualStack = false);
|
||||
|
||||
// lock protects access to mListener; so it is not cleared while being used.
|
||||
mozilla::Mutex mLock MOZ_UNANNOTATED{"nsServerSocket.mLock"};
|
||||
PRNetAddr mAddr = {.raw = {0, {0}}};
|
||||
|
@ -213,6 +213,11 @@ const ServerSocketIPv6 = CC(
|
||||
"nsIServerSocket",
|
||||
"initIPv6"
|
||||
);
|
||||
const ServerSocketDualStack = CC(
|
||||
"@mozilla.org/network/server-socket;1",
|
||||
"nsIServerSocket",
|
||||
"initDualStack"
|
||||
);
|
||||
const ScriptableInputStream = CC(
|
||||
"@mozilla.org/scriptableinputstream;1",
|
||||
"nsIScriptableInputStream",
|
||||
@ -523,7 +528,11 @@ nsHttpServer.prototype = {
|
||||
this._start(port, "[::1]");
|
||||
},
|
||||
|
||||
_start(port, host) {
|
||||
start_dualStack(port) {
|
||||
this._start(port, "[::1]", true);
|
||||
},
|
||||
|
||||
_start(port, host, dualStack) {
|
||||
if (this._socket) {
|
||||
throw Components.Exception("", Cr.NS_ERROR_ALREADY_INITIALIZED);
|
||||
}
|
||||
@ -567,7 +576,9 @@ nsHttpServer.prototype = {
|
||||
var socket;
|
||||
for (var i = 100; i; i--) {
|
||||
var temp = null;
|
||||
if (this._host.includes(":")) {
|
||||
if (dualStack) {
|
||||
temp = new ServerSocketDualStack(this._port, maxConnections);
|
||||
} else if (this._host.includes(":")) {
|
||||
temp = new ServerSocketIPv6(
|
||||
this._port,
|
||||
loopback, // true = localhost, false = everybody
|
||||
@ -608,7 +619,7 @@ nsHttpServer.prototype = {
|
||||
|
||||
socket.asyncListen(this);
|
||||
this._port = socket.port;
|
||||
this._identity._initialize(socket.port, host, true);
|
||||
this._identity._initialize(socket.port, host, true, dualStack);
|
||||
this._socket = socket;
|
||||
dumpn(
|
||||
">>> listening on port " +
|
||||
@ -1170,7 +1181,7 @@ ServerIdentity.prototype = {
|
||||
* Initializes the primary name for the corresponding server, based on the
|
||||
* provided port number.
|
||||
*/
|
||||
_initialize(port, host, addSecondaryDefault) {
|
||||
_initialize(port, host, addSecondaryDefault, dualStack) {
|
||||
this._host = host;
|
||||
if (this._primaryPort !== -1) {
|
||||
this.add("http", host, port);
|
||||
@ -1183,6 +1194,9 @@ ServerIdentity.prototype = {
|
||||
if (addSecondaryDefault && host != "127.0.0.1") {
|
||||
if (host.includes(":")) {
|
||||
this.add("http", "[::1]", port);
|
||||
if (dualStack) {
|
||||
this.add("http", "127.0.0.1", port);
|
||||
}
|
||||
} else {
|
||||
this.add("http", "127.0.0.1", port);
|
||||
}
|
||||
|
@ -60,6 +60,12 @@ interface nsIHttpServer : nsISupports
|
||||
*/
|
||||
void start_ipv6(in long port);
|
||||
|
||||
/**
|
||||
* Like the two functions above, but this server supports both IPv6 and
|
||||
* IPv4 addresses.
|
||||
*/
|
||||
void start_dualStack(in long port);
|
||||
|
||||
/**
|
||||
* Shuts down this server if it is running (including the period of time after
|
||||
* stop() has been called but before the provided callback has been called).
|
||||
|
@ -9,6 +9,8 @@
|
||||
/* import-globals-from head_channels.js */
|
||||
/* import-globals-from head_servers.js */
|
||||
|
||||
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
|
||||
|
||||
function makeChan(uri) {
|
||||
let chan = NetUtil.newChannel({
|
||||
uri,
|
||||
@ -18,6 +20,34 @@ function makeChan(uri) {
|
||||
return chan;
|
||||
}
|
||||
|
||||
function channelOpenPromise(chan, flags, observer) {
|
||||
return new Promise(resolve => {
|
||||
function finish(req, buffer) {
|
||||
resolve([req, buffer]);
|
||||
}
|
||||
chan.asyncOpen(new ChannelListener(finish, null, flags));
|
||||
});
|
||||
}
|
||||
|
||||
add_task(async function test_dual_stack() {
|
||||
let httpserv = new HttpServer();
|
||||
let content = "ok";
|
||||
httpserv.registerPathHandler("/", function handler(metadata, response) {
|
||||
response.setHeader("Content-Length", `${content.length}`);
|
||||
response.bodyOutputStream.write(content, content.length);
|
||||
});
|
||||
httpserv.start_dualStack(-1);
|
||||
|
||||
let chan = makeChan(`http://127.0.0.1:${httpserv.identity.primaryPort}/`);
|
||||
let [, response] = await channelOpenPromise(chan);
|
||||
Assert.equal(response, content);
|
||||
|
||||
chan = makeChan(`http://[::1]:${httpserv.identity.primaryPort}/`);
|
||||
[, response] = await channelOpenPromise(chan);
|
||||
Assert.equal(response, content);
|
||||
await new Promise(resolve => httpserv.stop(resolve));
|
||||
});
|
||||
|
||||
add_task(async function test_http() {
|
||||
let server = new NodeHTTPServer();
|
||||
await server.start();
|
||||
|
Loading…
Reference in New Issue
Block a user