This app provides monitoring and information features for the common freifunk user and the technical stuff of a freifunk community.
Code base is taken from a TUM Practical Course project and added here to see if Freifunk Altdorf can use it.
https://www.freifunk-altdorf.de
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
6.4 KiB
298 lines
6.4 KiB
/** |
|
* 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;
|
|
|