66#include " system-streams.h"
77#include < workerd/io/worker-interface.h>
88#include " url-standard.h"
9+ #include < workerd/util/autogate.h>
910
1011
1112namespace workerd ::api {
@@ -262,7 +263,11 @@ jsg::Ref<Socket> connectImpl(
262263 return connectImplNoOutputLock (js, kj::mv (fetcher), kj::mv (address), kj::mv (options));
263264}
264265
265- jsg::Promise<void > Socket::close (jsg::Lock& js) {
266+ // Closes the underlying socket connection. This is an old implementation and will be removed soon.
267+ // See closeImplNew below for the new implementation.
268+ //
269+ // TODO(later): remove once safe
270+ jsg::Promise<void > Socket::closeImplOld (jsg::Lock& js) {
266271 // Forcibly close the readable/writable streams.
267272 auto cancelPromise = readable->getController ().cancel (js, kj::none);
268273 auto abortPromise = writable->getController ().abort (js, kj::none);
@@ -271,8 +276,58 @@ jsg::Promise<void> Socket::close(jsg::Lock& js) {
271276 return abortPromise.then (js, [this ](jsg::Lock& js) {
272277 resolveFulfiller (js, kj::none);
273278 return js.resolvedPromise ();
274- }, [this ](jsg::Lock& js, jsg::Value err) { return errorHandler (js, kj::mv (err)); });
275- }, [this ](jsg::Lock& js, jsg::Value err) { return errorHandler (js, kj::mv (err)); });
279+ }, [this ](jsg::Lock& js, jsg::Value err) {
280+ errorHandler (js, kj::mv (err));
281+ return js.resolvedPromise ();
282+ });
283+ }, [this ](jsg::Lock& js, jsg::Value err) {
284+ errorHandler (js, kj::mv (err));
285+ return js.resolvedPromise ();
286+ });
287+ }
288+
289+ // Closes the underlying socket connection, but only after the socket connection is properly
290+ // established through any configured proxy. This method also flushes the writable stream prior to
291+ // closing.
292+ jsg::Promise<void > Socket::closeImplNew (jsg::Lock& js) {
293+ if (isClosing) {
294+ return closedPromiseCopy.whenResolved (js);
295+ }
296+
297+ isClosing = true ;
298+ writable->getController ().setPendingClosure ();
299+ readable->getController ().setPendingClosure ();
300+
301+ // Wait until the socket connects (successfully or otherwise)
302+ return openedPromiseCopy.whenResolved (js).then (js, [this ](jsg::Lock& js) {
303+ if (!writable->getController ().isClosedOrClosing ()) {
304+ return writable->getController ().flush (js);
305+ } else {
306+ return js.resolvedPromise ();
307+ }
308+ }).then (js, [this ](jsg::Lock& js) {
309+ // Forcibly abort the readable/writable streams.
310+ auto cancelPromise = readable->getController ().cancel (js, kj::none);
311+ auto abortPromise = writable->getController ().abort (js, kj::none);
312+ // The below is effectively `Promise.all(cancelPromise, abortPromise)`
313+ return cancelPromise.then (js,
314+ [abortPromise = kj::mv (abortPromise)](jsg::Lock& js) mutable {
315+ return kj::mv (abortPromise);
316+ });
317+ }).then (js, [this ](jsg::Lock& js) {
318+ resolveFulfiller (js, kj::none);
319+ return js.resolvedPromise ();
320+ }).catch_ (js, [this ](jsg::Lock& js, jsg::Value err) {
321+ errorHandler (js, kj::mv (err));
322+ });
323+ }
324+
325+ jsg::Promise<void > Socket::close (jsg::Lock& js) {
326+ if (util::Autogate::isEnabled (util::AutogateKey::SOCKETS_AWAIT_PROXY_BEFORE_CLOSE)) {
327+ return closeImplNew (js);
328+ } else {
329+ return closeImplOld (js);
330+ }
276331}
277332
278333jsg::Ref<Socket> Socket::startTls (jsg::Lock& js, jsg::Optional<TlsOptions> tlsOptions) {
0 commit comments