1
1
const stringify = require ( "./vendor/json-stringify-safe/stringify" ) ;
2
+ const pako = require ( 'pako' ) ;
3
+
4
+ // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
5
+ const _window =
6
+ typeof window !== 'undefined'
7
+ ? window
8
+ : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : { } ;
9
+
10
+ /**
11
+ * hasKey, a better form of hasOwnProperty
12
+ * Example: hasKey(MainHostObject, property) === true/false
13
+ *
14
+ * @param {Object } host object to check property
15
+ * @param {string } key to check
16
+ */
17
+ function hasKey ( object , key ) {
18
+ return Object . prototype . hasOwnProperty . call ( object , key ) ;
19
+ }
20
+
21
+ function isUndefined ( what ) {
22
+ return what === void 0 ;
23
+ }
24
+
25
+ function each ( obj , callback ) {
26
+ var i , j ;
27
+
28
+ if ( isUndefined ( obj . length ) ) {
29
+ for ( i in obj ) {
30
+ if ( hasKey ( obj , i ) ) {
31
+ callback . call ( null , i , obj [ i ] ) ;
32
+ }
33
+ }
34
+ } else {
35
+ j = obj . length ;
36
+ if ( j ) {
37
+ for ( i = 0 ; i < j ; i ++ ) {
38
+ callback . call ( null , i , obj [ i ] ) ;
39
+ }
40
+ }
41
+ }
42
+ }
43
+
44
+ function supportsFetch ( ) {
45
+ if ( ! ( 'fetch' in _window ) ) return false ;
46
+
47
+ try {
48
+ new Headers ( ) ; // eslint-disable-line no-new
49
+ new Request ( '' ) ; // eslint-disable-line no-new
50
+ new Response ( ) ; // eslint-disable-line no-new
51
+ return true ;
52
+ } catch ( e ) {
53
+ return false ;
54
+ }
55
+ }
56
+
57
+ function urlencode ( o ) {
58
+ var pairs = [ ] ;
59
+ each ( o , function ( key , value ) {
60
+ pairs . push ( encodeURIComponent ( key ) + '=' + encodeURIComponent ( value ) ) ;
61
+ } ) ;
62
+ return pairs . join ( '&' ) ;
63
+ }
64
+
65
+ function objectMerge ( obj1 , obj2 ) {
66
+ if ( ! obj2 ) {
67
+ return obj1 ;
68
+ }
69
+ each ( obj2 , function ( key , value ) {
70
+ obj1 [ key ] = value ;
71
+ } ) ;
72
+ return obj1 ;
73
+ }
74
+
75
+
76
+ // Unfortunately, this doesn't work at the moment, because Sentry doesn't allow us to
77
+ // send a Content-Encoding header. The CORS preflight request returns:
78
+ //
79
+ // Access-Control-Allow-Headers: X-Sentry-Auth, X-Requested-With,
80
+ // Origin, Accept, Content-Type, Authentication
81
+ //
82
+ // (Content-Encoding is missing.)
83
+ //
84
+ // Also it might be dangerous for them to support gzip/deflate, because people could
85
+ // send gzip bombs (a tiny amount of compressed data that expands to gigabytes on disk)
86
+ // But maybe there's a decompression library that can be configured to abort after a max size.
87
+
88
+ const makeRequestWithZlib = function ( opts , shouldSendRequest ) {
89
+ // Auth is intentionally sent as part of query string (NOT as custom HTTP header) to avoid preflight CORS requests
90
+ var url = opts . url + '?' + urlencode ( opts . auth ) ;
91
+
92
+ var evaluatedHeaders = null ;
93
+ var evaluatedFetchParameters = { } ;
94
+
95
+ if ( opts . options . headers ) {
96
+ evaluatedHeaders = this . _evaluateHash ( opts . options . headers ) ;
97
+ }
98
+
99
+ if ( opts . options . fetchParameters ) {
100
+ evaluatedFetchParameters = this . _evaluateHash ( opts . options . fetchParameters ) ;
101
+ }
102
+
103
+ const requestJSON = stringify ( opts . data ) ;
104
+ const requestDeflate = pako . deflate ( requestJSON , { to : 'string' } ) ;
105
+
106
+ if ( shouldSendRequest && ! shouldSendRequest ( requestDeflate ) ) {
107
+ return ;
108
+ }
109
+
110
+ if ( supportsFetch ( ) ) {
111
+ var defaultFetchOptions = objectMerge ( { } , this . _fetchDefaults ) ;
112
+ var fetchOptions = objectMerge ( defaultFetchOptions , evaluatedFetchParameters ) ;
113
+
114
+ if ( evaluatedHeaders ) {
115
+ fetchOptions . headers = evaluatedHeaders ;
116
+ }
117
+
118
+ evaluatedFetchParameters . body = requestDeflate
119
+
120
+ fetchOptions . headers = Object . assign ( fetchOptions . headers || { } , {
121
+ 'Content-Type' : 'application/json; charset=utf-8' ,
122
+ 'Content-Encoding' : 'deflate' ,
123
+ } )
124
+
125
+ return _window
126
+ . fetch ( url , fetchOptions )
127
+ . then ( function ( response ) {
128
+ if ( response . ok ) {
129
+ opts . onSuccess && opts . onSuccess ( ) ;
130
+ } else {
131
+ var error = new Error ( 'Sentry error code: ' + response . status ) ;
132
+ // It's called request only to keep compatibility with XHR interface
133
+ // and not add more redundant checks in setBackoffState method
134
+ error . request = response ;
135
+ opts . onError && opts . onError ( error ) ;
136
+ }
137
+ } )
138
+ [ 'catch' ] ( function ( ) {
139
+ opts . onError &&
140
+ opts . onError ( new Error ( 'Sentry error code: network unavailable' ) ) ;
141
+ } ) ;
142
+ }
143
+
144
+ var request = _window . XMLHttpRequest && new _window . XMLHttpRequest ( ) ;
145
+ if ( ! request ) return ;
146
+
147
+ // if browser doesn't support CORS (e.g. IE7), we are out of luck
148
+ var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined' ;
149
+
150
+ if ( ! hasCORS ) return ;
151
+
152
+ if ( 'withCredentials' in request ) {
153
+ request . onreadystatechange = function ( ) {
154
+ if ( request . readyState !== 4 ) {
155
+ return ;
156
+ } else if ( request . status === 200 ) {
157
+ opts . onSuccess && opts . onSuccess ( ) ;
158
+ } else if ( opts . onError ) {
159
+ var err = new Error ( 'Sentry error code: ' + request . status ) ;
160
+ err . request = request ;
161
+ opts . onError ( err ) ;
162
+ }
163
+ } ;
164
+ } else {
165
+ request = new XDomainRequest ( ) ;
166
+ // xdomainrequest cannot go http -> https (or vice versa),
167
+ // so always use protocol relative
168
+ url = url . replace ( / ^ h t t p s ? : / , '' ) ;
169
+
170
+ // onreadystatechange not supported by XDomainRequest
171
+ if ( opts . onSuccess ) {
172
+ request . onload = opts . onSuccess ;
173
+ }
174
+ if ( opts . onError ) {
175
+ request . onerror = function ( ) {
176
+ var err = new Error ( 'Sentry error code: XDomainRequest' ) ;
177
+ err . request = request ;
178
+ opts . onError ( err ) ;
179
+ } ;
180
+ }
181
+ }
182
+
183
+ request . open ( 'POST' , url ) ;
184
+
185
+ if ( evaluatedHeaders ) {
186
+ each ( evaluatedHeaders , function ( key , value ) {
187
+ request . setRequestHeader ( key , value ) ;
188
+ } ) ;
189
+ }
190
+
191
+ request . setRequestHeader ( 'Content-Type' , 'application/json; charset=utf-8' ) ;
192
+ request . setRequestHeader ( 'Content-Encoding' , 'deflate' ) ;
193
+
194
+ request . send ( requestDeflate ) ;
195
+ }
196
+
2
197
3
198
const identity = x => x ;
4
199
const getUndefined = ( ) => { } ;
@@ -57,18 +252,6 @@ function createRavenMiddleware(Raven, options = {}) {
57
252
const originalTransport = Raven . _globalOptions . transport ;
58
253
Raven . setTransport ( opts => {
59
254
Raven . setTransport ( originalTransport ) ;
60
- const requestBody = stringify ( opts . data ) ;
61
- if ( requestBody . length > 200000 ) {
62
- // We know the request is too large, so don't try sending it to Sentry.
63
- // Retry the capture function, and don't include the state this time.
64
- const errorMessage =
65
- "Could not send state because request would be larger than 200KB. " +
66
- `(Was: ${ requestBody . length } B)` ;
67
- retryCaptureWithoutReduxState ( errorMessage , ( ) => {
68
- originalFn . apply ( Raven , captureArguments ) ;
69
- } ) ;
70
- return ;
71
- }
72
255
opts . onError = error => {
73
256
if ( error . request && error . request . status === 413 ) {
74
257
const errorMessage =
@@ -78,7 +261,21 @@ function createRavenMiddleware(Raven, options = {}) {
78
261
} ) ;
79
262
}
80
263
} ;
81
- ( originalTransport || Raven . _makeRequest ) . call ( Raven , opts ) ;
264
+
265
+ makeRequestWithZlib ( opts , ( requestBody ) => {
266
+ if ( requestBody . length > 200000 ) {
267
+ // We know the request is too large, so don't try sending it to Sentry.
268
+ // Retry the capture function, and don't include the state this time.
269
+ const errorMessage =
270
+ "Could not send state because request would be larger than 200KB. " +
271
+ `(Was: ${ requestBody . length } B)` ;
272
+ retryCaptureWithoutReduxState ( errorMessage , ( ) => {
273
+ originalFn . apply ( Raven , captureArguments ) ;
274
+ } ) ;
275
+ return false ;
276
+ }
277
+ return true ;
278
+ } ) ;
82
279
} ) ;
83
280
originalFn . apply ( Raven , captureArguments ) ;
84
281
} ;
@@ -91,6 +288,9 @@ function createRavenMiddleware(Raven, options = {}) {
91
288
Raven . captureMessage
92
289
) ;
93
290
291
+ // Set the default transport to use zlib compression
292
+ Raven . setTransport ( makeRequestWithZlib . bind ( Raven ) ) ;
293
+
94
294
return next => action => {
95
295
// Log the action taken to Raven so that we have narrative context in our
96
296
// error report.
0 commit comments