@@ -1396,36 +1396,21 @@ export class Web<T extends Record<string, unknown> = Record<string, unknown>> {
13961396 }
13971397
13981398 /**
1399- * Request handler optimized for Bun runtime with automatic IP extraction .
1400- * Uses Bun's server.requestIP() for reliable client IP detection .
1399+ * Internal handler that processes requests with a known client IP .
1400+ * This is the common implementation used by both runtime-specific handlers .
14011401 *
14021402 * @param req - The incoming Request object
1403- * @param server - Bun.Server instance for accessing runtime-specific features
1403+ * @param clientIp - The client IP address (optional)
14041404 * @returns Promise that resolves to a Response object
14051405 *
1406- * @example
1407- * ```typescript
1408- * const server = Bun.serve({
1409- * port: 3000,
1410- * hostname: 'localhost',
1411- * fetch: (req, server) => app.handleBun(req, server)
1412- * });
1413- *
1414- * // Or using shorthand
1415- * const server = Bun.serve({
1416- * port: 3000,
1417- * fetch: app.handleBun
1418- * });
1419- * ```
1406+ * @internal
14201407 */
1421- async handleBun ( req : Request , server : Bun . Server ) : Promise < Response > {
1408+ private async handleWithIp ( req : Request , clientIp ?: string ) : Promise < Response > {
14221409 const method = req . method as Method ;
14231410 const parsedUrl = this . parseUrl ( req . url ) ;
14241411 const path = parsedUrl . pathname ;
14251412
14261413 try {
1427- const clientIp = server . requestIP ( req ) ?. address ;
1428-
14291414 // Match route first
14301415 const matched = this . match ( method , path ) ;
14311416 if ( ! matched ) {
@@ -1557,6 +1542,7 @@ export class Web<T extends Record<string, unknown> = Record<string, unknown>> {
15571542 req,
15581543 params : EMPTY_PARAMS ,
15591544 state : { } as T ,
1545+ clientIp,
15601546 // Minimal implementations for error handling
15611547 text : ( data , status = 500 ) => new Response ( data , { status } ) ,
15621548 json : ( data , status = 500 ) =>
@@ -1581,6 +1567,153 @@ export class Web<T extends Record<string, unknown> = Record<string, unknown>> {
15811567 return new Response ( "Internal Server Error" , { status : 500 } ) ;
15821568 }
15831569 }
1570+
1571+ /**
1572+ * Request handler optimized for Bun runtime with automatic IP extraction.
1573+ * Uses Bun's server request info for reliable client IP detection.
1574+ *
1575+ * @param req - The incoming Request object
1576+ * @param server - Bun server instance
1577+ * @returns Promise that resolves to a Response object
1578+ *
1579+ * @example
1580+ * ```typescript
1581+ * Bun.serve({
1582+ * port: 3000,
1583+ * hostname: 'localhost',
1584+ * fetch: app.handleBun
1585+ * });
1586+ * ```
1587+ */
1588+ async handleBun ( req : Request , server : unknown ) : Promise < Response > {
1589+ // Extract client IP from Bun's server object
1590+ const clientIp = ( server as any ) ?. requestIP ?.( req ) ?. address ;
1591+ return this . handleWithIp ( req , clientIp ) ;
1592+ }
1593+
1594+ /**
1595+ * Request handler optimized for Deno runtime with automatic IP extraction.
1596+ * Uses Deno's ServeHandlerInfo for reliable client IP detection.
1597+ *
1598+ * @param req - The incoming Request object
1599+ * @param info - Deno.ServeHandlerInfo instance for accessing runtime-specific features
1600+ * @returns Promise that resolves to a Response object
1601+ *
1602+ * @example
1603+ * ```typescript
1604+ * Deno.serve({
1605+ * port: 3000
1606+ * },
1607+ * (req, info) => app.handleDeno(req, info)
1608+ * );
1609+ * ```
1610+ */
1611+ async handleDeno ( req : Request , info : unknown ) : Promise < Response > {
1612+ // Extract client IP from Deno's ServeHandlerInfo
1613+ const clientIp = ( info as any ) ?. remoteAddr ?. hostname ;
1614+ return this . handleWithIp ( req , clientIp ) ;
1615+ }
1616+
1617+ /**
1618+ * Request handler for Node.js that handles both request and response.
1619+ * Automatically converts between Node.js and Web APIs and extracts client IP.
1620+ *
1621+ * @param nodeReq - Node.js IncomingMessage object
1622+ * @param nodeRes - Node.js ServerResponse object
1623+ * @returns Promise that resolves when response is sent
1624+ *
1625+ * @example
1626+ * ```typescript
1627+ * import { createServer } from "http";
1628+ *
1629+ * createServer((req, res) => app.handleNode(req, res)).listen(3000);
1630+ * ```
1631+ */
1632+ async handleNode ( nodeReq : unknown , nodeRes : unknown ) : Promise < void > {
1633+ const req = nodeReq as any ;
1634+ const res = nodeRes as any ;
1635+
1636+ try {
1637+ // Extract client IP from Node.js request
1638+ const clientIp : string | undefined = req . socket ?. remoteAddress ;
1639+
1640+ // Convert Node.js request to Web Request
1641+ const host = req . headers . host || "localhost" ;
1642+ const protocol = req . socket ?. encrypted ? "https" : "http" ;
1643+ const url = `${ protocol } ://${ host } ${ req . url } ` ;
1644+
1645+ // Create headers
1646+ const headers = new Headers ( ) ;
1647+ for ( const [ key , value ] of Object . entries ( req . headers ) ) {
1648+ if ( value ) {
1649+ if ( Array . isArray ( value ) ) {
1650+ value . forEach ( ( v ) => headers . append ( key , v ) ) ;
1651+ } else {
1652+ headers . set ( key , value as string ) ;
1653+ }
1654+ }
1655+ }
1656+
1657+ // Handle request body
1658+ let body : BodyInit | null = null ;
1659+ if ( req . method !== "GET" && req . method !== "HEAD" ) {
1660+ // Collect body data
1661+ const chunks : Buffer [ ] = [ ] ;
1662+ await new Promise < void > ( ( resolve , reject ) => {
1663+ req . on ( "data" , ( chunk : Buffer ) => chunks . push ( chunk ) ) ;
1664+ req . on ( "end" , ( ) => resolve ( ) ) ;
1665+ req . on ( "error" , reject ) ;
1666+ } ) ;
1667+
1668+ if ( chunks . length > 0 ) {
1669+ body = Buffer . concat ( chunks ) ;
1670+ }
1671+ }
1672+
1673+ // Create Web Request
1674+ const webRequest = new Request ( url , {
1675+ method : req . method ,
1676+ headers,
1677+ body,
1678+ // @ts -ignore - Node.js doesn't have duplex, but it's safe to ignore
1679+ duplex : "half" ,
1680+ } ) ;
1681+
1682+ // Handle the request with extracted IP
1683+ const response = await this . handleWithIp ( webRequest , clientIp ) ;
1684+
1685+ // Convert Web Response to Node.js response
1686+ const responseHeaders : Record < string , string > = { } ;
1687+ response . headers . forEach ( ( value , key ) => {
1688+ responseHeaders [ key ] = value ;
1689+ } ) ;
1690+
1691+ res . writeHead ( response . status , responseHeaders ) ;
1692+
1693+ // Stream the response body
1694+ if ( response . body ) {
1695+ const reader = response . body . getReader ( ) ;
1696+ try {
1697+ while ( true ) {
1698+ const { done, value } = await reader . read ( ) ;
1699+ if ( done ) break ;
1700+ res . write ( value ) ;
1701+ }
1702+ } finally {
1703+ reader . releaseLock ( ) ;
1704+ }
1705+ }
1706+
1707+ res . end ( ) ;
1708+ } catch ( error ) {
1709+ // Handle errors
1710+ if ( ! res . headersSent ) {
1711+ res . writeHead ( 500 , { "Content-Type" : "text/plain" } ) ;
1712+ }
1713+ res . end ( "Internal Server Error" ) ;
1714+ console . error ( "Error in handleNode:" , error ) ;
1715+ }
1716+ }
15841717}
15851718
15861719/**
0 commit comments