@php-wasm/node-7-4 3.1.34 → 3.1.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6243,6 +6243,7 @@ export function init(RuntimeName, PHPLoader) {
6243
6243
  O_NONBLOCK: 2048,
6244
6244
  POLLHUP: 16,
6245
6245
  SETFL_MASK: 3072,
6246
+ socketTimeouts: new Map,
6246
6247
  init: function () {
6247
6248
  // TODO: Move this to a library function that is made an onInit callback by the `__postset` suffix.
6248
6249
  if (PHPLoader.bindUserSpace) {
@@ -6672,6 +6673,29 @@ export function init(RuntimeName, PHPLoader) {
6672
6673
  return [promise, cancel];
6673
6674
  },
6674
6675
  noop: function () {},
6676
+ parseSocketTimeout: function (optionValuePtr, optionLen) {
6677
+ if (!optionValuePtr || optionLen < 8) {
6678
+ return null;
6679
+ }
6680
+ let seconds;
6681
+ let microseconds;
6682
+ if (optionLen >= 16) {
6683
+ seconds = Number(HEAP64[optionValuePtr >> 3]);
6684
+ microseconds = Number(HEAP64[(optionValuePtr + 8) >> 3]);
6685
+ } else {
6686
+ seconds = HEAP32[optionValuePtr >> 2];
6687
+ microseconds = HEAP32[(optionValuePtr + 4) >> 2];
6688
+ }
6689
+ if (
6690
+ !Number.isFinite(seconds) ||
6691
+ !Number.isFinite(microseconds) ||
6692
+ seconds < 0 ||
6693
+ microseconds < 0
6694
+ ) {
6695
+ return null;
6696
+ }
6697
+ return seconds * 1e3 + Math.ceil(microseconds / 1e3);
6698
+ },
6675
6699
  spawnProcess: function (command, args, options) {
6676
6700
  if (Module['spawnProcess']) {
6677
6701
  const spawned = Module['spawnProcess'](
@@ -6720,6 +6744,7 @@ export function init(RuntimeName, PHPLoader) {
6720
6744
  throw e;
6721
6745
  },
6722
6746
  shutdownSocket: function (socketd, how) {
6747
+ PHPWASM.socketTimeouts.delete(socketd);
6723
6748
  // This implementation only supports websockets at the moment
6724
6749
  const sock = getSocketFromFD(socketd);
6725
6750
  const peer = Object.values(sock.peers)[0];
@@ -6804,43 +6829,74 @@ export function init(RuntimeName, PHPLoader) {
6804
6829
  wakeUp(-ERRNO_CODES.ECONNREFUSED);
6805
6830
  return;
6806
6831
  }
6807
- // Wait for the connection to be established
6808
- const timeout = 3e4;
6809
- // 30 second timeout
6832
+ // Wait for the connection to be established. A zero timeval
6833
+ // disables the timeout, matching SO_SNDTIMEO semantics.
6834
+ const sendTimeout = PHPWASM.socketTimeouts.get(sockfd)?.send;
6835
+ const timeout = sendTimeout ?? 3e4;
6810
6836
  let resolved = false;
6811
- const timeoutId = setTimeout(() => {
6812
- if (!resolved) {
6813
- resolved = true;
6814
- wakeUp(-ERRNO_CODES.ETIMEDOUT);
6815
- }
6816
- }, timeout);
6817
- const handleOpen = () => {
6818
- if (!resolved) {
6819
- resolved = true;
6837
+ let timeoutId;
6838
+ let handleOpen;
6839
+ let handleError;
6840
+ let handleClose;
6841
+ const peer = PHPWASM.getAllPeers(sock).find(
6842
+ (candidate) => candidate.socket === ws
6843
+ );
6844
+
6845
+ const cleanupConnectListeners = () => {
6846
+ if (typeof timeoutId !== 'undefined') {
6820
6847
  clearTimeout(timeoutId);
6821
- ws.removeEventListener('error', handleError);
6822
- ws.removeEventListener('close', handleClose);
6823
- wakeUp(0);
6824
6848
  }
6849
+ ws.removeEventListener('open', handleOpen);
6850
+ ws.removeEventListener('error', handleError);
6851
+ ws.removeEventListener('close', handleClose);
6825
6852
  };
6826
- const handleError = () => {
6827
- if (!resolved) {
6828
- resolved = true;
6829
- clearTimeout(timeoutId);
6830
- ws.removeEventListener('open', handleOpen);
6831
- ws.removeEventListener('close', handleClose);
6832
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
6853
+
6854
+ const cleanupFailedConnect = (errno) => {
6855
+ try {
6856
+ if (
6857
+ ws.readyState !== ws.CLOSING &&
6858
+ ws.readyState !== ws.CLOSED
6859
+ ) {
6860
+ ws.close();
6861
+ }
6862
+ } catch (e) {
6863
+ // Ignore close errors on an already-failed connect.
6864
+ }
6865
+ if (peer) {
6866
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
6833
6867
  }
6868
+ sock.connecting = false;
6869
+ sock.error = errno;
6834
6870
  };
6835
- const handleClose = () => {
6871
+
6872
+ const finishConnect = (result) => {
6836
6873
  if (!resolved) {
6837
6874
  resolved = true;
6838
- clearTimeout(timeoutId);
6839
- ws.removeEventListener('open', handleOpen);
6840
- ws.removeEventListener('error', handleError);
6841
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
6875
+ cleanupConnectListeners();
6876
+ if (result < 0) {
6877
+ cleanupFailedConnect(-result);
6878
+ }
6879
+ wakeUp(result);
6842
6880
  }
6843
6881
  };
6882
+
6883
+ if (timeout > 0) {
6884
+ timeoutId = setTimeout(() => {
6885
+ finishConnect(-ERRNO_CODES.ETIMEDOUT);
6886
+ }, timeout);
6887
+ }
6888
+
6889
+ handleOpen = () => {
6890
+ finishConnect(0);
6891
+ };
6892
+
6893
+ handleError = () => {
6894
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
6895
+ };
6896
+
6897
+ handleClose = () => {
6898
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
6899
+ };
6844
6900
  ws.addEventListener('open', handleOpen);
6845
6901
  ws.addEventListener('error', handleError);
6846
6902
  ws.addEventListener('close', handleClose);
@@ -9800,25 +9856,35 @@ export function init(RuntimeName, PHPLoader) {
9800
9856
  const SO_SNDTIMEO = 67;
9801
9857
  const IPPROTO_TCP = 6;
9802
9858
  const TCP_NODELAY = 1;
9803
- // Options that we can forward to the WebSocket proxy
9859
+ if (
9860
+ level === SOL_SOCKET &&
9861
+ (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)
9862
+ ) {
9863
+ const timeoutMs = PHPWASM.parseSocketTimeout(
9864
+ optionValuePtr,
9865
+ optionLen
9866
+ );
9867
+ if (timeoutMs === null) {
9868
+ return -1;
9869
+ }
9870
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
9871
+ if (optionName === SO_RCVTIMEO) {
9872
+ timeouts.receive = timeoutMs;
9873
+ } else {
9874
+ timeouts.send = timeoutMs;
9875
+ }
9876
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
9877
+ return 0;
9878
+ }
9804
9879
  const isForwardable =
9805
9880
  (level === SOL_SOCKET && optionName === SO_KEEPALIVE) ||
9806
9881
  (level === IPPROTO_TCP && optionName === TCP_NODELAY);
9807
- // Options that we acknowledge but don't actually implement
9808
- // (WebSocket connections handle timeouts differently)
9809
- const isIgnorable =
9810
- level === SOL_SOCKET &&
9811
- (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
9812
- if (!isForwardable && !isIgnorable) {
9882
+ if (!isForwardable) {
9813
9883
  console.warn(
9814
9884
  `Unsupported socket option: ${level}, ${optionName}, ${optionValue}`
9815
9885
  );
9816
9886
  return -1;
9817
9887
  }
9818
- // For ignorable options, just return success
9819
- if (isIgnorable) {
9820
- return 0;
9821
- }
9822
9888
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
9823
9889
  if (!ws) {
9824
9890
  return -1;
@@ -10181,7 +10247,15 @@ export function init(RuntimeName, PHPLoader) {
10181
10247
 
10182
10248
  var _wasm_recv = function (sockfd, buffer, size, flags) {
10183
10249
  return Asyncify.handleSleep((wakeUp) => {
10250
+ const receiveTimeout =
10251
+ PHPWASM.socketTimeouts.get(sockfd)?.receive;
10252
+ const startedAt = Date.now();
10253
+ let resolved = false;
10254
+
10184
10255
  const poll = function () {
10256
+ if (resolved) {
10257
+ return;
10258
+ }
10185
10259
  let newl = ___syscall_recvfrom(
10186
10260
  sockfd,
10187
10261
  buffer,
@@ -10191,10 +10265,20 @@ export function init(RuntimeName, PHPLoader) {
10191
10265
  null
10192
10266
  );
10193
10267
  if (newl > 0) {
10268
+ resolved = true;
10194
10269
  wakeUp(newl);
10195
- } else if (newl === -6) {
10270
+ } else if (newl === -ERRNO_CODES.EAGAIN) {
10271
+ if (
10272
+ receiveTimeout > 0 &&
10273
+ Date.now() - startedAt >= receiveTimeout
10274
+ ) {
10275
+ resolved = true;
10276
+ wakeUp(-ERRNO_CODES.EAGAIN);
10277
+ return;
10278
+ }
10196
10279
  setTimeout(poll, 20);
10197
10280
  } else {
10281
+ resolved = true;
10198
10282
  wakeUp(0);
10199
10283
  }
10200
10284
  };
package/jspi/php_7_4.js CHANGED
@@ -5411,6 +5411,7 @@ var PHPWASM = {
5411
5411
  O_NONBLOCK: 2048,
5412
5412
  POLLHUP: 16,
5413
5413
  SETFL_MASK: 3072,
5414
+ socketTimeouts: new Map,
5414
5415
  init: function() {
5415
5416
  // TODO: Move this to a library function that is made an onInit callback by the `__postset` suffix.
5416
5417
  if (PHPLoader.bindUserSpace) {
@@ -5807,6 +5808,24 @@ var PHPWASM = {
5807
5808
  return [ promise, cancel ];
5808
5809
  },
5809
5810
  noop: function() {},
5811
+ parseSocketTimeout: function(optionValuePtr, optionLen) {
5812
+ if (!optionValuePtr || optionLen < 8) {
5813
+ return null;
5814
+ }
5815
+ let seconds;
5816
+ let microseconds;
5817
+ if (optionLen >= 16) {
5818
+ seconds = Number(HEAP64[optionValuePtr >> 3]);
5819
+ microseconds = Number(HEAP64[(optionValuePtr + 8) >> 3]);
5820
+ } else {
5821
+ seconds = HEAP32[optionValuePtr >> 2];
5822
+ microseconds = HEAP32[(optionValuePtr + 4) >> 2];
5823
+ }
5824
+ if (!Number.isFinite(seconds) || !Number.isFinite(microseconds) || seconds < 0 || microseconds < 0) {
5825
+ return null;
5826
+ }
5827
+ return seconds * 1e3 + Math.ceil(microseconds / 1e3);
5828
+ },
5810
5829
  spawnProcess: function(command, args, options) {
5811
5830
  if (Module["spawnProcess"]) {
5812
5831
  const spawned = Module["spawnProcess"](command, args, /**
@@ -5845,6 +5864,7 @@ var PHPWASM = {
5845
5864
  throw e;
5846
5865
  },
5847
5866
  shutdownSocket: function(socketd, how) {
5867
+ PHPWASM.socketTimeouts.delete(socketd);
5848
5868
  // This implementation only supports websockets at the moment
5849
5869
  const sock = getSocketFromFD(socketd);
5850
5870
  const peer = Object.values(sock.peers)[0];
@@ -5929,43 +5949,74 @@ function _wasm_connect(sockfd, addr, addrlen) {
5929
5949
  wakeUp(-ERRNO_CODES.ECONNREFUSED);
5930
5950
  return;
5931
5951
  }
5932
- // Wait for the connection to be established
5933
- const timeout = 3e4;
5934
- // 30 second timeout
5952
+ // Wait for the connection to be established. A zero timeval
5953
+ // disables the timeout, matching SO_SNDTIMEO semantics.
5954
+ const sendTimeout = PHPWASM.socketTimeouts.get(sockfd)?.send;
5955
+ const timeout = sendTimeout ?? 3e4;
5935
5956
  let resolved = false;
5936
- const timeoutId = setTimeout(() => {
5937
- if (!resolved) {
5938
- resolved = true;
5939
- wakeUp(-ERRNO_CODES.ETIMEDOUT);
5940
- }
5941
- }, timeout);
5942
- const handleOpen = () => {
5943
- if (!resolved) {
5944
- resolved = true;
5957
+ let timeoutId;
5958
+ let handleOpen;
5959
+ let handleError;
5960
+ let handleClose;
5961
+ const peer = PHPWASM.getAllPeers(sock).find(
5962
+ (candidate) => candidate.socket === ws
5963
+ );
5964
+
5965
+ const cleanupConnectListeners = () => {
5966
+ if (typeof timeoutId !== "undefined") {
5945
5967
  clearTimeout(timeoutId);
5946
- ws.removeEventListener("error", handleError);
5947
- ws.removeEventListener("close", handleClose);
5948
- wakeUp(0);
5949
5968
  }
5969
+ ws.removeEventListener("open", handleOpen);
5970
+ ws.removeEventListener("error", handleError);
5971
+ ws.removeEventListener("close", handleClose);
5950
5972
  };
5951
- const handleError = () => {
5952
- if (!resolved) {
5953
- resolved = true;
5954
- clearTimeout(timeoutId);
5955
- ws.removeEventListener("open", handleOpen);
5956
- ws.removeEventListener("close", handleClose);
5957
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5973
+
5974
+ const cleanupFailedConnect = (errno) => {
5975
+ try {
5976
+ if (
5977
+ ws.readyState !== ws.CLOSING &&
5978
+ ws.readyState !== ws.CLOSED
5979
+ ) {
5980
+ ws.close();
5981
+ }
5982
+ } catch (e) {
5983
+ // Ignore close errors on an already-failed connect.
5984
+ }
5985
+ if (peer) {
5986
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
5958
5987
  }
5988
+ sock.connecting = false;
5989
+ sock.error = errno;
5959
5990
  };
5960
- const handleClose = () => {
5991
+
5992
+ const finishConnect = (result) => {
5961
5993
  if (!resolved) {
5962
5994
  resolved = true;
5963
- clearTimeout(timeoutId);
5964
- ws.removeEventListener("open", handleOpen);
5965
- ws.removeEventListener("error", handleError);
5966
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5995
+ cleanupConnectListeners();
5996
+ if (result < 0) {
5997
+ cleanupFailedConnect(-result);
5998
+ }
5999
+ wakeUp(result);
5967
6000
  }
5968
6001
  };
6002
+
6003
+ if (timeout > 0) {
6004
+ timeoutId = setTimeout(() => {
6005
+ finishConnect(-ERRNO_CODES.ETIMEDOUT);
6006
+ }, timeout);
6007
+ }
6008
+
6009
+ handleOpen = () => {
6010
+ finishConnect(0);
6011
+ };
6012
+
6013
+ handleError = () => {
6014
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
6015
+ };
6016
+
6017
+ handleClose = () => {
6018
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
6019
+ };
5969
6020
  ws.addEventListener("open", handleOpen);
5970
6021
  ws.addEventListener("error", handleError);
5971
6022
  ws.addEventListener("close", handleClose);
@@ -8702,19 +8753,26 @@ function _wasm_setsockopt(socketd, level, optionName, optionValuePtr, optionLen)
8702
8753
  const SO_SNDTIMEO = 67;
8703
8754
  const IPPROTO_TCP = 6;
8704
8755
  const TCP_NODELAY = 1;
8756
+ if (level === SOL_SOCKET && (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)) {
8757
+ const timeoutMs = PHPWASM.parseSocketTimeout(optionValuePtr, optionLen);
8758
+ if (timeoutMs === null) {
8759
+ return -1;
8760
+ }
8761
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
8762
+ if (optionName === SO_RCVTIMEO) {
8763
+ timeouts.receive = timeoutMs;
8764
+ } else {
8765
+ timeouts.send = timeoutMs;
8766
+ }
8767
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
8768
+ return 0;
8769
+ }
8705
8770
  // Options that we can forward to the WebSocket proxy
8706
8771
  const isForwardable = (level === SOL_SOCKET && optionName === SO_KEEPALIVE) || (level === IPPROTO_TCP && optionName === TCP_NODELAY);
8707
- // Options that we acknowledge but don't actually implement
8708
- // (WebSocket connections handle timeouts differently)
8709
- const isIgnorable = level === SOL_SOCKET && (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
8710
- if (!isForwardable && !isIgnorable) {
8772
+ if (!isForwardable) {
8711
8773
  console.warn(`Unsupported socket option: ${level}, ${optionName}, ${optionValue}`);
8712
8774
  return -1;
8713
8775
  }
8714
- // For ignorable options, just return success
8715
- if (isIgnorable) {
8716
- return 0;
8717
- }
8718
8776
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
8719
8777
  if (!ws) {
8720
8778
  return -1;
@@ -8857,13 +8915,26 @@ var FS_createDevice = (...args) => FS.createDevice(...args);
8857
8915
 
8858
8916
  var _wasm_recv = function(sockfd, buffer, size, flags) {
8859
8917
  return Asyncify.handleSleep(wakeUp => {
8918
+ const receiveTimeout = PHPWASM.socketTimeouts.get(sockfd)?.receive;
8919
+ const startedAt = Date.now();
8920
+ let resolved = false;
8860
8921
  const poll = function() {
8922
+ if (resolved) {
8923
+ return;
8924
+ }
8861
8925
  let newl = ___syscall_recvfrom(sockfd, buffer, size, flags, null, null);
8862
8926
  if (newl > 0) {
8927
+ resolved = true;
8863
8928
  wakeUp(newl);
8864
- } else if (newl === -6) {
8929
+ } else if (newl === -ERRNO_CODES.EAGAIN) {
8930
+ if (receiveTimeout > 0 && Date.now() - startedAt >= receiveTimeout) {
8931
+ resolved = true;
8932
+ wakeUp(-ERRNO_CODES.EAGAIN);
8933
+ return;
8934
+ }
8865
8935
  setTimeout(poll, 20);
8866
8936
  } else {
8937
+ resolved = true;
8867
8938
  wakeUp(0);
8868
8939
  }
8869
8940
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@php-wasm/node-7-4",
3
- "version": "3.1.34",
3
+ "version": "3.1.36",
4
4
  "description": "PHP 7.4 WebAssembly binaries for node",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,10 +35,10 @@
35
35
  "node": ">=20.10.0",
36
36
  "npm": ">=10.2.3"
37
37
  },
38
- "gitHead": "86daccc84fe31900eb57c4c8e1c3ba21a7ae8d13",
38
+ "gitHead": "371f4fd6f9d56af6fb35b6c2cf0267edaea83755",
39
39
  "dependencies": {
40
40
  "wasm-feature-detect": "1.8.0",
41
- "@php-wasm/universal": "3.1.34"
41
+ "@php-wasm/universal": "3.1.36"
42
42
  },
43
43
  "packageManager": "npm@10.9.2",
44
44
  "overrides": {