299 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			299 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  * Copyright (c) 2015-present, Facebook, Inc.
 | |
|  *
 | |
|  * This source code is licensed under the MIT license found in the
 | |
|  * LICENSE file in the root directory of this source tree.
 | |
|  *
 | |
|  * @providesModule NetworkAgent
 | |
|  * @flow
 | |
|  */
 | |
| 'use strict';
 | |
| 
 | |
| const InspectorAgent = require('InspectorAgent');
 | |
| const JSInspector = require('JSInspector');
 | |
| const Map = require('Map');
 | |
| const XMLHttpRequest = require('XMLHttpRequest');
 | |
| 
 | |
| import type EventSender from 'InspectorAgent';
 | |
| 
 | |
| type RequestId = string;
 | |
| 
 | |
| type LoaderId = string;
 | |
| type FrameId = string;
 | |
| type Timestamp = number;
 | |
| 
 | |
| type Headers = Object;
 | |
| 
 | |
| // We don't currently care about this
 | |
| type ResourceTiming = null;
 | |
| 
 | |
| type ResourceType =
 | |
|   'Document' |
 | |
|   'Stylesheet' |
 | |
|   'Image' |
 | |
|   'Media' |
 | |
|   'Font' |
 | |
|   'Script' |
 | |
|   'TextTrack' |
 | |
|   'XHR' |
 | |
|   'Fetch' |
 | |
|   'EventSource' |
 | |
|   'WebSocket' |
 | |
|   'Manifest' |
 | |
|   'Other';
 | |
| 
 | |
| type SecurityState =
 | |
|   'unknown' |
 | |
|   'neutral' |
 | |
|   'insecure' |
 | |
|   'warning' |
 | |
|   'secure' |
 | |
|   'info';
 | |
| type BlockedReason =
 | |
|   'csp' |
 | |
|   'mixed-content' |
 | |
|   'origin' |
 | |
|   'inspector' |
 | |
|   'subresource-filter' |
 | |
|   'other';
 | |
| 
 | |
| type StackTrace = null;
 | |
| 
 | |
| type Initiator = {
 | |
|   type: 'script' | 'other',
 | |
|   stackTrace?: StackTrace,
 | |
|   url?: string,
 | |
|   lineNumber?: number
 | |
| }
 | |
| 
 | |
| type ResourcePriority = 'VeryLow' | 'Low' | 'Medium' | 'High' | 'VeryHigh';
 | |
| 
 | |
| type Request = {
 | |
|   url: string,
 | |
|   method: string,
 | |
|   headers: Headers,
 | |
|   postData?: string,
 | |
|   mixedContentType?: 'blockable' | 'optionally-blockable' | 'none',
 | |
|   initialPriority: ResourcePriority,
 | |
| };
 | |
| 
 | |
| type Response = {
 | |
|   url: string,
 | |
|   status: number,
 | |
|   statusText: string,
 | |
|   headers: Headers,
 | |
|   headersText?: string,
 | |
|   mimeType: string,
 | |
|   requestHeaders?: Headers,
 | |
|   requestHeadersText?: string,
 | |
|   connectionReused: boolean,
 | |
|   connectionId: number,
 | |
|   fromDiskCache?: boolean,
 | |
|   encodedDataLength: number,
 | |
|   timing?: ResourceTiming,
 | |
|   securityState: SecurityState,
 | |
| };
 | |
| 
 | |
| type RequestWillBeSentEvent = {
 | |
|   requestId: RequestId,
 | |
|   frameId: FrameId,
 | |
|   loaderId: LoaderId,
 | |
|   documentURL: string,
 | |
|   request: Request,
 | |
|   timestamp: Timestamp,
 | |
|   initiator: Initiator,
 | |
|   redirectResponse?: Response,
 | |
|   // This is supposed to be optional but the inspector crashes without it,
 | |
|   // see https://bugs.chromium.org/p/chromium/issues/detail?id=653138
 | |
|   type: ResourceType,
 | |
| };
 | |
| 
 | |
| type ResponseReceivedEvent = {
 | |
|   requestId: RequestId,
 | |
|   frameId: FrameId,
 | |
|   loaderId: LoaderId,
 | |
|   timestamp: Timestamp,
 | |
|   type: ResourceType,
 | |
|   response: Response,
 | |
| };
 | |
| 
 | |
| type DataReceived = {
 | |
|   requestId: RequestId,
 | |
|   timestamp: Timestamp,
 | |
|   dataLength: number,
 | |
|   encodedDataLength: number,
 | |
| };
 | |
| 
 | |
| type LoadingFinishedEvent = {
 | |
|   requestId: RequestId,
 | |
|   timestamp: Timestamp,
 | |
|   encodedDataLength: number,
 | |
| };
 | |
| 
 | |
| type LoadingFailedEvent = {
 | |
|   requestId: RequestId,
 | |
|   timestamp: Timestamp,
 | |
|   type: ResourceType,
 | |
|   errorText: string,
 | |
|   canceled?: boolean,
 | |
|   blockedReason?: BlockedReason,
 | |
| };
 | |
| 
 | |
| class Interceptor {
 | |
|   _agent: NetworkAgent;
 | |
|   _requests: Map<string, string>;
 | |
| 
 | |
|   constructor(agent: NetworkAgent) {
 | |
|     this._agent = agent;
 | |
|     this._requests = new Map();
 | |
|   }
 | |
| 
 | |
|   getData(requestId: string): ?string {
 | |
|     return this._requests.get(requestId);
 | |
|   }
 | |
| 
 | |
|   requestSent(
 | |
|     id: number,
 | |
|     url: string,
 | |
|     method: string,
 | |
|     headers: Object) {
 | |
|     const requestId = String(id);
 | |
|     this._requests.set(requestId, '');
 | |
| 
 | |
|     const request: Request = {
 | |
|       url,
 | |
|       method,
 | |
|       headers,
 | |
|       initialPriority: 'Medium',
 | |
|     };
 | |
|     const event: RequestWillBeSentEvent = {
 | |
|       requestId,
 | |
|       documentURL: '',
 | |
|       frameId: '1',
 | |
|       loaderId: '1',
 | |
|       request,
 | |
|       timestamp: JSInspector.getTimestamp(),
 | |
|       initiator: {
 | |
|         // TODO(blom): Get stack trace
 | |
|         // If type is 'script' the inspector will try to execute
 | |
|         // `stack.callFrames[0]`
 | |
|         type: 'other',
 | |
|       },
 | |
|       type: 'Other',
 | |
|     };
 | |
|     this._agent.sendEvent('requestWillBeSent', event);
 | |
|   }
 | |
| 
 | |
|   responseReceived(
 | |
|     id: number,
 | |
|     url: string,
 | |
|     status: number,
 | |
|     headers: Object) {
 | |
|     const requestId = String(id);
 | |
|     const response: Response = {
 | |
|       url,
 | |
|       status,
 | |
|       statusText: String(status),
 | |
|       headers,
 | |
|       // TODO(blom) refined headers, can we get this?
 | |
|       requestHeaders: {},
 | |
|       mimeType: this._getMimeType(headers),
 | |
|       connectionReused: false,
 | |
|       connectionId: -1,
 | |
|       encodedDataLength: 0,
 | |
|       securityState: 'unknown',
 | |
|     };
 | |
| 
 | |
|     const event: ResponseReceivedEvent = {
 | |
|       requestId,
 | |
|       frameId: '1',
 | |
|       loaderId: '1',
 | |
|       timestamp: JSInspector.getTimestamp(),
 | |
|       type: 'Other',
 | |
|       response,
 | |
|     };
 | |
|     this._agent.sendEvent('responseReceived', event);
 | |
|   }
 | |
| 
 | |
|   dataReceived(
 | |
|     id: number,
 | |
|     data: string) {
 | |
|     const requestId = String(id);
 | |
|     const existingData = this._requests.get(requestId) || '';
 | |
|     this._requests.set(requestId, existingData.concat(data));
 | |
|     const event: DataReceived = {
 | |
|       requestId,
 | |
|       timestamp: JSInspector.getTimestamp(),
 | |
|       dataLength: data.length,
 | |
|       encodedDataLength: data.length,
 | |
|     };
 | |
|     this._agent.sendEvent('dataReceived', event);
 | |
|   }
 | |
| 
 | |
|   loadingFinished(
 | |
|     id: number,
 | |
|     encodedDataLength: number) {
 | |
|     const event: LoadingFinishedEvent = {
 | |
|       requestId: String(id),
 | |
|       timestamp: JSInspector.getTimestamp(),
 | |
|       encodedDataLength: encodedDataLength,
 | |
|     };
 | |
|     this._agent.sendEvent('loadingFinished', event);
 | |
|   }
 | |
| 
 | |
|   loadingFailed(
 | |
|       id: number,
 | |
|       error: string) {
 | |
|     const event: LoadingFailedEvent = {
 | |
|       requestId: String(id),
 | |
|       timestamp: JSInspector.getTimestamp(),
 | |
|       type: 'Other',
 | |
|       errorText: error,
 | |
|     };
 | |
|     this._agent.sendEvent('loadingFailed', event);
 | |
|   }
 | |
| 
 | |
|   _getMimeType(headers: Object): string {
 | |
|     const contentType = headers['Content-Type'] || '';
 | |
|     return contentType.split(';')[0];
 | |
|   }
 | |
| }
 | |
| 
 | |
| type EnableArgs = {
 | |
|   maxResourceBufferSize?: number,
 | |
|   maxTotalBufferSize?: number
 | |
| };
 | |
| 
 | |
| class NetworkAgent extends InspectorAgent {
 | |
|   static DOMAIN = 'Network';
 | |
| 
 | |
|   _sendEvent: EventSender;
 | |
|   _interceptor: ?Interceptor;
 | |
| 
 | |
|   enable({ maxResourceBufferSize, maxTotalBufferSize }: EnableArgs) {
 | |
|     this._interceptor = new Interceptor(this);
 | |
|     XMLHttpRequest.setInterceptor(this._interceptor);
 | |
|   }
 | |
| 
 | |
|   disable() {
 | |
|     XMLHttpRequest.setInterceptor(null);
 | |
|     this._interceptor = null;
 | |
|   }
 | |
| 
 | |
|   getResponseBody({requestId}: {requestId: RequestId})
 | |
|       : {body: ?string, base64Encoded: boolean} {
 | |
|     return {body: this.interceptor().getData(requestId), base64Encoded: false};
 | |
|   }
 | |
| 
 | |
|   interceptor(): Interceptor {
 | |
|     if (this._interceptor) {
 | |
|       return this._interceptor;
 | |
|     } else {
 | |
|       throw Error('_interceptor can not be null');
 | |
|     }
 | |
| 
 | |
|   }
 | |
| }
 | |
| 
 | |
| module.exports = NetworkAgent;
 | 
