@libp2p/http-utils 0.0.0

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.
@@ -0,0 +1,200 @@
1
+ import { Duplex } from 'node:stream'
2
+ import { byteStream } from 'it-byte-stream'
3
+ import type { Connection, Stream } from '@libp2p/interface'
4
+ import type { Socket, SocketConnectOpts, AddressInfo, SocketReadyState } from 'node:net'
5
+
6
+ const MAX_TIMEOUT = 2_147_483_647
7
+
8
+ class Libp2pSocket extends Duplex {
9
+ public readonly autoSelectFamilyAttemptedAddresses = []
10
+ public readonly connecting = false
11
+ public readonly pending = false
12
+ public readonly remoteAddress: string
13
+ public bytesRead: number
14
+ public bytesWritten: number
15
+ public timeout = MAX_TIMEOUT
16
+ public allowHalfOpen: boolean
17
+
18
+ private readonly stream: Stream
19
+
20
+ constructor (stream: Stream, connection: Connection) {
21
+ const bytes = byteStream(stream)
22
+
23
+ super({
24
+ write: (chunk, encoding, cb) => {
25
+ this.stream.log('write %d bytes', chunk.byteLength)
26
+
27
+ this.bytesWritten += chunk.byteLength
28
+ bytes.write(chunk)
29
+ .then(() => {
30
+ cb()
31
+ }, err => {
32
+ cb(err)
33
+ })
34
+ },
35
+ read: (size) => {
36
+ this.stream.log('asked to read %d bytes', size)
37
+
38
+ void Promise.resolve().then(async () => {
39
+ try {
40
+ while (true) {
41
+ const chunk = await bytes.read({
42
+ signal: AbortSignal.timeout(this.timeout)
43
+ })
44
+
45
+ if (chunk == null) {
46
+ this.stream.log('socket readable end closed')
47
+ this.push(null)
48
+ return
49
+ }
50
+
51
+ this.bytesRead += chunk.byteLength
52
+
53
+ this.stream.log('socket read %d bytes', chunk.byteLength)
54
+ const more = this.push(chunk.subarray())
55
+
56
+ if (!more) {
57
+ break
58
+ }
59
+ }
60
+ } catch (err: any) {
61
+ this.destroy(err)
62
+ }
63
+ })
64
+ },
65
+ destroy: (err, cb) => {
66
+ this.stream.log('destroy with %d bytes buffered - %e', this.bufferSize, err)
67
+
68
+ if (err != null) {
69
+ bytes.unwrap().abort(err)
70
+ cb()
71
+ } else {
72
+ bytes.unwrap().close()
73
+ .then(() => {
74
+ cb()
75
+ })
76
+ .catch(err => {
77
+ stream.abort(err)
78
+ cb(err)
79
+ })
80
+ }
81
+ },
82
+ final: (cb) => {
83
+ this.stream.log('final')
84
+
85
+ bytes.unwrap().closeWrite()
86
+ .then(() => {
87
+ cb()
88
+ })
89
+ .catch(err => {
90
+ bytes.unwrap().abort(err)
91
+ cb(err)
92
+ })
93
+ }
94
+ })
95
+
96
+ this.stream = stream
97
+ this.remoteAddress = connection.remoteAddr.toString()
98
+ this.bytesRead = 0
99
+ this.bytesWritten = 0
100
+ this.allowHalfOpen = true
101
+ }
102
+
103
+ public get readyState (): SocketReadyState {
104
+ if (this.stream.status === 'closed') {
105
+ return 'closed'
106
+ }
107
+
108
+ if (this.stream.writeStatus === 'closed' || this.stream.writeStatus === 'closing') {
109
+ return 'readOnly'
110
+ }
111
+
112
+ if (this.stream.readStatus === 'closed' || this.stream.readStatus === 'closing') {
113
+ return 'writeOnly'
114
+ }
115
+
116
+ return 'open'
117
+ }
118
+
119
+ public get bufferSize (): number {
120
+ return this.writableLength
121
+ }
122
+
123
+ destroySoon (): void {
124
+ this.stream.log('destroySoon with %d bytes buffered', this.bufferSize)
125
+ this.destroy()
126
+ }
127
+
128
+ connect (options: SocketConnectOpts, connectionListener?: () => void): this
129
+ connect (port: number, host: string, connectionListener?: () => void): this
130
+ connect (port: number, connectionListener?: () => void): this
131
+ connect (path: string, connectionListener?: () => void): this
132
+ connect (...args: any[]): this {
133
+ this.stream.log('connect %o', args)
134
+ return this
135
+ }
136
+
137
+ setEncoding (encoding?: BufferEncoding): this {
138
+ this.stream.log('setEncoding %s', encoding)
139
+ return this
140
+ }
141
+
142
+ resetAndDestroy (): this {
143
+ this.stream.log('resetAndDestroy')
144
+ this.stream.abort(new Error('Libp2pSocket.resetAndDestroy'))
145
+
146
+ return this
147
+ }
148
+
149
+ setTimeout (timeout: number, callback?: () => void): this {
150
+ this.stream.log('setTimeout %d', timeout)
151
+
152
+ if (callback != null) {
153
+ this.addListener('timeout', callback)
154
+ }
155
+
156
+ this.timeout = timeout === 0 ? MAX_TIMEOUT : timeout
157
+
158
+ return this
159
+ }
160
+
161
+ setNoDelay (noDelay?: boolean): this {
162
+ this.stream.log('setNoDelay %b', noDelay)
163
+
164
+ return this
165
+ }
166
+
167
+ setKeepAlive (enable?: boolean, initialDelay?: number): this {
168
+ this.stream.log('setKeepAlive %b %d', enable, initialDelay)
169
+
170
+ return this
171
+ }
172
+
173
+ address (): AddressInfo | Record<string, any> {
174
+ this.stream.log('address')
175
+
176
+ return {}
177
+ }
178
+
179
+ unref (): this {
180
+ this.stream.log('unref')
181
+
182
+ return this
183
+ }
184
+
185
+ ref (): this {
186
+ this.stream.log('ref')
187
+
188
+ return this
189
+ }
190
+
191
+ write (buffer: Uint8Array | string, cb?: (err?: Error) => void): boolean
192
+ write (str: Uint8Array | string, encoding?: BufferEncoding, cb?: (err?: Error) => void): boolean
193
+ write (chunk: any, encoding?: any, cb?: any): boolean {
194
+ return super.write(chunk, encoding, cb)
195
+ }
196
+ }
197
+
198
+ export function streamToSocket (stream: Stream, connection: Connection): Socket {
199
+ return new Libp2pSocket(stream, connection)
200
+ }