11import { createServer , type Server , IncomingMessage , ServerResponse } from "node:http" ;
2- import { AddressInfo } from "node:net" ;
2+ import { createServer as netCreateServer , AddressInfo } from "node:net" ;
33import { randomUUID } from "node:crypto" ;
44import { EventStore , StreamableHTTPServerTransport , EventId , StreamId } from "./streamableHttp.js" ;
55import { McpServer } from "./mcp.js" ;
66import { CallToolResult , JSONRPCMessage } from "../types.js" ;
77import { z } from "zod" ;
88import { AuthInfo } from "./auth/types.js" ;
99
10+ async function getFreePort ( ) {
11+ return new Promise ( res => {
12+ const srv = netCreateServer ( ) ;
13+ srv . listen ( 0 , ( ) => {
14+ const address = srv . address ( ) !
15+ if ( typeof address === "string" ) {
16+ throw new Error ( "Unexpected address type: " + typeof address ) ;
17+ }
18+ const port = ( address as AddressInfo ) . port ;
19+ srv . close ( ( err ) => res ( port ) )
20+ } ) ;
21+ } )
22+ }
23+
1024/**
1125 * Test server configuration for StreamableHTTPServerTransport tests
1226 */
@@ -1441,7 +1455,7 @@ describe("StreamableHTTPServerTransport DNS rebinding protection", () => {
14411455 it ( "should accept requests with allowed host headers" , async ( ) => {
14421456 const result = await createTestServerWithDnsProtection ( {
14431457 sessionIdGenerator : undefined ,
1444- allowedHosts : [ 'localhost:3001 ' ] ,
1458+ allowedHosts : [ 'localhost' ] ,
14451459 enableDnsRebindingProtection : true ,
14461460 } ) ;
14471461 server = result . server ;
@@ -1563,7 +1577,7 @@ describe("StreamableHTTPServerTransport DNS rebinding protection", () => {
15631577 it ( "should skip all validations when enableDnsRebindingProtection is false" , async ( ) => {
15641578 const result = await createTestServerWithDnsProtection ( {
15651579 sessionIdGenerator : undefined ,
1566- allowedHosts : [ 'localhost:3001 ' ] ,
1580+ allowedHosts : [ 'localhost' ] ,
15671581 allowedOrigins : [ 'http://localhost:3000' ] ,
15681582 enableDnsRebindingProtection : false ,
15691583 } ) ;
@@ -1591,7 +1605,7 @@ describe("StreamableHTTPServerTransport DNS rebinding protection", () => {
15911605 it ( "should validate both host and origin when both are configured" , async ( ) => {
15921606 const result = await createTestServerWithDnsProtection ( {
15931607 sessionIdGenerator : undefined ,
1594- allowedHosts : [ 'localhost:3001 ' ] ,
1608+ allowedHosts : [ 'localhost' ] ,
15951609 allowedOrigins : [ 'http://localhost:3001' ] ,
15961610 enableDnsRebindingProtection : true ,
15971611 } ) ;
@@ -1649,6 +1663,17 @@ async function createTestServerWithDnsProtection(config: {
16491663 { capabilities : { logging : { } } }
16501664 ) ;
16511665
1666+ const port = await getFreePort ( ) ;
1667+
1668+ if ( config . allowedHosts ) {
1669+ config . allowedHosts = config . allowedHosts . map ( host => {
1670+ if ( host . includes ( ':' ) ) {
1671+ return host ;
1672+ }
1673+ return `localhost:${ port } ` ;
1674+ } ) ;
1675+ }
1676+
16521677 const transport = new StreamableHTTPServerTransport ( {
16531678 sessionIdGenerator : config . sessionIdGenerator ,
16541679 allowedHosts : config . allowedHosts ,
@@ -1672,10 +1697,9 @@ async function createTestServerWithDnsProtection(config: {
16721697 } ) ;
16731698
16741699 await new Promise < void > ( ( resolve ) => {
1675- httpServer . listen ( 3001 , ( ) => resolve ( ) ) ;
1700+ httpServer . listen ( port , ( ) => resolve ( ) ) ;
16761701 } ) ;
16771702
1678- const port = ( httpServer . address ( ) as AddressInfo ) . port ;
16791703 const serverUrl = new URL ( `http://localhost:${ port } /` ) ;
16801704
16811705 return {
0 commit comments