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;