1
1
import { createTransport } from '@sentry/core' ;
2
2
import { EventEnvelope , EventItem } from '@sentry/types' ;
3
- import { createEnvelope , serializeEnvelope } from '@sentry/utils' ;
3
+ import { createEnvelope , serializeEnvelope , addItemToEnvelope , createAttachmentEnvelopeItem } from '@sentry/utils' ;
4
4
import * as http from 'http' ;
5
5
import { TextEncoder } from 'util' ;
6
6
7
7
import { makeNodeTransport } from '../../src/transports' ;
8
+ import { createGunzip } from 'zlib' ;
8
9
9
10
jest . mock ( '@sentry/core' , ( ) => {
10
11
const actualCore = jest . requireActual ( '@sentry/core' ) ;
@@ -34,17 +35,21 @@ let testServer: http.Server | undefined;
34
35
35
36
function setupTestServer (
36
37
options : TestServerOptions ,
37
- requestInspector ?: ( req : http . IncomingMessage , body : string ) => void ,
38
+ requestInspector ?: ( req : http . IncomingMessage , body : string , raw : Uint8Array ) => void ,
38
39
) {
39
40
testServer = http . createServer ( ( req , res ) => {
40
- let body = '' ;
41
+ let chunks : Buffer [ ] = [ ] ;
41
42
42
- req . on ( 'data' , data => {
43
- body += data ;
43
+ const stream = req . headers [ 'content-encoding' ] === 'gzip' ? req . pipe ( createGunzip ( { } ) ) : req ;
44
+
45
+ stream . on ( 'error' , ( ) => { } ) ;
46
+
47
+ stream . on ( 'data' , data => {
48
+ chunks . push ( data ) ;
44
49
} ) ;
45
50
46
- req . on ( 'end' , ( ) => {
47
- requestInspector ?.( req , body ) ;
51
+ stream . on ( 'end' , ( ) => {
52
+ requestInspector ?.( req , chunks . join ( ) , Buffer . concat ( chunks ) ) ;
48
53
} ) ;
49
54
50
55
res . writeHead ( options . statusCode , options . responseHeaders ) ;
@@ -69,6 +74,13 @@ const EVENT_ENVELOPE = createEnvelope<EventEnvelope>({ event_id: 'aa3ff046696b4b
69
74
70
75
const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ;
71
76
77
+ const ATTACHMENT_ITEM = createAttachmentEnvelopeItem (
78
+ { filename : 'empty-file.bin' , data : new Uint8Array ( 50_000 ) } ,
79
+ new TextEncoder ( ) ,
80
+ ) ;
81
+ const EVENT_ATTACHMENT_ENVELOPE = addItemToEnvelope ( EVENT_ENVELOPE , ATTACHMENT_ITEM ) ;
82
+ const SERIALIZED_EVENT_ATTACHMENT_ENVELOPE = serializeEnvelope ( EVENT_ATTACHMENT_ENVELOPE , new TextEncoder ( ) ) ;
83
+
72
84
const defaultOptions = {
73
85
url : TEST_SERVER_URL ,
74
86
recordDroppedEvent : ( ) => undefined ,
@@ -155,6 +167,40 @@ describe('makeNewHttpTransport()', () => {
155
167
} ) ;
156
168
} ) ;
157
169
170
+ describe ( 'compression' , ( ) => {
171
+ it ( 'small envelopes should not be compressed' , async ( ) => {
172
+ await setupTestServer (
173
+ {
174
+ statusCode : SUCCESS ,
175
+ responseHeaders : { } ,
176
+ } ,
177
+ ( req , body ) => {
178
+ expect ( req . headers [ 'content-encoding' ] ) . toBeUndefined ( ) ;
179
+ expect ( body ) . toBe ( SERIALIZED_EVENT_ENVELOPE ) ;
180
+ } ,
181
+ ) ;
182
+
183
+ const transport = makeNodeTransport ( defaultOptions ) ;
184
+ await transport . send ( EVENT_ENVELOPE ) ;
185
+ } ) ;
186
+
187
+ it ( 'large envelopes should be compressed' , async ( ) => {
188
+ await setupTestServer (
189
+ {
190
+ statusCode : SUCCESS ,
191
+ responseHeaders : { } ,
192
+ } ,
193
+ ( req , _ , raw ) => {
194
+ expect ( req . headers [ 'content-encoding' ] ) . toEqual ( 'gzip' ) ;
195
+ expect ( raw . buffer ) . toStrictEqual ( SERIALIZED_EVENT_ATTACHMENT_ENVELOPE . buffer ) ;
196
+ } ,
197
+ ) ;
198
+
199
+ const transport = makeNodeTransport ( defaultOptions ) ;
200
+ await transport . send ( EVENT_ATTACHMENT_ENVELOPE ) ;
201
+ } ) ;
202
+ } ) ;
203
+
158
204
describe ( 'proxy' , ( ) => {
159
205
it ( 'can be configured through option' , ( ) => {
160
206
makeNodeTransport ( {
@@ -236,104 +282,106 @@ describe('makeNewHttpTransport()', () => {
236
282
} ) ;
237
283
} ) ;
238
284
239
- it ( 'should register TransportRequestExecutor that returns the correct object from server response (rate limit)' , async ( ) => {
240
- await setupTestServer ( {
241
- statusCode : RATE_LIMIT ,
242
- responseHeaders : { } ,
243
- } ) ;
244
-
245
- makeNodeTransport ( defaultOptions ) ;
246
- const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
247
-
248
- const executorResult = registeredRequestExecutor ( {
249
- body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
250
- category : 'error' ,
251
- } ) ;
252
-
253
- await expect ( executorResult ) . resolves . toEqual (
254
- expect . objectContaining ( {
285
+ describe ( 'should register TransportRequestExecutor that returns the correct object from server response' , ( ) => {
286
+ it ( 'rate limit' , async ( ) => {
287
+ await setupTestServer ( {
255
288
statusCode : RATE_LIMIT ,
256
- } ) ,
257
- ) ;
258
- } ) ;
289
+ responseHeaders : { } ,
290
+ } ) ;
259
291
260
- it ( 'should register TransportRequestExecutor that returns the correct object from server response (OK)' , async ( ) => {
261
- await setupTestServer ( {
262
- statusCode : SUCCESS ,
263
- } ) ;
292
+ makeNodeTransport ( defaultOptions ) ;
293
+ const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
264
294
265
- makeNodeTransport ( defaultOptions ) ;
266
- const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
295
+ const executorResult = registeredRequestExecutor ( {
296
+ body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
297
+ category : 'error' ,
298
+ } ) ;
267
299
268
- const executorResult = registeredRequestExecutor ( {
269
- body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
270
- category : 'error' ,
300
+ await expect ( executorResult ) . resolves . toEqual (
301
+ expect . objectContaining ( {
302
+ statusCode : RATE_LIMIT ,
303
+ } ) ,
304
+ ) ;
271
305
} ) ;
272
306
273
- await expect ( executorResult ) . resolves . toEqual (
274
- expect . objectContaining ( {
307
+ it ( 'OK' , async ( ) => {
308
+ await setupTestServer ( {
275
309
statusCode : SUCCESS ,
276
- headers : {
277
- 'retry-after' : null ,
278
- 'x-sentry-rate-limits' : null ,
279
- } ,
280
- } ) ,
281
- ) ;
282
- } ) ;
310
+ } ) ;
283
311
284
- it ( 'should register TransportRequestExecutor that returns the correct object from server response (OK with rate-limit headers)' , async ( ) => {
285
- await setupTestServer ( {
286
- statusCode : SUCCESS ,
287
- responseHeaders : {
288
- 'Retry-After' : '2700' ,
289
- 'X-Sentry-Rate-Limits' : '60::organization, 2700::organization' ,
290
- } ,
291
- } ) ;
312
+ makeNodeTransport ( defaultOptions ) ;
313
+ const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
292
314
293
- makeNodeTransport ( defaultOptions ) ;
294
- const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
315
+ const executorResult = registeredRequestExecutor ( {
316
+ body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
317
+ category : 'error' ,
318
+ } ) ;
295
319
296
- const executorResult = registeredRequestExecutor ( {
297
- body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
298
- category : 'error' ,
320
+ await expect ( executorResult ) . resolves . toEqual (
321
+ expect . objectContaining ( {
322
+ statusCode : SUCCESS ,
323
+ headers : {
324
+ 'retry-after' : null ,
325
+ 'x-sentry-rate-limits' : null ,
326
+ } ,
327
+ } ) ,
328
+ ) ;
299
329
} ) ;
300
330
301
- await expect ( executorResult ) . resolves . toEqual (
302
- expect . objectContaining ( {
331
+ it ( 'OK with rate-limit headers' , async ( ) => {
332
+ await setupTestServer ( {
303
333
statusCode : SUCCESS ,
304
- headers : {
305
- 'retry-after ' : '2700' ,
306
- 'x-sentry-rate-limits ' : '60::organization, 2700::organization' ,
334
+ responseHeaders : {
335
+ 'Retry-After ' : '2700' ,
336
+ 'X-Sentry-Rate-Limits ' : '60::organization, 2700::organization' ,
307
337
} ,
308
- } ) ,
309
- ) ;
310
- } ) ;
338
+ } ) ;
311
339
312
- it ( 'should register TransportRequestExecutor that returns the correct object from server response (NOK with rate-limit headers)' , async ( ) => {
313
- await setupTestServer ( {
314
- statusCode : RATE_LIMIT ,
315
- responseHeaders : {
316
- 'Retry-After' : '2700' ,
317
- 'X-Sentry-Rate-Limits' : '60::organization, 2700::organization' ,
318
- } ,
319
- } ) ;
340
+ makeNodeTransport ( defaultOptions ) ;
341
+ const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
320
342
321
- makeNodeTransport ( defaultOptions ) ;
322
- const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
343
+ const executorResult = registeredRequestExecutor ( {
344
+ body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
345
+ category : 'error' ,
346
+ } ) ;
323
347
324
- const executorResult = registeredRequestExecutor ( {
325
- body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
326
- category : 'error' ,
348
+ await expect ( executorResult ) . resolves . toEqual (
349
+ expect . objectContaining ( {
350
+ statusCode : SUCCESS ,
351
+ headers : {
352
+ 'retry-after' : '2700' ,
353
+ 'x-sentry-rate-limits' : '60::organization, 2700::organization' ,
354
+ } ,
355
+ } ) ,
356
+ ) ;
327
357
} ) ;
328
358
329
- await expect ( executorResult ) . resolves . toEqual (
330
- expect . objectContaining ( {
359
+ it ( 'NOK with rate-limit headers' , async ( ) => {
360
+ await setupTestServer ( {
331
361
statusCode : RATE_LIMIT ,
332
- headers : {
333
- 'retry-after ' : '2700' ,
334
- 'x-sentry-rate-limits ' : '60::organization, 2700::organization' ,
362
+ responseHeaders : {
363
+ 'Retry-After ' : '2700' ,
364
+ 'X-Sentry-Rate-Limits ' : '60::organization, 2700::organization' ,
335
365
} ,
336
- } ) ,
337
- ) ;
366
+ } ) ;
367
+
368
+ makeNodeTransport ( defaultOptions ) ;
369
+ const registeredRequestExecutor = ( createTransport as jest . Mock ) . mock . calls [ 0 ] [ 1 ] ;
370
+
371
+ const executorResult = registeredRequestExecutor ( {
372
+ body : serializeEnvelope ( EVENT_ENVELOPE , new TextEncoder ( ) ) ,
373
+ category : 'error' ,
374
+ } ) ;
375
+
376
+ await expect ( executorResult ) . resolves . toEqual (
377
+ expect . objectContaining ( {
378
+ statusCode : RATE_LIMIT ,
379
+ headers : {
380
+ 'retry-after' : '2700' ,
381
+ 'x-sentry-rate-limits' : '60::organization, 2700::organization' ,
382
+ } ,
383
+ } ) ,
384
+ ) ;
385
+ } ) ;
338
386
} ) ;
339
387
} ) ;
0 commit comments