@@ -4,6 +4,7 @@ import { HTTPStatusCodes } from '../config';
44import MockItem from '../mocker/mock-item' ;
55import Mocker from '../mocker/mocker' ;
66import { HttpVerb , RemoteResponse , XMLHttpRequestInstance } from '../types' ;
7+ import { OriginalResponse } from './../types' ;
78import Base from './base' ;
89
910export default class XMLHttpRequestInterceptor extends Base {
@@ -74,6 +75,12 @@ export default class XMLHttpRequestInterceptor extends Base {
7475 this . mockResponse = new NotResolved ( ) ;
7576 this . requestInfo = me . getRequestInfo ( { url : requestUrl , method, } ) ;
7677 this . requestArgs = [ method , requestUrl , async , user , password ] ;
78+
79+ this . requestInfo . doOriginalCall = async ( ) : Promise < OriginalResponse > => {
80+ const res = await me . getOriginalResponse ( this ) ;
81+ this . requestInfo . doOriginalCall = undefined ;
82+ return res ;
83+ } ;
7784 return ;
7885 }
7986 }
@@ -128,17 +135,20 @@ export default class XMLHttpRequestInterceptor extends Base {
128135 * @param {Record<string, string> } remoteInfo
129136 */
130137 private sendRemoteResult ( xhr : XMLHttpRequestInstance , mockItem : MockItem , remoteInfo : Record < string , string > ) {
131- const [ method , , async , user , password ] = xhr . requestArgs ;
138+ const [ method , async , user , password ] = xhr . requestArgs ;
132139
133140 const newXhr = new XMLHttpRequest ( ) ;
141+ newXhr . responseType = xhr . responseType ;
142+ newXhr . timeout = xhr . timeout ;
143+
134144 Object . assign ( newXhr , { isMockRequest : false , bypassMock : true } ) ;
135145 newXhr . onreadystatechange = ( ) => {
136146 if ( newXhr . readyState === 4 ) {
137147 const remoteResponse : RemoteResponse = {
138148 status : newXhr . status ,
139149 headers : newXhr . getAllResponseHeaders ( ) . split ( '\r\n' ) . reduce ( ( res : Record < string , string > , item : string ) => {
140150 const [ key , val ] = item . split ( ':' ) ;
141- if ( key ) {
151+ if ( key && val ) {
142152 res [ key . toLowerCase ( ) ] = val . trim ( ) ;
143153 }
144154 return res ;
@@ -164,11 +174,83 @@ export default class XMLHttpRequestInterceptor extends Base {
164174 return xhr ;
165175 }
166176
177+ private getOriginalResponse ( xhr : XMLHttpRequestInstance ) : Promise < OriginalResponse > {
178+ const [ method , requestUrl , async , user , password ] = xhr . requestArgs ;
179+ const { requestInfo } = xhr ;
180+
181+ return new Promise ( resolve => {
182+ const newXhr = new XMLHttpRequest ( ) ;
183+ newXhr . responseType = xhr . responseType ;
184+ newXhr . timeout = xhr . timeout ;
185+
186+ Object . assign ( newXhr , { isMockRequest : false , bypassMock : true } ) ;
187+ let status : OriginalResponse [ 'status' ] = null ;
188+ let headers : OriginalResponse [ 'headers' ] = { } ;
189+ let responseText : OriginalResponse [ 'responseText' ] = null ;
190+ let responseJson : OriginalResponse [ 'responseJson' ] = null ;
191+ let responseBuffer : OriginalResponse [ 'responseBuffer' ] = null ;
192+ let responseBlob : OriginalResponse [ 'responseBlob' ] = null ;
193+
194+ newXhr . onreadystatechange = function handleLoad ( ) {
195+ if ( newXhr . readyState === 4 ) {
196+ const responseType = newXhr . responseType ;
197+ status = newXhr . status ;
198+ headers = newXhr . getAllResponseHeaders ( )
199+ . split ( '\r\n' )
200+ . reduce ( ( res : Record < string , string > , item : string ) => {
201+ const [ key , val ] = item . split ( ':' ) ;
202+ if ( key && val ) {
203+ res [ key . toLowerCase ( ) ] = val . trim ( ) ;
204+ }
205+ return res ;
206+ } , { } as Record < string , string > ) ;
207+
208+ responseText = ! responseType || responseType === 'text' || responseType === 'json'
209+ ? newXhr . responseText
210+ : ( typeof newXhr . response === 'string' ? typeof newXhr . response : null ) ;
211+
212+ responseJson = tryToParseJson ( responseText as string ) ;
213+ responseBuffer = ( typeof ArrayBuffer === 'function' ) && ( newXhr . response instanceof ArrayBuffer )
214+ ? newXhr . response
215+ : null ;
216+ responseBlob = ( typeof Blob === 'function' ) && ( newXhr . response instanceof Blob )
217+ ? newXhr . response
218+ : null ;
219+
220+ resolve ( { status, headers, responseText, responseJson, responseBuffer, responseBlob, error : null } ) ;
221+ }
222+ } ;
223+ newXhr . open ( method as string , requestUrl as string , async as boolean , user as string , password as string ) ;
224+ newXhr . ontimeout = function handleTimeout ( ) {
225+ const error = new Error ( 'timeout exceeded' ) ;
226+ resolve ( { status, headers, responseText, responseJson, responseBuffer, responseBlob, error } ) ;
227+ } ;
228+
229+ // Real errors are hidden from us by the browser
230+ // onerror should only fire if it's a network error
231+ newXhr . onerror = function handleError ( ) {
232+ const error = new Error ( 'network error' ) ;
233+ resolve ( { status, headers, responseText, responseJson, responseBuffer, responseBlob, error } ) ;
234+ } ;
235+
236+ // Handle browser request cancellation (as opposed to a manual cancellation)
237+ newXhr . onabort = function handleAbort ( ) {
238+ const error = new Error ( 'request aborted' ) ;
239+ resolve ( { status, headers, responseText, responseJson, responseBuffer, responseBlob, error } ) ;
240+ } ;
241+
242+
243+ Object . entries ( requestInfo . headers || { } ) . forEach ( ( [ key , val ] : [ string , string ] ) => {
244+ newXhr . setRequestHeader ( key , val ) ;
245+ } ) ;
246+ newXhr . send ( requestInfo . rawBody as Document ) ; // raw body
247+ } ) ;
248+ }
249+
167250 /**
168251 * Make mock request.
169252 * @param {XMLHttpRequestInstance } xhr
170- * @param {MockItemInfo } mockItem
171- * @param {RequestInfo } requestInfo
253+ * @param {RemoteResponse | null } remoteResponse
172254 */
173255 private async doMockRequest ( xhr : XMLHttpRequestInstance , remoteResponse : RemoteResponse | null = null ) {
174256 let isBypassed = false ;
@@ -185,8 +267,7 @@ export default class XMLHttpRequestInterceptor extends Base {
185267 /**
186268 * Make mock response.
187269 * @param {XMLHttpRequestInstance } xhr
188- * @param {MockItemInfo } mockItem
189- * @param {RequestInfo } requestInfo
270+ * @param {RemoteResponse | null } remoteResponse
190271 */
191272 private async doMockResponse ( xhr : XMLHttpRequestInstance , remoteResponse : RemoteResponse | null = null ) {
192273 const { mockItem, requestInfo } = xhr ;
@@ -326,7 +407,9 @@ export default class XMLHttpRequestInterceptor extends Base {
326407 return ( header : string , value : string ) => {
327408 if ( this . isMockRequest ) {
328409 this . requestInfo . headers = this . requestInfo . headers || { } ;
410+ this . requestInfo . header = this . requestInfo . header || { } ;
329411 this . requestInfo . headers [ header ] = value ;
412+ this . requestInfo . header [ header ] = value ;
330413 return ;
331414 }
332415 return original . call ( this , header , value ) ;
0 commit comments