appium-ios-tuntap 0.4.1 → 0.4.2
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.
- package/CHANGELOG.md +6 -0
- package/lib/tunnel/buffer-utils.d.ts +4 -0
- package/lib/tunnel/buffer-utils.js +15 -0
- package/lib/tunnel/manager.js +7 -5
- package/package.json +1 -1
- package/prebuilds/darwin-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/darwin-x64/appium-ios-tuntap.node +0 -0
- package/prebuilds/linux-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/linux-x64/appium-ios-tuntap.node +0 -0
- package/prebuilds/win32-arm64/appium-ios-tuntap.node +0 -0
- package/prebuilds/win32-x64/appium-ios-tuntap.node +0 -0
- package/src/native/tun_backend_darwin.cc +7 -4
- package/src/tuntap.cc +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
## [0.4.2](https://github.com/appium/appium-ios-tuntap/compare/v0.4.1...v0.4.2) (2026-06-01)
|
|
2
|
+
|
|
3
|
+
### Miscellaneous Chores
|
|
4
|
+
|
|
5
|
+
* Tune further tunnel perf ([#46](https://github.com/appium/appium-ios-tuntap/issues/46)) ([d422937](https://github.com/appium/appium-ios-tuntap/commit/d422937a47b16bf1c984030772d1de6e555609e6))
|
|
6
|
+
|
|
1
7
|
## [0.4.1](https://github.com/appium/appium-ios-tuntap/compare/v0.4.0...v0.4.1) (2026-05-31)
|
|
2
8
|
|
|
3
9
|
### Bug Fixes
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Append socket/TUN chunks without repeated Buffer.concat growth copies.
|
|
3
|
+
*/
|
|
4
|
+
export function appendBuffer(existing, chunk) {
|
|
5
|
+
if (chunk.length === 0) {
|
|
6
|
+
return existing;
|
|
7
|
+
}
|
|
8
|
+
if (existing.length === 0) {
|
|
9
|
+
return Buffer.from(chunk);
|
|
10
|
+
}
|
|
11
|
+
const combined = Buffer.allocUnsafe(existing.length + chunk.length);
|
|
12
|
+
existing.copy(combined, 0);
|
|
13
|
+
chunk.copy(combined, existing.length);
|
|
14
|
+
return combined;
|
|
15
|
+
}
|
package/lib/tunnel/manager.js
CHANGED
|
@@ -3,6 +3,7 @@ import { TunTap } from '../TunTap.js';
|
|
|
3
3
|
import { EventEmitter } from 'node:events';
|
|
4
4
|
import { Buffer } from 'node:buffer';
|
|
5
5
|
import { CD_TUNNEL_HANDSHAKE_TIMEOUT_MS, CD_TUNNEL_HEADER_SIZE, CD_TUNNEL_MAGIC, CD_TUNNEL_MAGIC_SIZE, CD_TUNNEL_MTU, IPV6_HEADER_SIZE, IPV6_VERSION, IPPROTO_TCP, IPPROTO_UDP, } from './constants.js';
|
|
6
|
+
import { appendBuffer } from './buffer-utils.js';
|
|
6
7
|
/**
|
|
7
8
|
* Bridges a CoreDevice tunnel `Socket` and a {@link TunTap} interface: IPv6 framing, TUN I/O, and packet fan-out.
|
|
8
9
|
* Emits {@link TunnelManagerEvents} (currently `data` with {@link PacketData}) for TCP/UDP packets, same as registered consumers.
|
|
@@ -125,14 +126,15 @@ export class TunnelManager extends EventEmitter {
|
|
|
125
126
|
}
|
|
126
127
|
this.deviceConn = deviceConn;
|
|
127
128
|
log.debug(`Starting bidirectional data forwarding for ${this.tun.name}`);
|
|
129
|
+
deviceConn.setNoDelay(true);
|
|
130
|
+
deviceConn.setKeepAlive(true, 1000);
|
|
128
131
|
// Handle data from the device connection
|
|
129
132
|
deviceConn.on('data', (data) => {
|
|
130
133
|
if (this.cancelled) {
|
|
131
134
|
return;
|
|
132
135
|
}
|
|
133
136
|
try {
|
|
134
|
-
|
|
135
|
-
this.buffer = Buffer.concat([this.buffer, data]);
|
|
137
|
+
this.buffer = appendBuffer(this.buffer, data);
|
|
136
138
|
// Process IPv6 packets
|
|
137
139
|
this.processBuffer();
|
|
138
140
|
}
|
|
@@ -258,10 +260,10 @@ export class TunnelManager extends EventEmitter {
|
|
|
258
260
|
if (this.cancelled || !data.length || deviceConn.destroyed) {
|
|
259
261
|
return;
|
|
260
262
|
}
|
|
261
|
-
if (data.length >= IPV6_HEADER_SIZE) {
|
|
263
|
+
if (this.hasPacketTap() && data.length >= IPV6_HEADER_SIZE) {
|
|
262
264
|
log.debug(`TUN → Device: ${data.length} bytes, IPv6 src=${formatIPv6Address(data.subarray(8, 24))}, dst=${formatIPv6Address(data.subarray(24, 40))}`);
|
|
263
265
|
}
|
|
264
|
-
else {
|
|
266
|
+
else if (this.hasPacketTap()) {
|
|
265
267
|
log.debug(`TUN → Device: ${data.length} bytes (too small for IPv6 header)`);
|
|
266
268
|
}
|
|
267
269
|
deviceConn.write(data);
|
|
@@ -401,7 +403,7 @@ function readCdTunnelResponse(socket, timeoutMs) {
|
|
|
401
403
|
};
|
|
402
404
|
const onData = (chunk) => {
|
|
403
405
|
log.debug('Received data chunk:', chunk.length, 'bytes');
|
|
404
|
-
buffer =
|
|
406
|
+
buffer = appendBuffer(buffer, chunk);
|
|
405
407
|
if (buffer.length >= CD_TUNNEL_HEADER_SIZE) {
|
|
406
408
|
const payloadLength = buffer.readUInt16BE(CD_TUNNEL_MAGIC_SIZE);
|
|
407
409
|
log.debug('Expected total packet length:', CD_TUNNEL_HEADER_SIZE + payloadLength, 'current buffer:', buffer.length);
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -91,8 +91,11 @@ public:
|
|
|
91
91
|
return ReadPacketStatus::Error;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
const size_t read_cap = max_payload_size + kUtunHeaderSize;
|
|
95
|
+
if (read_frame_.size() < read_cap) {
|
|
96
|
+
read_frame_.resize(read_cap);
|
|
97
|
+
}
|
|
98
|
+
ssize_t bytes_read = read(fd_.get(), read_frame_.data(), read_cap);
|
|
96
99
|
if (bytes_read < 0) {
|
|
97
100
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
98
101
|
out.clear();
|
|
@@ -111,9 +114,8 @@ public:
|
|
|
111
114
|
}
|
|
112
115
|
|
|
113
116
|
const auto payload_len = static_cast<size_t>(bytes_read - kUtunHeaderSize);
|
|
114
|
-
// Collapse the utun 4-byte address-family prefix in-place.
|
|
115
|
-
memmove(out.data(), out.data() + kUtunHeaderSize, payload_len);
|
|
116
117
|
out.resize(payload_len);
|
|
118
|
+
memcpy(out.data(), read_frame_.data() + kUtunHeaderSize, payload_len);
|
|
117
119
|
return ReadPacketStatus::Data;
|
|
118
120
|
}
|
|
119
121
|
|
|
@@ -168,6 +170,7 @@ private:
|
|
|
168
170
|
return false;
|
|
169
171
|
}
|
|
170
172
|
|
|
173
|
+
std::vector<uint8_t> read_frame_;
|
|
171
174
|
std::vector<uint8_t> write_frame_;
|
|
172
175
|
};
|
|
173
176
|
|
package/src/tuntap.cc
CHANGED
|
@@ -209,12 +209,14 @@ Napi::Value TunDevice::StartPolling(const Napi::CallbackInfo& info) {
|
|
|
209
209
|
buffer_size = size;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
// Queue depth > 1 lets the poll thread post the next packet while JS is still
|
|
213
|
+
// handling the previous callback (still serialized on the main thread).
|
|
212
214
|
tsfn_ = Napi::ThreadSafeFunction::New(
|
|
213
215
|
env,
|
|
214
216
|
info[0].As<Napi::Function>(),
|
|
215
217
|
"TunDeviceDataCallback",
|
|
216
218
|
0,
|
|
217
|
-
|
|
219
|
+
8);
|
|
218
220
|
|
|
219
221
|
uv_loop_t* loop = nullptr;
|
|
220
222
|
napi_status napi_st = napi_get_uv_event_loop(env, &loop);
|