aboutsummaryrefslogtreecommitdiffstats
path: root/web/src/js/components/FlowTable
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/js/components/FlowTable')
-rw-r--r--web/src/js/components/FlowTable/FlowColumns.jsx137
-rw-r--r--web/src/js/components/FlowTable/FlowRow.jsx28
-rw-r--r--web/src/js/components/FlowTable/FlowTableHead.jsx43
3 files changed, 208 insertions, 0 deletions
diff --git a/web/src/js/components/FlowTable/FlowColumns.jsx b/web/src/js/components/FlowTable/FlowColumns.jsx
new file mode 100644
index 00000000..11c0796c
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowColumns.jsx
@@ -0,0 +1,137 @@
+import React, { Component } from 'react'
+import classnames from 'classnames'
+import { RequestUtils, ResponseUtils } from '../../flow/utils.js'
+import { formatSize, formatTimeDelta } from '../../utils.js'
+
+export function TLSColumn({ flow }) {
+ return (
+ <td className={classnames('col-tls', flow.request.scheme === 'https' ? 'col-tls-https' : 'col-tls-http')}></td>
+ )
+}
+
+TLSColumn.sortKeyFun = flow => flow.request.scheme
+TLSColumn.headerClass = 'col-tls'
+TLSColumn.headerName = ''
+
+export function IconColumn({ flow }) {
+ return (
+ <td className="col-icon">
+ <div className={classnames('resource-icon', IconColumn.getIcon(flow))}></div>
+ </td>
+ )
+}
+
+IconColumn.headerClass = 'col-icon'
+IconColumn.headerName = ''
+
+IconColumn.getIcon = flow => {
+ if (!flow.response) {
+ return 'resource-icon-plain'
+ }
+
+ var contentType = ResponseUtils.getContentType(flow.response) || ''
+
+ // @todo We should assign a type to the flow somewhere else.
+ if (flow.response.status_code === 304) {
+ return 'resource-icon-not-modified'
+ }
+ if (300 <= flow.response.status_code && flow.response.status_code < 400) {
+ return 'resource-icon-redirect'
+ }
+ if (contentType.indexOf('image') >= 0) {
+ return 'resource-icon-image'
+ }
+ if (contentType.indexOf('javascript') >= 0) {
+ return 'resource-icon-js'
+ }
+ if (contentType.indexOf('css') >= 0) {
+ return 'resource-icon-css'
+ }
+ if (contentType.indexOf('html') >= 0) {
+ return 'resource-icon-document'
+ }
+
+ return 'resource-icon-plain'
+}
+
+export function PathColumn({ flow }) {
+ return (
+ <td className="col-path">
+ {flow.request.is_replay && (
+ <i className="fa fa-fw fa-repeat pull-right"></i>
+ )}
+ {flow.intercepted && (
+ <i className="fa fa-fw fa-pause pull-right"></i>
+ )}
+ {RequestUtils.pretty_url(flow.request)}
+ </td>
+ )
+}
+
+PathColumn.sortKeyFun = flow => RequestUtils.pretty_url(flow.request)
+PathColumn.headerClass = 'col-path'
+PathColumn.headerName = 'Path'
+
+export function MethodColumn({ flow }) {
+ return (
+ <td className="col-method">{flow.request.method}</td>
+ )
+}
+
+MethodColumn.sortKeyFun = flow => flow.request.method
+MethodColumn.headerClass = 'col-method'
+MethodColumn.headerName = 'Method'
+
+export function StatusColumn({ flow }) {
+ return (
+ <td className="col-status">{flow.response && flow.response.status_code}</td>
+ )
+}
+
+StatusColumn.sortKeyFun = flow => flow.response && flow.response.status_code
+StatusColumn.headerClass = 'col-status'
+StatusColumn.headerName = 'Status'
+
+export function SizeColumn({ flow }) {
+ return (
+ <td className="col-size">{formatSize(SizeColumn.getTotalSize(flow))}</td>
+ )
+}
+
+SizeColumn.sortKeyFun = flow => {
+ let total = flow.request.contentLength
+ if (flow.response) {
+ total += flow.response.contentLength || 0
+ }
+ return total
+}
+
+SizeColumn.getTotalSize = SizeColumn.sortKeyFun
+SizeColumn.headerClass = 'col-size'
+SizeColumn.headerName = 'Size'
+
+export function TimeColumn({ flow }) {
+ return (
+ <td className="col-time">
+ {flow.response ? (
+ formatTimeDelta(1000 * (flow.response.timestamp_end - flow.request.timestamp_start))
+ ) : (
+ '...'
+ )}
+ </td>
+ )
+}
+
+TimeColumn.sortKeyFun = flow => flow.response && flow.response.timestamp_end - flow.request.timestamp_start
+TimeColumn.headerClass = 'col-time'
+TimeColumn.headerName = 'Time'
+
+export default [
+ TLSColumn,
+ IconColumn,
+ PathColumn,
+ MethodColumn,
+ StatusColumn,
+ SizeColumn,
+ TimeColumn,
+]
diff --git a/web/src/js/components/FlowTable/FlowRow.jsx b/web/src/js/components/FlowTable/FlowRow.jsx
new file mode 100644
index 00000000..749bc0ce
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowRow.jsx
@@ -0,0 +1,28 @@
+import React, { PropTypes } from 'react'
+import classnames from 'classnames'
+import columns from './FlowColumns'
+
+FlowRow.propTypes = {
+ onSelect: PropTypes.func.isRequired,
+ flow: PropTypes.object.isRequired,
+ highlighted: PropTypes.bool,
+ selected: PropTypes.bool,
+}
+
+export default function FlowRow({ flow, selected, highlighted, onSelect }) {
+ const className = classnames({
+ 'selected': selected,
+ 'highlighted': highlighted,
+ 'intercepted': flow.intercepted,
+ 'has-request': flow.request,
+ 'has-response': flow.response,
+ })
+
+ return (
+ <tr className={className} onClick={() => onSelect(flow)}>
+ {columns.map(Column => (
+ <Column key={Column.name} flow={flow}/>
+ ))}
+ </tr>
+ )
+}
diff --git a/web/src/js/components/FlowTable/FlowTableHead.jsx b/web/src/js/components/FlowTable/FlowTableHead.jsx
new file mode 100644
index 00000000..a46219d1
--- /dev/null
+++ b/web/src/js/components/FlowTable/FlowTableHead.jsx
@@ -0,0 +1,43 @@
+import React, { PropTypes } from 'react'
+import { bindActionCreators } from 'redux'
+import { connect } from 'react-redux'
+import classnames from 'classnames'
+import columns from './FlowColumns'
+
+import { setSort } from "../../ducks/flows"
+
+FlowTableHead.propTypes = {
+ onSort: PropTypes.func.isRequired,
+ sortDesc: React.PropTypes.bool.isRequired,
+ sortColumn: React.PropTypes.string,
+}
+
+function FlowTableHead({ sortColumn, sortDesc, onSort }) {
+ const sortType = sortDesc ? 'sort-desc' : 'sort-asc'
+
+ return (
+ <tr>
+ {columns.map(Column => (
+ <th className={classnames(Column.headerClass, sortColumn === Column.name && sortType)}
+ key={Column.name}
+ onClick={() => onClick(Column)}>
+ {Column.headerName}
+ </th>
+ ))}
+ </tr>
+ )
+
+ function onClick(Column) {
+ onSort({ sortColumn: Column.name, sortDesc: Column.name !== sortColumn ? false : !sortDesc })
+ }
+}
+
+export default connect(
+ state => ({
+ sortDesc: state.flows.sort.sortDesc,
+ sortColumn: state.flows.sort.sortColumn,
+ }),
+ dispatch => bindActionCreators({
+ onSort: setSort,
+ }, dispatch)
+)(FlowTableHead)