quico 0.1.1 → 0.1.3
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/README.md +216 -143
- package/examples/webtransport_datagram_echo_server/server.js +12 -18
- package/h3_server.js +416 -0
- package/h3_socket.js +473 -0
- package/index.js +3 -2611
- package/libs/crypto.js +182 -448
- package/libs/h3.js +312 -65
- package/libs/utils.js +12 -12
- package/package.json +60 -28
- package/quic_socket.js +1550 -0
- package/quico.svg +1 -1
package/h3_socket.js
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* quico: HTTP/3 and QUIC implementation for Node.js
|
|
3
|
+
* Copyright 2025 colocohen
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at:
|
|
8
|
+
*
|
|
9
|
+
* https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* This file is part of the open-source project hosted at:
|
|
12
|
+
* https://github.com/colocohen/quico
|
|
13
|
+
*
|
|
14
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
15
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
build_h3_frames,
|
|
21
|
+
build_settings_frame,
|
|
22
|
+
parse_h3_settings_frame,
|
|
23
|
+
extract_qpack_encoder_instructions_from_chunks,
|
|
24
|
+
extract_h3_frames_from_chunks,
|
|
25
|
+
parse_qpack_header_block,
|
|
26
|
+
build_close_webtransport,
|
|
27
|
+
build_http3_literal_headers_frame,
|
|
28
|
+
parse_webtransport_datagram,
|
|
29
|
+
qpack_static_table_entries
|
|
30
|
+
} from './libs/h3.js';
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
function Emitter(){
|
|
34
|
+
var listeners = {};
|
|
35
|
+
return {
|
|
36
|
+
on: function(name, fn){ (listeners[name] = listeners[name] || []).push(fn); },
|
|
37
|
+
emit: function(name){
|
|
38
|
+
var args = Array.prototype.slice.call(arguments, 1);
|
|
39
|
+
var arr = listeners[name] || [];
|
|
40
|
+
for (var i=0;i<arr.length;i++){ try{ arr[i].apply(null, args); }catch(e){} }
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
// ==== H3Socket ====
|
|
48
|
+
function H3Socket(options){
|
|
49
|
+
if (!(this instanceof H3Socket)) return new H3Socket(options);
|
|
50
|
+
options = options || {};
|
|
51
|
+
|
|
52
|
+
var ev = Emitter();
|
|
53
|
+
|
|
54
|
+
var context = {
|
|
55
|
+
isServer: true,
|
|
56
|
+
|
|
57
|
+
local_max_header_size: 65536,
|
|
58
|
+
local_qpack_max_table_capacity: 65536,
|
|
59
|
+
local_datagram_support: true,
|
|
60
|
+
|
|
61
|
+
remote_max_header_size: 0,//מתקבל ב settings - אחרי פיענוח
|
|
62
|
+
remote_qpack_max_table_capacity: 0,//מתקבל ב settings - גודל הטבלה המקסימלי
|
|
63
|
+
remote_datagram_support: null,
|
|
64
|
+
|
|
65
|
+
remote_qpack_table_base_index: 0,
|
|
66
|
+
remote_qpack_table_capacity: 0,
|
|
67
|
+
remote_qpack_dynamic_table: [],
|
|
68
|
+
|
|
69
|
+
receiving_streams: {},
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
function process_qpack_instructions(instructions){
|
|
74
|
+
var arr_inserts=[];
|
|
75
|
+
|
|
76
|
+
for(var i in instructions){
|
|
77
|
+
if(instructions[i].type=='set_dynamic_table_capacity'){
|
|
78
|
+
|
|
79
|
+
context.remote_qpack_table_capacity=instructions[i].capacity;
|
|
80
|
+
|
|
81
|
+
}else if(instructions[i].type=='insert_with_name_ref' || instructions[i].type=='insert_without_name_ref'){
|
|
82
|
+
var name=null;
|
|
83
|
+
var value=instructions[i].value;
|
|
84
|
+
|
|
85
|
+
if(instructions[i].type=='insert_with_name_ref'){
|
|
86
|
+
if(instructions[i].from_static_table==true){
|
|
87
|
+
if(instructions[i].name_index<qpack_static_table_entries.length){
|
|
88
|
+
name=qpack_static_table_entries[instructions[i].name_index][0];
|
|
89
|
+
}else{
|
|
90
|
+
//error...
|
|
91
|
+
}
|
|
92
|
+
}else{
|
|
93
|
+
//from dynamic...
|
|
94
|
+
var base_index = context.remote_qpack_table_base_index;
|
|
95
|
+
var name_index = instructions[i].name_index;
|
|
96
|
+
var dynamic_index = base_index - 1 - name_index;
|
|
97
|
+
var dynamic_table = context.remote_qpack_dynamic_table;
|
|
98
|
+
|
|
99
|
+
if (dynamic_index >= 0 && dynamic_index < dynamic_table.length) {
|
|
100
|
+
name = dynamic_table[dynamic_index][0];
|
|
101
|
+
} else {
|
|
102
|
+
// Error: missing reference
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}else{
|
|
106
|
+
name=instructions[i].name;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if(name!==null){
|
|
111
|
+
arr_inserts.push([name,value]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if(arr_inserts.length>0){
|
|
118
|
+
//console.log(arr_inserts);
|
|
119
|
+
|
|
120
|
+
for(var i in arr_inserts){
|
|
121
|
+
insert_into_qpack_remote_encoder_dynamic_table(arr_inserts[i][0],arr_inserts[i][1]);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//console.log(context.remote_qpack_dynamic_table);
|
|
125
|
+
//then... build_qpack_known_received_count(arr_inserts.length);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
function close_wt(stream_id){
|
|
132
|
+
var close_frame = build_h3_frames([
|
|
133
|
+
{ frame_type: 0x2843, payload: build_close_webtransport(0,'close...') }
|
|
134
|
+
]);
|
|
135
|
+
|
|
136
|
+
ev.emit('stream',stream_id,close_frame);
|
|
137
|
+
}
|
|
138
|
+
*/
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
function http_header_write(stream_id,headers){
|
|
142
|
+
|
|
143
|
+
var headers_payload = build_http3_literal_headers_frame(headers);
|
|
144
|
+
|
|
145
|
+
var http3_response=build_h3_frames([
|
|
146
|
+
{ frame_type: 1, payload: headers_payload }
|
|
147
|
+
]);
|
|
148
|
+
|
|
149
|
+
ev.emit('stream',stream_id,http3_response);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function http_body_write(stream_id,payload,fin){
|
|
153
|
+
|
|
154
|
+
if(payload==null){
|
|
155
|
+
ev.emit('stream',stream_id,null,true);
|
|
156
|
+
}else{
|
|
157
|
+
var http3_response=build_h3_frames([
|
|
158
|
+
{ frame_type: 0, payload: payload }
|
|
159
|
+
]);
|
|
160
|
+
|
|
161
|
+
ev.emit('stream',stream_id,http3_response,fin);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function process_http_frame(stream_id,frame_type,payload){
|
|
167
|
+
|
|
168
|
+
if(frame_type==1){
|
|
169
|
+
var headers={};
|
|
170
|
+
|
|
171
|
+
var dynamic_table = context.remote_qpack_dynamic_table;
|
|
172
|
+
var header_block = parse_qpack_header_block(payload);
|
|
173
|
+
|
|
174
|
+
if(header_block.insert_count<=dynamic_table.length){
|
|
175
|
+
var used_dynamic_ref=false;
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
for(var i2 in header_block.headers){
|
|
179
|
+
|
|
180
|
+
if(header_block.headers[i2].type=='indexed'){
|
|
181
|
+
if(header_block.headers[i2].from_static_table==true){
|
|
182
|
+
if(header_block.headers[i2].index<qpack_static_table_entries.length){
|
|
183
|
+
|
|
184
|
+
headers[qpack_static_table_entries[header_block.headers[i2].index][0]]=qpack_static_table_entries[header_block.headers[i2].index][1];
|
|
185
|
+
|
|
186
|
+
}else{
|
|
187
|
+
//error?
|
|
188
|
+
}
|
|
189
|
+
}else{
|
|
190
|
+
// from dynamic table
|
|
191
|
+
used_dynamic_ref=true;
|
|
192
|
+
|
|
193
|
+
var dynamic_index = header_block.base_index - 1 - header_block.headers[i2].index;
|
|
194
|
+
if (dynamic_index >= 0 && dynamic_index < dynamic_table.length) {
|
|
195
|
+
var [name, value] = dynamic_table[dynamic_index];
|
|
196
|
+
headers[name] = value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
}
|
|
200
|
+
}else if(header_block.headers[i2].type=='literal_with_name_ref'){
|
|
201
|
+
if(header_block.headers[i2].from_static_table==true){
|
|
202
|
+
if(header_block.headers[i2].name_index<qpack_static_table_entries.length){
|
|
203
|
+
headers[qpack_static_table_entries[header_block.headers[i2].name_index][0]]=header_block.headers[i2].value;
|
|
204
|
+
}
|
|
205
|
+
}else{
|
|
206
|
+
//from dynamic table...
|
|
207
|
+
used_dynamic_ref=true;
|
|
208
|
+
|
|
209
|
+
var dynamic_index = header_block.base_index - 1 - header_block.headers[i2].name_index;
|
|
210
|
+
if (dynamic_index >= 0 && dynamic_index < dynamic_table.length) {
|
|
211
|
+
var [name] = dynamic_table[dynamic_index];
|
|
212
|
+
headers[name] = header_block.headers[i2].value;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}else if(header_block.headers[i2].type=='literal_with_literal_name'){
|
|
216
|
+
headers[header_block.headers[i2].name]=header_block.headers[i2].value;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if(used_dynamic_ref==true){
|
|
221
|
+
//build and send the: build_qpack_block_header_ack(stream_id)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
ev.emit('http_headers',Number(stream_id),headers);
|
|
227
|
+
|
|
228
|
+
}else if(frame_type==0){
|
|
229
|
+
|
|
230
|
+
ev.emit('http_body',Number(stream_id),payload);
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function evict_qpack_remote_dynamic_table_if_needed(){
|
|
237
|
+
|
|
238
|
+
var entries = context.remote_qpack_dynamic_table;
|
|
239
|
+
var capacity = context.remote_qpack_table_capacity;
|
|
240
|
+
|
|
241
|
+
// חישוב גודל כולל של כל הערכים בטבלה
|
|
242
|
+
var totalSize = 0;
|
|
243
|
+
for (var i = 0; i < entries.length; i++) {
|
|
244
|
+
var name = entries[i][0];
|
|
245
|
+
var value = entries[i][1];
|
|
246
|
+
totalSize += name.length + value.length + 32;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// הדחה של ערכים ישנים עד שהטבלה בגבולות המותר
|
|
250
|
+
while (totalSize > capacity && entries.length > 0) {
|
|
251
|
+
var removed = entries.pop(); // מסיר את הערך האחרון
|
|
252
|
+
var removedSize = removed[0].length + removed[1].length + 32;
|
|
253
|
+
totalSize -= removedSize;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function insert_into_qpack_remote_encoder_dynamic_table(name, value){
|
|
259
|
+
var entry_size = name.length + value.length + 32;
|
|
260
|
+
|
|
261
|
+
if (entry_size > context.remote_qpack_table_capacity) return false;
|
|
262
|
+
|
|
263
|
+
context.remote_qpack_dynamic_table.unshift([name, value]);
|
|
264
|
+
context.remote_qpack_table_base_index++;
|
|
265
|
+
|
|
266
|
+
evict_qpack_remote_dynamic_table_if_needed();
|
|
267
|
+
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function process_settings_frame(payload){
|
|
272
|
+
var control_settings=parse_h3_settings_frame(payload);
|
|
273
|
+
//console.log(control_settings);
|
|
274
|
+
|
|
275
|
+
if('SETTINGS_QPACK_MAX_TABLE_CAPACITY' in control_settings && control_settings['SETTINGS_QPACK_MAX_TABLE_CAPACITY']>0){
|
|
276
|
+
context.remote_qpack_max_table_capacity=control_settings['SETTINGS_QPACK_MAX_TABLE_CAPACITY'];
|
|
277
|
+
|
|
278
|
+
evict_qpack_remote_dynamic_table_if_needed();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if('SETTINGS_MAX_FIELD_SECTION_SIZE' in control_settings && control_settings['SETTINGS_MAX_FIELD_SECTION_SIZE']>0){
|
|
282
|
+
context.remote_max_header_size=control_settings['SETTINGS_MAX_FIELD_SECTION_SIZE'];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if('SETTINGS_H3_DATAGRAM' in control_settings && control_settings['SETTINGS_H3_DATAGRAM']>0){
|
|
286
|
+
context.remote_datagram_support=Boolean(control_settings['SETTINGS_H3_DATAGRAM']);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
function stream_write(stream_id,data,fin){
|
|
292
|
+
|
|
293
|
+
//console.log('stream_id: '+stream_id);
|
|
294
|
+
|
|
295
|
+
if(stream_id in context.receiving_streams==false){
|
|
296
|
+
context.receiving_streams[stream_id]={
|
|
297
|
+
data_chunks: [],
|
|
298
|
+
next_offset: 0,
|
|
299
|
+
total_size: 0,
|
|
300
|
+
from_offset: 0,
|
|
301
|
+
type: null,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
context.receiving_streams[stream_id].data_chunks.push(data);
|
|
306
|
+
context.receiving_streams[stream_id].next_offset=context.receiving_streams[stream_id].next_offset+data.byteLength;
|
|
307
|
+
|
|
308
|
+
if(context.receiving_streams[stream_id].total_size==0){
|
|
309
|
+
if(typeof fin=='boolean' && fin==true){
|
|
310
|
+
context.receiving_streams[stream_id].total_size=context.receiving_streams[stream_id].next_offset;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if(context.receiving_streams[stream_id].type==null){
|
|
315
|
+
|
|
316
|
+
var is_unidirectional = (Number(stream_id) & 0x2) !== 0;
|
|
317
|
+
if (is_unidirectional) {
|
|
318
|
+
|
|
319
|
+
if (context.receiving_streams[stream_id].data_chunks.length>0 && context.receiving_streams[stream_id].data_chunks[0].byteLength > 0) {
|
|
320
|
+
|
|
321
|
+
var first_byte=context.receiving_streams[stream_id].data_chunks[0][0];
|
|
322
|
+
|
|
323
|
+
if(first_byte==0x00){
|
|
324
|
+
//console.log("✅ Control Stream");
|
|
325
|
+
context.receiving_streams[stream_id].type=0;
|
|
326
|
+
context.receiving_streams[stream_id].from_offset=1;
|
|
327
|
+
}else if(first_byte==0x01){
|
|
328
|
+
//console.log("✅ Push Stream");
|
|
329
|
+
|
|
330
|
+
}else if(first_byte==0x02){
|
|
331
|
+
//console.log("✅ QPACK Encoder Stream");
|
|
332
|
+
context.receiving_streams[stream_id].type=2;
|
|
333
|
+
context.receiving_streams[stream_id].from_offset=1;
|
|
334
|
+
}else if(first_byte==0x03){
|
|
335
|
+
//console.log("✅ QPACK Decoder Stream");
|
|
336
|
+
context.receiving_streams[stream_id].type=3;
|
|
337
|
+
context.receiving_streams[stream_id].from_offset=1;
|
|
338
|
+
}else{
|
|
339
|
+
//console.log("❓ Unknown Unidirectional Stream");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
} else {
|
|
344
|
+
//console.log("🔄 Bidirectional (HTTP Request/Response)");
|
|
345
|
+
context.receiving_streams[stream_id].type=4;
|
|
346
|
+
context.receiving_streams[stream_id].from_offset=0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
var ext=null;
|
|
354
|
+
|
|
355
|
+
if(context.receiving_streams[stream_id].type==2 || context.receiving_streams[stream_id].type==3){
|
|
356
|
+
ext = extract_qpack_encoder_instructions_from_chunks(context.receiving_streams[stream_id].data_chunks,context.receiving_streams[stream_id].from_offset);
|
|
357
|
+
}else{
|
|
358
|
+
ext = extract_h3_frames_from_chunks(context.receiving_streams[stream_id].data_chunks,context.receiving_streams[stream_id].from_offset);
|
|
359
|
+
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
//console.log('stream '+stream_id+': -----');
|
|
363
|
+
//console.log(context.receiving_streams[stream_id]);
|
|
364
|
+
|
|
365
|
+
if(ext && context.receiving_streams[stream_id].from_offset!==ext.new_from_offset){//
|
|
366
|
+
context.receiving_streams[stream_id].from_offset=ext.new_from_offset;
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
if(context.receiving_streams[stream_id].type==0){
|
|
371
|
+
|
|
372
|
+
for(var i in ext.frames){
|
|
373
|
+
|
|
374
|
+
//console.log('frame:');
|
|
375
|
+
//console.log(ext.frames[i]);
|
|
376
|
+
|
|
377
|
+
if(ext.frames[i].frame_type==0x04){
|
|
378
|
+
//SETTINGS
|
|
379
|
+
process_settings_frame(ext.frames[i].payload);
|
|
380
|
+
|
|
381
|
+
}else if(ext.frames[i].frame_type==0x07){
|
|
382
|
+
//GOAWAY
|
|
383
|
+
|
|
384
|
+
//TODO...
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
}else if(context.receiving_streams[stream_id].type==2){
|
|
389
|
+
|
|
390
|
+
//console.log(ext);
|
|
391
|
+
|
|
392
|
+
process_qpack_instructions(ext.instructions);
|
|
393
|
+
|
|
394
|
+
}else if(context.receiving_streams[stream_id].type==3){
|
|
395
|
+
|
|
396
|
+
}else if(context.receiving_streams[stream_id].type==4){
|
|
397
|
+
for(var i in ext.frames){
|
|
398
|
+
process_http_frame(Number(stream_id),ext.frames[i].frame_type,ext.frames[i].payload);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
function connect(){
|
|
411
|
+
|
|
412
|
+
if(context.local_max_header_size>0){
|
|
413
|
+
|
|
414
|
+
}
|
|
415
|
+
var settings_frame=build_settings_frame({
|
|
416
|
+
SETTINGS_QPACK_MAX_TABLE_CAPACITY: context.local_qpack_max_table_capacity,
|
|
417
|
+
SETTINGS_MAX_FIELD_SECTION_SIZE: context.local_max_header_size,
|
|
418
|
+
SETTINGS_ENABLE_WEBTRANSPORT: context.local_datagram_support,
|
|
419
|
+
SETTINGS_H3_DATAGRAM: context.local_datagram_support,
|
|
420
|
+
SETTINGS_ENABLE_CONNECT_PROTOCOL: context.local_datagram_support,
|
|
421
|
+
SETTINGS_WT_MAX_SESSIONS: 1
|
|
422
|
+
//SETTINGS_NO_RFC9114_LEGACY_CODEPOINT: 1
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
var control_stream_frames=build_h3_frames([
|
|
426
|
+
{ frame_type: 0x04, payload: settings_frame },
|
|
427
|
+
]);
|
|
428
|
+
|
|
429
|
+
ev.emit('stream',3,new Uint8Array([0x00]));
|
|
430
|
+
ev.emit('stream',3,control_stream_frames);
|
|
431
|
+
|
|
432
|
+
ev.emit('stream',7,new Uint8Array([0x02]));
|
|
433
|
+
|
|
434
|
+
ev.emit('stream',11,new Uint8Array([0x03]));
|
|
435
|
+
|
|
436
|
+
//console.log('sending connect()............................');
|
|
437
|
+
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
setTimeout(connect,0);
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
var api = {
|
|
445
|
+
on: function(name, fn){ ev.on(name, fn); },
|
|
446
|
+
|
|
447
|
+
stream: stream_write,
|
|
448
|
+
|
|
449
|
+
//close_wt: close_wt,
|
|
450
|
+
|
|
451
|
+
http_header: http_header_write,
|
|
452
|
+
http_body: http_body_write,
|
|
453
|
+
|
|
454
|
+
end: function(data){
|
|
455
|
+
if (context.destroyed) return;
|
|
456
|
+
if (typeof data !== 'undefined' && data !== null) api.write(data);
|
|
457
|
+
try { context.transport && context.transport.end && context.transport.end(); } catch(e){}
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
destroy: function(){
|
|
461
|
+
if (context.destroyed) return;
|
|
462
|
+
context.destroyed = true;
|
|
463
|
+
try { context.transport && context.transport.destroy && context.transport.destroy(); } catch(e){}
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
for (var k in api) if (Object.prototype.hasOwnProperty.call(api,k)) this[k] = api[k];
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
return this;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export default H3Socket;
|