@php-wasm/node-8-5 3.1.33 → 3.1.35

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);
@@ -9820,25 +9876,35 @@ export function init(RuntimeName, PHPLoader) {
9820
9876
  const SO_SNDTIMEO = 67;
9821
9877
  const IPPROTO_TCP = 6;
9822
9878
  const TCP_NODELAY = 1;
9823
- // Options that we can forward to the WebSocket proxy
9879
+ if (
9880
+ level === SOL_SOCKET &&
9881
+ (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)
9882
+ ) {
9883
+ const timeoutMs = PHPWASM.parseSocketTimeout(
9884
+ optionValuePtr,
9885
+ optionLen
9886
+ );
9887
+ if (timeoutMs === null) {
9888
+ return -1;
9889
+ }
9890
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
9891
+ if (optionName === SO_RCVTIMEO) {
9892
+ timeouts.receive = timeoutMs;
9893
+ } else {
9894
+ timeouts.send = timeoutMs;
9895
+ }
9896
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
9897
+ return 0;
9898
+ }
9824
9899
  const isForwardable =
9825
9900
  (level === SOL_SOCKET && optionName === SO_KEEPALIVE) ||
9826
9901
  (level === IPPROTO_TCP && optionName === TCP_NODELAY);
9827
- // Options that we acknowledge but don't actually implement
9828
- // (WebSocket connections handle timeouts differently)
9829
- const isIgnorable =
9830
- level === SOL_SOCKET &&
9831
- (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
9832
- if (!isForwardable && !isIgnorable) {
9902
+ if (!isForwardable) {
9833
9903
  console.warn(
9834
9904
  `Unsupported socket option: ${level}, ${optionName}, ${optionValue}`
9835
9905
  );
9836
9906
  return -1;
9837
9907
  }
9838
- // For ignorable options, just return success
9839
- if (isIgnorable) {
9840
- return 0;
9841
- }
9842
9908
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
9843
9909
  if (!ws) {
9844
9910
  return -1;
@@ -10201,7 +10267,15 @@ export function init(RuntimeName, PHPLoader) {
10201
10267
 
10202
10268
  var _wasm_recv = function (sockfd, buffer, size, flags) {
10203
10269
  return Asyncify.handleSleep((wakeUp) => {
10270
+ const receiveTimeout =
10271
+ PHPWASM.socketTimeouts.get(sockfd)?.receive;
10272
+ const startedAt = Date.now();
10273
+ let resolved = false;
10274
+
10204
10275
  const poll = function () {
10276
+ if (resolved) {
10277
+ return;
10278
+ }
10205
10279
  let newl = ___syscall_recvfrom(
10206
10280
  sockfd,
10207
10281
  buffer,
@@ -10211,10 +10285,20 @@ export function init(RuntimeName, PHPLoader) {
10211
10285
  null
10212
10286
  );
10213
10287
  if (newl > 0) {
10288
+ resolved = true;
10214
10289
  wakeUp(newl);
10215
- } else if (newl === -6) {
10290
+ } else if (newl === -ERRNO_CODES.EAGAIN) {
10291
+ if (
10292
+ receiveTimeout > 0 &&
10293
+ Date.now() - startedAt >= receiveTimeout
10294
+ ) {
10295
+ resolved = true;
10296
+ wakeUp(-ERRNO_CODES.EAGAIN);
10297
+ return;
10298
+ }
10216
10299
  setTimeout(poll, 20);
10217
10300
  } else {
10301
+ resolved = true;
10218
10302
  wakeUp(0);
10219
10303
  }
10220
10304
  };
package/jspi/php_8_5.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);
@@ -8722,19 +8773,26 @@ function _wasm_setsockopt(socketd, level, optionName, optionValuePtr, optionLen)
8722
8773
  const SO_SNDTIMEO = 67;
8723
8774
  const IPPROTO_TCP = 6;
8724
8775
  const TCP_NODELAY = 1;
8776
+ if (level === SOL_SOCKET && (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)) {
8777
+ const timeoutMs = PHPWASM.parseSocketTimeout(optionValuePtr, optionLen);
8778
+ if (timeoutMs === null) {
8779
+ return -1;
8780
+ }
8781
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
8782
+ if (optionName === SO_RCVTIMEO) {
8783
+ timeouts.receive = timeoutMs;
8784
+ } else {
8785
+ timeouts.send = timeoutMs;
8786
+ }
8787
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
8788
+ return 0;
8789
+ }
8725
8790
  // Options that we can forward to the WebSocket proxy
8726
8791
  const isForwardable = (level === SOL_SOCKET && optionName === SO_KEEPALIVE) || (level === IPPROTO_TCP && optionName === TCP_NODELAY);
8727
- // Options that we acknowledge but don't actually implement
8728
- // (WebSocket connections handle timeouts differently)
8729
- const isIgnorable = level === SOL_SOCKET && (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
8730
- if (!isForwardable && !isIgnorable) {
8792
+ if (!isForwardable) {
8731
8793
  console.warn(`Unsupported socket option: ${level}, ${optionName}, ${optionValue}`);
8732
8794
  return -1;
8733
8795
  }
8734
- // For ignorable options, just return success
8735
- if (isIgnorable) {
8736
- return 0;
8737
- }
8738
8796
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
8739
8797
  if (!ws) {
8740
8798
  return -1;
@@ -8877,13 +8935,26 @@ var FS_createDevice = (...args) => FS.createDevice(...args);
8877
8935
 
8878
8936
  var _wasm_recv = function(sockfd, buffer, size, flags) {
8879
8937
  return Asyncify.handleSleep(wakeUp => {
8938
+ const receiveTimeout = PHPWASM.socketTimeouts.get(sockfd)?.receive;
8939
+ const startedAt = Date.now();
8940
+ let resolved = false;
8880
8941
  const poll = function() {
8942
+ if (resolved) {
8943
+ return;
8944
+ }
8881
8945
  let newl = ___syscall_recvfrom(sockfd, buffer, size, flags, null, null);
8882
8946
  if (newl > 0) {
8947
+ resolved = true;
8883
8948
  wakeUp(newl);
8884
- } else if (newl === -6) {
8949
+ } else if (newl === -ERRNO_CODES.EAGAIN) {
8950
+ if (receiveTimeout > 0 && Date.now() - startedAt >= receiveTimeout) {
8951
+ resolved = true;
8952
+ wakeUp(-ERRNO_CODES.EAGAIN);
8953
+ return;
8954
+ }
8885
8955
  setTimeout(poll, 20);
8886
8956
  } else {
8957
+ resolved = true;
8887
8958
  wakeUp(0);
8888
8959
  }
8889
8960
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@php-wasm/node-8-5",
3
- "version": "3.1.33",
3
+ "version": "3.1.35",
4
4
  "description": "PHP 8.5 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": "f80a40d763283d582f9b97b0d5eb59bf54d2a943",
38
+ "gitHead": "9d29b73246e12465902d8ce42c0fe747125bc69a",
39
39
  "dependencies": {
40
40
  "wasm-feature-detect": "1.8.0",
41
- "@php-wasm/universal": "3.1.33"
41
+ "@php-wasm/universal": "3.1.35"
42
42
  },
43
43
  "packageManager": "npm@10.9.2",
44
44
  "overrides": {