Files
eruda/src/Network/Network.js
2018-08-04 10:43:57 +08:00

238 lines
5.2 KiB
JavaScript

import Tool from '../DevTools/Tool'
import XhrRequest from './XhrRequest'
import FetchRequest from './FetchRequest'
import Settings from '../Settings/Settings'
import {
evalCss,
isNative,
defaults,
now,
extend,
isEmpty,
$,
ms
} from '../lib/util'
export default class Network extends Tool {
constructor() {
super()
this._style = evalCss(require('./Network.scss'))
this.name = 'network'
this._requests = {}
this._tpl = require('./Network.hbs')
this._isFetchSupported = false
if (window.fetch) this._isFetchSupported = isNative(window.fetch)
}
init($el, container) {
super.init($el)
this._container = container
this._bindEvent()
this._initCfg()
this.overrideXhr()
}
show() {
super.show()
this._render()
}
clear() {
this._requests = {}
this._render()
}
overrideXhr() {
let winXhrProto = window.XMLHttpRequest.prototype
let origSend = (this._origSend = winXhrProto.send),
origOpen = (this._origOpen = winXhrProto.open),
origSetRequestHeader = (this._origSetRequestHeader =
winXhrProto.setRequestHeader)
let self = this
winXhrProto.open = function(method, url) {
let xhr = this
let req = (xhr.erudaRequest = new XhrRequest(xhr, method, url))
req.on('send', (id, data) => self._addReq(id, data))
req.on('update', (id, data) => self._updateReq(id, data))
xhr.addEventListener('readystatechange', function() {
switch (xhr.readyState) {
case 2:
return req.handleHeadersReceived()
case 4:
return req.handleDone()
}
})
origOpen.apply(this, arguments)
}
winXhrProto.send = function(data) {
let req = this.erudaRequest
if (req) req.handleSend(data)
origSend.apply(this, arguments)
}
winXhrProto.setRequestHeader = function() {
let req = this.erudaRequest
if (!req._headers) {
req._headers = {}
}
let key = arguments[0]
let val = arguments[1]
if (key && val) {
req._headers[key] = val
}
origSetRequestHeader.apply(this, arguments)
}
}
restoreXhr() {
let winXhrProto = window.XMLHttpRequest.prototype
if (this._origOpen) winXhrProto.open = this._origOpen
if (this._origSend) winXhrProto.send = this._origSend
}
overrideFetch() {
if (!this._isFetchSupported) return
let origFetch = (this._origFetch = window.fetch)
let self = this
window.fetch = function(...args) {
let req = new FetchRequest(...args)
req.on('send', (id, data) => self._addReq(id, data))
req.on('update', (id, data) => self._updateReq(id, data))
let fetchResult = origFetch(...args)
req.send(fetchResult)
return fetchResult
}
}
restoreFetch() {
if (!this._isFetchSupported) return
if (this._origFetch) window.fetch = this._origFetch
}
_addReq(id, data) {
defaults(data, {
name: '',
url: '',
status: 'pending',
type: 'unknown',
subType: 'unknown',
size: 0,
data: '',
method: 'GET',
startTime: now(),
time: 0,
resHeaders: {},
reqHeaders: {},
resTxt: '',
done: false
})
this._requests[id] = data
this._render()
}
_updateReq(id, data) {
let target = this._requests[id]
if (!target) return
extend(target, data)
target.time = target.time - target.startTime
target.displayTime = ms(target.time)
if (target.done && (target.status < 200 || target >= 300))
target.hasErr = true
this._render()
}
_bindEvent() {
let $el = this._$el,
container = this._container
let self = this
$el
.on('click', '.eruda-request', function() {
let id = $(this).data('id'),
data = self._requests[id]
if (!data.done) return
showSources('http', {
url: data.url,
data: data.data,
resTxt: data.resTxt,
type: data.type,
subType: data.subType,
resHeaders: data.resHeaders,
reqHeaders: data.reqHeaders
})
})
.on('click', '.eruda-clear-request', () => this.clear())
function showSources(type, data) {
let sources = container.get('sources')
if (!sources) return
sources.set(type, data)
container.showTool('sources')
}
}
destroy() {
super.destroy()
evalCss.remove(this._style)
this.restoreXhr()
this.restoreFetch()
}
_initCfg() {
let cfg = (this.config = Settings.createCfg('network', {
overrideFetch: true
}))
if (cfg.get('overrideFetch')) this.overrideFetch()
cfg.on('change', (key, val) => {
switch (key) {
case 'overrideFetch':
return val ? this.overrideFetch() : this.restoreFetch()
}
})
let settings = this._container.get('settings')
settings
.text('Network')
.switch(cfg, 'overrideFetch', 'Catch Fetch Requests')
.separator()
}
_render() {
if (!this.active) return
let renderData = {}
if (!isEmpty(this._requests)) renderData.requests = this._requests
this._renderHtml(this._tpl(renderData))
}
_renderHtml(html) {
if (html === this._lastHtml) return
this._lastHtml = html
this._$el.html(html)
}
}