bdy 1.24.0 → 1.24.1-dev

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.24.0",
4
+ "version": "1.24.1-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "homepage": "https://buddy.works/docs/cli",
@@ -9,6 +9,7 @@ const uuid_1 = require("uuid");
9
9
  const http_1 = __importDefault(require("http"));
10
10
  const texts_1 = require("../../texts");
11
11
  const tunnel_1 = require("../../types/tunnel");
12
+ const utils_1 = require("../../utils");
12
13
  class ServerHttp1 extends events_1.default {
13
14
  server;
14
15
  host;
@@ -16,6 +17,14 @@ class ServerHttp1 extends events_1.default {
16
17
  super();
17
18
  this.host = host;
18
19
  this.server = http_1.default.createServer((req, res) => this.processRequest(req, res));
20
+ // Backstop, not the primary bound - and that ordering is the whole point of the number. A
21
+ // channel is now bound to a CLIENT connection on the worker side, so it goes idle exactly when
22
+ // that connection does and the worker releases it there. This has to stay ABOVE the worker's own
23
+ // client keep-alive (node's 5s default on its h1 server) so the worker always decides first;
24
+ // reaping ahead of it would close a channel whose connection is still in use, which is precisely
25
+ // how node's 5s default here used to kill live client connections. Same invariant the h2 side
26
+ // already keeps with TUNNEL_HTTP2_SESSION_IDLE_TIMEOUT above the worker's session idle timeout.
27
+ this.server.keepAliveTimeout = utils_1.TUNNEL_HTTP1_CHANNEL_KEEPALIVE;
19
28
  this.server.on('connection', (socket) => {
20
29
  socket.id = (0, uuid_1.v4)();
21
30
  this.emit(tunnel_1.TUNNEL_HTTP_SOCKET.OPEN, socket);
@@ -187,6 +187,15 @@ class SshClient extends events_1.default {
187
187
  triggerStream(tunnel_1.TUNNEL_SSH_EVENT.STREAM_HTTP1, ip);
188
188
  return;
189
189
  }
190
+ if (type === tunnel_1.TUNNEL_SOCKET_TYPE.HTTP1_SHARED) {
191
+ logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('HTTP1_SHARED'));
192
+ // Same h1 path as above - only where the client address comes from differs, and requestIp()
193
+ // already asks the channel rather than the protocol, so this flag is the whole change. The
194
+ // handshake IP is a placeholder here.
195
+ stream.sharedIp = true;
196
+ triggerStream(tunnel_1.TUNNEL_SSH_EVENT.STREAM_HTTP1, ip);
197
+ return;
198
+ }
190
199
  if (type === tunnel_1.TUNNEL_SOCKET_TYPE.HTTP2) {
191
200
  logger_1.default.debug((0, texts_1.LOG_DETECTED_STREAM)('HTTP2'));
192
201
  triggerStream(tunnel_1.TUNNEL_SSH_EVENT.STREAM_HTTP2, ip);
@@ -119,6 +119,12 @@ var TUNNEL_SOCKET_TYPE;
119
119
  // agent that advertised the capability in its SSH ident - types 0-4 keep working forever, since
120
120
  // old workers will be opening them for a long time.
121
121
  TUNNEL_SOCKET_TYPE[TUNNEL_SOCKET_TYPE["HTTP2_SHARED"] = 5] = "HTTP2_SHARED";
122
+ // Same channel as HTTP1, and the address travels per request for the same reason as on type 5 -
123
+ // except here what makes the frame's IP field unusable is not many clients at once but time: the
124
+ // worker binds this channel to a client CONNECTION rather than to a single request, so the frame
125
+ // would only ever describe the request that opened it. Behind a proxy that multiplexes clients,
126
+ // consecutive requests on one connection belong to different ones.
127
+ TUNNEL_SOCKET_TYPE[TUNNEL_SOCKET_TYPE["HTTP1_SHARED"] = 6] = "HTTP1_SHARED";
122
128
  })(TUNNEL_SOCKET_TYPE || (exports.TUNNEL_SOCKET_TYPE = TUNNEL_SOCKET_TYPE = {}));
123
129
  var TUNNEL_REGION;
124
130
  (function (TUNNEL_REGION) {
@@ -36,9 +36,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.getRootDir = exports.TARGET_ONLY_PORT_REGEX = exports.TARGET_HTTP_REGEX = exports.TARGET_TCP_TLS_REGEX = exports.ApiErrorTunnelsDisabled = exports.ApiErrorWorkspaceFlagged = exports.ApiErrorTunnelLimitReached = exports.ApiErrorAgentLimitReached = exports.ApiErrorDomainRestricted = exports.ApiErrorTargetInvalid = exports.ApiErrorWrongToken = exports.ApiErrorFailedToConnect = exports.ApiErrorAgentNotFound = exports.ARTIFACT_AUTH_TYPE = exports.TARGET_EXEC_STATUS = exports.TARGET_SCOPE = exports.ARTIFACT_TYPE = exports.SANDBOX_SNAPSHOT_STATUS = exports.SANDBOX_APP_STATUS = exports.SANDBOX_SETUP_STATUS = exports.SANDBOX_EXEC_STATUS = exports.SANDBOX_EXEC_RUNTIME = exports.SANDBOX_STATUS = exports.REST_API_ENDPOINT = exports.REST_API_REGION = exports.DEFAULT_SANDBOX_CP_USER = exports.ARTIFACT_DEFAULT_VERSION = exports.SUGGESTED_BROWSER_VERSION = exports.AGENT_UPDATE_STATUS_TTL_MS = exports.AGENT_UPDATE_PROBATION_MS = exports.AGENT_UPDATE_MAX_ATTEMPTS = exports.AGENT_FETCH_RETRY_MAX = exports.AGENT_FETCH_RETRY_MIN = exports.SSH_HANDSHAKE_TIMEOUT = exports.DEFAULT_TIMEOUT = exports.getSshIdent = exports.TUNNEL_SSH_IDENT_SHARED_H2 = exports.TUNNEL_CLIENT_IP_HEADER = exports.TUNNEL_HTTP2_SHED_LOG_INTERVAL = exports.TUNNEL_HTTP2_MAX_SESSIONS = exports.TUNNEL_HTTP2_SESSION_IDLE_TIMEOUT = exports.TUNNEL_HTTP_RETRY_AFTER = exports.TUNNEL_HTTP_MAX_INFLIGHT = exports.TUNNEL_HTTP_CB_MIN_REQUESTS = exports.TUNNEL_HTTP_CB_WINDOW = exports.TUNNEL_MAX_REQUEST_SIZE_TO_SYNC = exports.TUNNEL_HTTP_LOG_MAX_REQUESTS = exports.TUNNEL_HTTP_LOG_MAX_BODY = exports.TUNNEL_HTTP_RATE_WINDOW = exports.TUNNEL_HTTP_RATE_LIMIT = void 0;
40
- exports.parseComposerJson = exports.ARTIFACT_README_NAMES = exports.ARTIFACT_README_SIZE_LIMIT = exports.filterIgnoredEntries = exports.parseIgnorePatterns = exports.ARTIFACT_IGNORE_DEFAULT = exports.createZip = exports.getGitName = exports.getGitEmail = exports.MCP_TOOL_CATEGORIES = exports.getBasicCommandTls = exports.getBasicCommandHttp = exports.getBasicCommandSandboxEndpoint = exports.getBasicCommandTcp = exports.getCurrentUser = exports.getServiceUser = exports.getRealTargetHost = exports.isWindows = exports.isLinux = exports.isOsx = exports.isDocker = exports.truncate = exports.sleep = exports.getLatestVersion = exports.getVersionEnv = exports.getCurrentVersionWithoutEnv = exports.isVersionNewer = exports.getVersionWithoutEnv = exports.parseVersion = exports.getVersion = exports.isInstalledByChoco = exports.isInstalledByApt = exports.isInstalledByBrew = exports.isInstalledByNpm = exports.isNpmGlobalWritable = exports.isPathWritable = exports.tryGetEmail = exports.execLocally = exports.getHomeDirectory = exports.HOME_DIR_NAME = exports.chownToHomeOwner = exports.isDomainAvailable = exports.sandboxScopeOptions = exports.newCommand = exports.formatBytes = exports.formatHelp = exports.getPlatform = exports.getHostname = exports.isStringRegExp = exports.getWorkingDir = void 0;
41
- exports.findArtifactReadme = exports.isPathInside = exports.getComposerReadmePath = void 0;
39
+ exports.TARGET_HTTP_REGEX = exports.TARGET_TCP_TLS_REGEX = exports.ApiErrorTunnelsDisabled = exports.ApiErrorWorkspaceFlagged = exports.ApiErrorTunnelLimitReached = exports.ApiErrorAgentLimitReached = exports.ApiErrorDomainRestricted = exports.ApiErrorTargetInvalid = exports.ApiErrorWrongToken = exports.ApiErrorFailedToConnect = exports.ApiErrorAgentNotFound = exports.ARTIFACT_AUTH_TYPE = exports.TARGET_EXEC_STATUS = exports.TARGET_SCOPE = exports.ARTIFACT_TYPE = exports.SANDBOX_SNAPSHOT_STATUS = exports.SANDBOX_APP_STATUS = exports.SANDBOX_SETUP_STATUS = exports.SANDBOX_EXEC_STATUS = exports.SANDBOX_EXEC_RUNTIME = exports.SANDBOX_STATUS = exports.REST_API_ENDPOINT = exports.REST_API_REGION = exports.DEFAULT_SANDBOX_CP_USER = exports.ARTIFACT_DEFAULT_VERSION = exports.SUGGESTED_BROWSER_VERSION = exports.AGENT_UPDATE_STATUS_TTL_MS = exports.AGENT_UPDATE_PROBATION_MS = exports.AGENT_UPDATE_MAX_ATTEMPTS = exports.AGENT_FETCH_RETRY_MAX = exports.AGENT_FETCH_RETRY_MIN = exports.SSH_HANDSHAKE_TIMEOUT = exports.DEFAULT_TIMEOUT = exports.getSshIdent = exports.TUNNEL_SSH_IDENT_SHARED_H1 = exports.TUNNEL_SSH_IDENT_SHARED_H2 = exports.TUNNEL_CLIENT_IP_HEADER = exports.TUNNEL_HTTP2_SHED_LOG_INTERVAL = exports.TUNNEL_HTTP2_MAX_SESSIONS = exports.TUNNEL_HTTP1_CHANNEL_KEEPALIVE = exports.TUNNEL_HTTP2_SESSION_IDLE_TIMEOUT = exports.TUNNEL_HTTP_RETRY_AFTER = exports.TUNNEL_HTTP_MAX_INFLIGHT = exports.TUNNEL_HTTP_CB_MIN_REQUESTS = exports.TUNNEL_HTTP_CB_WINDOW = exports.TUNNEL_MAX_REQUEST_SIZE_TO_SYNC = exports.TUNNEL_HTTP_LOG_MAX_REQUESTS = exports.TUNNEL_HTTP_LOG_MAX_BODY = exports.TUNNEL_HTTP_RATE_WINDOW = exports.TUNNEL_HTTP_RATE_LIMIT = void 0;
40
+ exports.ARTIFACT_README_SIZE_LIMIT = exports.filterIgnoredEntries = exports.parseIgnorePatterns = exports.ARTIFACT_IGNORE_DEFAULT = exports.createZip = exports.getGitName = exports.getGitEmail = exports.MCP_TOOL_CATEGORIES = exports.getBasicCommandTls = exports.getBasicCommandHttp = exports.getBasicCommandSandboxEndpoint = exports.getBasicCommandTcp = exports.getCurrentUser = exports.getServiceUser = exports.getRealTargetHost = exports.isWindows = exports.isLinux = exports.isOsx = exports.isDocker = exports.truncate = exports.sleep = exports.getLatestVersion = exports.getVersionEnv = exports.getCurrentVersionWithoutEnv = exports.isVersionNewer = exports.getVersionWithoutEnv = exports.parseVersion = exports.getVersion = exports.isInstalledByChoco = exports.isInstalledByApt = exports.isInstalledByBrew = exports.isInstalledByNpm = exports.isNpmGlobalWritable = exports.isPathWritable = exports.tryGetEmail = exports.execLocally = exports.getHomeDirectory = exports.HOME_DIR_NAME = exports.chownToHomeOwner = exports.isDomainAvailable = exports.sandboxScopeOptions = exports.newCommand = exports.formatBytes = exports.formatHelp = exports.getPlatform = exports.getHostname = exports.isStringRegExp = exports.getWorkingDir = exports.getRootDir = exports.TARGET_ONLY_PORT_REGEX = void 0;
41
+ exports.findArtifactReadme = exports.isPathInside = exports.getComposerReadmePath = exports.parseComposerJson = exports.ARTIFACT_README_NAMES = void 0;
42
42
  exports.apiErrorCodeToClass = apiErrorCodeToClass;
43
43
  exports.isFile = isFile;
44
44
  exports.getAppWorkspaceUrl = getAppWorkspaceUrl;
@@ -86,6 +86,17 @@ exports.TUNNEL_HTTP_RETRY_AFTER = 30;
86
86
  // Idle timeout sits above the worker's 60s so a healthy worker closes its side first and this
87
87
  // timer only fires against a worker that never reaps.
88
88
  exports.TUNNEL_HTTP2_SESSION_IDLE_TIMEOUT = 75000;
89
+ // The h1 equivalent, and the same ordering rule: node's 5s default on the channel server sat BELOW
90
+ // the worker's client keep-alive, so the agent reaped channels the worker still had connections for.
91
+ // Explicit, above it, and only ever a backstop - a healthy worker releases a channel when the client
92
+ // connection it is bound to goes away (worker/src/pipe.js, PipeHttp1.release).
93
+ //
94
+ // This number describes the CHANNEL and nothing else. node puts it on every response as
95
+ // `Keep-Alive: timeout=20`, and the worker strips that on the way to the client
96
+ // (PIPE_RESPONSE_HOP_HEADERS) precisely so it cannot be read as a promise about the client's own
97
+ // connection - which the worker closes on its own, much sooner. Raising it here without that strip
98
+ // in place tells clients to park connections that are already gone.
99
+ exports.TUNNEL_HTTP1_CHANNEL_KEEPALIVE = 20000;
89
100
  // Above the worker's global cap (2000): the worker sheds first, this only catches what slips
90
101
  // through (multiple workers, old workers, caps overshoot under a burst).
91
102
  exports.TUNNEL_HTTP2_MAX_SESSIONS = 4000;
@@ -106,7 +117,12 @@ exports.TUNNEL_CLIENT_IP_HEADER = 'x-bdy-client-ip';
106
117
  // one). Per SSH connection, so an updated agent gets the new mode on its next reconnect rather
107
118
  // than when its tunnel record is recreated. RFC 4253: printable ASCII, no spaces, no minus.
108
119
  exports.TUNNEL_SSH_IDENT_SHARED_H2 = 'hip1';
109
- const getSshIdent = () => `bdy_${(0, exports.getVersionWithoutEnv)((0, exports.getVersion)())}_${exports.TUNNEL_SSH_IDENT_SHARED_H2}`;
120
+ // The same announcement for TUNNEL_SOCKET_TYPE.HTTP1_SHARED, and deliberately a second token rather
121
+ // than a new meaning for the one above: agents advertising hip1 alone are already in the field and
122
+ // destroy an h1 channel of a type they do not know, so the worker has to be able to tell the two
123
+ // generations apart.
124
+ exports.TUNNEL_SSH_IDENT_SHARED_H1 = 'h1s1';
125
+ const getSshIdent = () => `bdy_${(0, exports.getVersionWithoutEnv)((0, exports.getVersion)())}_${exports.TUNNEL_SSH_IDENT_SHARED_H2}_${exports.TUNNEL_SSH_IDENT_SHARED_H1}`;
110
126
  exports.getSshIdent = getSshIdent;
111
127
  exports.DEFAULT_TIMEOUT = 30;
112
128
  exports.SSH_HANDSHAKE_TIMEOUT = 10000;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bdy",
3
3
  "preferGlobal": false,
4
- "version": "1.24.0",
4
+ "version": "1.24.1-dev",
5
5
  "type": "commonjs",
6
6
  "license": "MIT",
7
7
  "homepage": "https://buddy.works/docs/cli",