diff --git a/web/src/js/__tests__/components/FlowView/ConnectionSpec.tsx b/web/src/js/__tests__/components/FlowView/ConnectionSpec.tsx new file mode 100644 index 000000000..220b48bf3 --- /dev/null +++ b/web/src/js/__tests__/components/FlowView/ConnectionSpec.tsx @@ -0,0 +1,30 @@ +import React from "react"; +import { formatAddress } from "../../../components/FlowView/Connection"; +import { render } from "../../test-utils"; + +describe("formatAddress", () => { + it("should handle IPv4 addresses", () => { + let { asFragment } = render( + + {formatAddress("Address", ["8.8.8.8", 53])} +
, + ); + expect(asFragment()).toHaveTextContent("8.8.8.8:53"); + }); + it("should handle IPv6 addresses", () => { + let { asFragment } = render( + + {formatAddress("Address", ["::1", 53, 0, 0])} +
, + ); + expect(asFragment()).toHaveTextContent("[::1]:53"); + }); + it("should handle missing addresses", () => { + let { asFragment } = render( + + {formatAddress("Address", undefined)} +
, + ); + expect(asFragment()).not.toHaveTextContent("Address"); + }); +}); diff --git a/web/src/js/components/FlowView/Connection.tsx b/web/src/js/components/FlowView/Connection.tsx index cf2d0e7d0..306df93ef 100644 --- a/web/src/js/components/FlowView/Connection.tsx +++ b/web/src/js/components/FlowView/Connection.tsx @@ -1,49 +1,46 @@ import * as React from "react"; import { formatTimeStamp } from "../../utils"; -import { Client, Flow, Server } from "../../flow"; - -import type { JSX } from "react"; +import { Address, Client, Flow, Server } from "../../flow"; type ConnectionInfoProps = { conn: Client | Server; }; +export function formatAddress( + desc: string, + address: Address | undefined, +): React.ReactElement { + if (address === undefined) { + return <>; + } + // strip IPv6 flowid + address = [address[0], address[1]]; + // Add IPv6 brackets + if (address[0].includes(":")) { + address[0] = `[${address[0]}]`; + } + return ( + + {desc}: + {address.join(":")} + + ); +} + export function ConnectionInfo({ conn }: ConnectionInfoProps) { - let address_info: JSX.Element | null = null; + let address_info: React.ReactElement; if ("address" in conn) { // Server address_info = ( <> - - Address: - {conn.address?.join(":")} - - {conn.peername && ( - - Resolved address: - {conn.peername.join(":")} - - )} - {conn.sockname && ( - - Source address: - {conn.sockname.join(":")} - - )} + {formatAddress("Address", conn.address)} + {formatAddress("Resolved address", conn.peername)} + {formatAddress("Source address", conn.sockname)} ); } else { // Client - if (conn.peername?.[0]) { - address_info = ( - <> - - Address: - {conn.peername?.join(":")} - - - ); - } + address_info = formatAddress("Address", conn.peername); } return ( @@ -82,7 +79,7 @@ export function ConnectionInfo({ conn }: ConnectionInfoProps) { ); } -function attrList(data: [string, string][]): JSX.Element { +function attrList(data: [string, string][]): React.ReactElement { return (
{data.map(([k, v]) => ( @@ -95,7 +92,7 @@ function attrList(data: [string, string][]): JSX.Element { ); } -export function CertificateInfo({ flow }: { flow: Flow }): JSX.Element { +export function CertificateInfo({ flow }: { flow: Flow }): React.ReactElement { const cert = flow.server_conn?.cert; if (!cert) return <>; diff --git a/web/src/js/flow.ts b/web/src/js/flow.ts index 6f51002d6..80d2f6bd3 100644 --- a/web/src/js/flow.ts +++ b/web/src/js/flow.ts @@ -39,7 +39,8 @@ export interface Error { timestamp: number; } -export type Address = [string, number]; +// IPv6 has flowinfo and scope_id (currently not correctly typed on the Python side) +export type Address = [string, number] | [string, number, number, number]; export interface Connection { id: string;