@php-wasm/web-8-3 3.1.34 → 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.
@@ -4796,6 +4796,7 @@ export function init(RuntimeName, PHPLoader) {
4796
4796
  O_NONBLOCK: 2048,
4797
4797
  POLLHUP: 16,
4798
4798
  SETFL_MASK: 3072,
4799
+ socketTimeouts: new Map,
4799
4800
  init: function () {
4800
4801
  if (PHPLoader.bindUserSpace) {
4801
4802
  addOnInit(() => {
@@ -5159,6 +5160,29 @@ export function init(RuntimeName, PHPLoader) {
5159
5160
  return [promise, cancel];
5160
5161
  },
5161
5162
  noop: function () {},
5163
+ parseSocketTimeout: function (optionValuePtr, optionLen) {
5164
+ if (!optionValuePtr || optionLen < 8) {
5165
+ return null;
5166
+ }
5167
+ let seconds;
5168
+ let microseconds;
5169
+ if (optionLen >= 16) {
5170
+ seconds = Number(HEAP64[optionValuePtr >> 3]);
5171
+ microseconds = Number(HEAP64[(optionValuePtr + 8) >> 3]);
5172
+ } else {
5173
+ seconds = HEAP32[optionValuePtr >> 2];
5174
+ microseconds = HEAP32[(optionValuePtr + 4) >> 2];
5175
+ }
5176
+ if (
5177
+ !Number.isFinite(seconds) ||
5178
+ !Number.isFinite(microseconds) ||
5179
+ seconds < 0 ||
5180
+ microseconds < 0
5181
+ ) {
5182
+ return null;
5183
+ }
5184
+ return seconds * 1e3 + Math.ceil(microseconds / 1e3);
5185
+ },
5162
5186
  spawnProcess: function (command, args, options) {
5163
5187
  if (Module['spawnProcess']) {
5164
5188
  const spawned = Module['spawnProcess'](command, args, {
@@ -5187,6 +5211,7 @@ export function init(RuntimeName, PHPLoader) {
5187
5211
  throw e;
5188
5212
  },
5189
5213
  shutdownSocket: function (socketd, how) {
5214
+ PHPWASM.socketTimeouts.delete(socketd);
5190
5215
  const sock = getSocketFromFD(socketd);
5191
5216
  const peer = Object.values(sock.peers)[0];
5192
5217
  if (!peer) {
@@ -5256,41 +5281,74 @@ export function init(RuntimeName, PHPLoader) {
5256
5281
  wakeUp(-ERRNO_CODES.ECONNREFUSED);
5257
5282
  return;
5258
5283
  }
5259
- const timeout = 3e4;
5284
+ // Wait for the connection to be established. A zero timeval
5285
+ // disables the timeout, matching SO_SNDTIMEO semantics.
5286
+ const sendTimeout = PHPWASM.socketTimeouts.get(sockfd)?.send;
5287
+ const timeout = sendTimeout ?? 3e4;
5260
5288
  let resolved = false;
5261
- const timeoutId = setTimeout(() => {
5262
- if (!resolved) {
5263
- resolved = true;
5264
- wakeUp(-ERRNO_CODES.ETIMEDOUT);
5265
- }
5266
- }, timeout);
5267
- const handleOpen = () => {
5268
- if (!resolved) {
5269
- resolved = true;
5289
+ let timeoutId;
5290
+ let handleOpen;
5291
+ let handleError;
5292
+ let handleClose;
5293
+ const peer = PHPWASM.getAllPeers(sock).find(
5294
+ (candidate) => candidate.socket === ws
5295
+ );
5296
+
5297
+ const cleanupConnectListeners = () => {
5298
+ if (typeof timeoutId !== 'undefined') {
5270
5299
  clearTimeout(timeoutId);
5271
- ws.removeEventListener('error', handleError);
5272
- ws.removeEventListener('close', handleClose);
5273
- wakeUp(0);
5274
5300
  }
5301
+ ws.removeEventListener('open', handleOpen);
5302
+ ws.removeEventListener('error', handleError);
5303
+ ws.removeEventListener('close', handleClose);
5275
5304
  };
5276
- const handleError = () => {
5277
- if (!resolved) {
5278
- resolved = true;
5279
- clearTimeout(timeoutId);
5280
- ws.removeEventListener('open', handleOpen);
5281
- ws.removeEventListener('close', handleClose);
5282
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5305
+
5306
+ const cleanupFailedConnect = (errno) => {
5307
+ try {
5308
+ if (
5309
+ ws.readyState !== ws.CLOSING &&
5310
+ ws.readyState !== ws.CLOSED
5311
+ ) {
5312
+ ws.close();
5313
+ }
5314
+ } catch (e) {
5315
+ // Ignore close errors on an already-failed connect.
5283
5316
  }
5317
+ if (peer) {
5318
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
5319
+ }
5320
+ sock.connecting = false;
5321
+ sock.error = errno;
5284
5322
  };
5285
- const handleClose = () => {
5323
+
5324
+ const finishConnect = (result) => {
5286
5325
  if (!resolved) {
5287
5326
  resolved = true;
5288
- clearTimeout(timeoutId);
5289
- ws.removeEventListener('open', handleOpen);
5290
- ws.removeEventListener('error', handleError);
5291
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5327
+ cleanupConnectListeners();
5328
+ if (result < 0) {
5329
+ cleanupFailedConnect(-result);
5330
+ }
5331
+ wakeUp(result);
5292
5332
  }
5293
5333
  };
5334
+
5335
+ if (timeout > 0) {
5336
+ timeoutId = setTimeout(() => {
5337
+ finishConnect(-ERRNO_CODES.ETIMEDOUT);
5338
+ }, timeout);
5339
+ }
5340
+
5341
+ handleOpen = () => {
5342
+ finishConnect(0);
5343
+ };
5344
+
5345
+ handleError = () => {
5346
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
5347
+ };
5348
+
5349
+ handleClose = () => {
5350
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
5351
+ };
5294
5352
  ws.addEventListener('open', handleOpen);
5295
5353
  ws.addEventListener('error', handleError);
5296
5354
  ws.addEventListener('close', handleClose);
@@ -7664,21 +7722,35 @@ export function init(RuntimeName, PHPLoader) {
7664
7722
  const SO_SNDTIMEO = 67;
7665
7723
  const IPPROTO_TCP = 6;
7666
7724
  const TCP_NODELAY = 1;
7725
+ if (
7726
+ level === SOL_SOCKET &&
7727
+ (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)
7728
+ ) {
7729
+ const timeoutMs = PHPWASM.parseSocketTimeout(
7730
+ optionValuePtr,
7731
+ optionLen
7732
+ );
7733
+ if (timeoutMs === null) {
7734
+ return -1;
7735
+ }
7736
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
7737
+ if (optionName === SO_RCVTIMEO) {
7738
+ timeouts.receive = timeoutMs;
7739
+ } else {
7740
+ timeouts.send = timeoutMs;
7741
+ }
7742
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
7743
+ return 0;
7744
+ }
7667
7745
  const isForwardable =
7668
7746
  (level === SOL_SOCKET && optionName === SO_KEEPALIVE) ||
7669
7747
  (level === IPPROTO_TCP && optionName === TCP_NODELAY);
7670
- const isIgnorable =
7671
- level === SOL_SOCKET &&
7672
- (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
7673
- if (!isForwardable && !isIgnorable) {
7748
+ if (!isForwardable) {
7674
7749
  console.warn(
7675
7750
  `Unsupported socket option: ${level}, ${optionName}, ${optionValue}`
7676
7751
  );
7677
7752
  return -1;
7678
7753
  }
7679
- if (isIgnorable) {
7680
- return 0;
7681
- }
7682
7754
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
7683
7755
  if (!ws) {
7684
7756
  return -1;
package/jspi/php_8_3.js CHANGED
@@ -4673,6 +4673,7 @@ export function init(RuntimeName, PHPLoader) {
4673
4673
  O_NONBLOCK: 2048,
4674
4674
  POLLHUP: 16,
4675
4675
  SETFL_MASK: 3072,
4676
+ socketTimeouts: new Map,
4676
4677
  init: function () {
4677
4678
  if (PHPLoader.bindUserSpace) {
4678
4679
  addOnInit(() => {
@@ -5036,6 +5037,29 @@ export function init(RuntimeName, PHPLoader) {
5036
5037
  return [promise, cancel];
5037
5038
  },
5038
5039
  noop: function () {},
5040
+ parseSocketTimeout: function (optionValuePtr, optionLen) {
5041
+ if (!optionValuePtr || optionLen < 8) {
5042
+ return null;
5043
+ }
5044
+ let seconds;
5045
+ let microseconds;
5046
+ if (optionLen >= 16) {
5047
+ seconds = Number(HEAP64[optionValuePtr >> 3]);
5048
+ microseconds = Number(HEAP64[(optionValuePtr + 8) >> 3]);
5049
+ } else {
5050
+ seconds = HEAP32[optionValuePtr >> 2];
5051
+ microseconds = HEAP32[(optionValuePtr + 4) >> 2];
5052
+ }
5053
+ if (
5054
+ !Number.isFinite(seconds) ||
5055
+ !Number.isFinite(microseconds) ||
5056
+ seconds < 0 ||
5057
+ microseconds < 0
5058
+ ) {
5059
+ return null;
5060
+ }
5061
+ return seconds * 1e3 + Math.ceil(microseconds / 1e3);
5062
+ },
5039
5063
  spawnProcess: function (command, args, options) {
5040
5064
  if (Module['spawnProcess']) {
5041
5065
  const spawned = Module['spawnProcess'](command, args, {
@@ -5064,6 +5088,7 @@ export function init(RuntimeName, PHPLoader) {
5064
5088
  throw e;
5065
5089
  },
5066
5090
  shutdownSocket: function (socketd, how) {
5091
+ PHPWASM.socketTimeouts.delete(socketd);
5067
5092
  const sock = getSocketFromFD(socketd);
5068
5093
  const peer = Object.values(sock.peers)[0];
5069
5094
  if (!peer) {
@@ -5133,41 +5158,74 @@ export function init(RuntimeName, PHPLoader) {
5133
5158
  wakeUp(-ERRNO_CODES.ECONNREFUSED);
5134
5159
  return;
5135
5160
  }
5136
- const timeout = 3e4;
5161
+ // Wait for the connection to be established. A zero timeval
5162
+ // disables the timeout, matching SO_SNDTIMEO semantics.
5163
+ const sendTimeout = PHPWASM.socketTimeouts.get(sockfd)?.send;
5164
+ const timeout = sendTimeout ?? 3e4;
5137
5165
  let resolved = false;
5138
- const timeoutId = setTimeout(() => {
5139
- if (!resolved) {
5140
- resolved = true;
5141
- wakeUp(-ERRNO_CODES.ETIMEDOUT);
5142
- }
5143
- }, timeout);
5144
- const handleOpen = () => {
5145
- if (!resolved) {
5146
- resolved = true;
5166
+ let timeoutId;
5167
+ let handleOpen;
5168
+ let handleError;
5169
+ let handleClose;
5170
+ const peer = PHPWASM.getAllPeers(sock).find(
5171
+ (candidate) => candidate.socket === ws
5172
+ );
5173
+
5174
+ const cleanupConnectListeners = () => {
5175
+ if (typeof timeoutId !== 'undefined') {
5147
5176
  clearTimeout(timeoutId);
5148
- ws.removeEventListener('error', handleError);
5149
- ws.removeEventListener('close', handleClose);
5150
- wakeUp(0);
5151
5177
  }
5178
+ ws.removeEventListener('open', handleOpen);
5179
+ ws.removeEventListener('error', handleError);
5180
+ ws.removeEventListener('close', handleClose);
5152
5181
  };
5153
- const handleError = () => {
5154
- if (!resolved) {
5155
- resolved = true;
5156
- clearTimeout(timeoutId);
5157
- ws.removeEventListener('open', handleOpen);
5158
- ws.removeEventListener('close', handleClose);
5159
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5182
+
5183
+ const cleanupFailedConnect = (errno) => {
5184
+ try {
5185
+ if (
5186
+ ws.readyState !== ws.CLOSING &&
5187
+ ws.readyState !== ws.CLOSED
5188
+ ) {
5189
+ ws.close();
5190
+ }
5191
+ } catch (e) {
5192
+ // Ignore close errors on an already-failed connect.
5160
5193
  }
5194
+ if (peer) {
5195
+ SOCKFS.websocket_sock_ops.removePeer(sock, peer);
5196
+ }
5197
+ sock.connecting = false;
5198
+ sock.error = errno;
5161
5199
  };
5162
- const handleClose = () => {
5200
+
5201
+ const finishConnect = (result) => {
5163
5202
  if (!resolved) {
5164
5203
  resolved = true;
5165
- clearTimeout(timeoutId);
5166
- ws.removeEventListener('open', handleOpen);
5167
- ws.removeEventListener('error', handleError);
5168
- wakeUp(-ERRNO_CODES.ECONNREFUSED);
5204
+ cleanupConnectListeners();
5205
+ if (result < 0) {
5206
+ cleanupFailedConnect(-result);
5207
+ }
5208
+ wakeUp(result);
5169
5209
  }
5170
5210
  };
5211
+
5212
+ if (timeout > 0) {
5213
+ timeoutId = setTimeout(() => {
5214
+ finishConnect(-ERRNO_CODES.ETIMEDOUT);
5215
+ }, timeout);
5216
+ }
5217
+
5218
+ handleOpen = () => {
5219
+ finishConnect(0);
5220
+ };
5221
+
5222
+ handleError = () => {
5223
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
5224
+ };
5225
+
5226
+ handleClose = () => {
5227
+ finishConnect(-ERRNO_CODES.ECONNREFUSED);
5228
+ };
5171
5229
  ws.addEventListener('open', handleOpen);
5172
5230
  ws.addEventListener('error', handleError);
5173
5231
  ws.addEventListener('close', handleClose);
@@ -7537,21 +7595,35 @@ export function init(RuntimeName, PHPLoader) {
7537
7595
  const SO_SNDTIMEO = 67;
7538
7596
  const IPPROTO_TCP = 6;
7539
7597
  const TCP_NODELAY = 1;
7598
+ if (
7599
+ level === SOL_SOCKET &&
7600
+ (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO)
7601
+ ) {
7602
+ const timeoutMs = PHPWASM.parseSocketTimeout(
7603
+ optionValuePtr,
7604
+ optionLen
7605
+ );
7606
+ if (timeoutMs === null) {
7607
+ return -1;
7608
+ }
7609
+ const timeouts = PHPWASM.socketTimeouts.get(socketd) || {};
7610
+ if (optionName === SO_RCVTIMEO) {
7611
+ timeouts.receive = timeoutMs;
7612
+ } else {
7613
+ timeouts.send = timeoutMs;
7614
+ }
7615
+ PHPWASM.socketTimeouts.set(socketd, timeouts);
7616
+ return 0;
7617
+ }
7540
7618
  const isForwardable =
7541
7619
  (level === SOL_SOCKET && optionName === SO_KEEPALIVE) ||
7542
7620
  (level === IPPROTO_TCP && optionName === TCP_NODELAY);
7543
- const isIgnorable =
7544
- level === SOL_SOCKET &&
7545
- (optionName === SO_RCVTIMEO || optionName === SO_SNDTIMEO);
7546
- if (!isForwardable && !isIgnorable) {
7621
+ if (!isForwardable) {
7547
7622
  console.warn(
7548
7623
  `Unsupported socket option: ${level}, ${optionName}, ${optionValue}`
7549
7624
  );
7550
7625
  return -1;
7551
7626
  }
7552
- if (isIgnorable) {
7553
- return 0;
7554
- }
7555
7627
  const ws = PHPWASM.getAllWebSockets(socketd)[0];
7556
7628
  if (!ws) {
7557
7629
  return -1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@php-wasm/web-8-3",
3
- "version": "3.1.34",
3
+ "version": "3.1.35",
4
4
  "description": "PHP 8.3 WebAssembly binaries for web",
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": "9d29b73246e12465902d8ce42c0fe747125bc69a",
39
39
  "dependencies": {
40
40
  "wasm-feature-detect": "1.8.0",
41
- "@php-wasm/universal": "3.1.34"
41
+ "@php-wasm/universal": "3.1.35"
42
42
  },
43
43
  "packageManager": "npm@10.9.2",
44
44
  "overrides": {