     1	/**
     2	 * Helper functions for HTTP request context creation
     3	 * Used by both HTTP and Express plugins
     4	 */
     5	
     6	/**
     7	 * Extract the real client IP address, handling proxies
     8	 * @param {Object} req - The request object (Node.js http.IncomingMessage or Express Request)
     9	 * @returns {string} The client IP address
    10	 */
    11	export function getClientIP (req) {
    12	  // Check X-Forwarded-For header (comma-separated list, first is original client)
    13	  const xForwardedFor = req.headers['x-forwarded-for']
    14	  if (xForwardedFor) {
    15	    return xForwardedFor.split(',')[0].trim()
    16	  }
    17	
    18	  // Check X-Real-IP header (single IP)
    19	  const xRealIP = req.headers['x-real-ip']
    20	  if (xRealIP) {
    21	    return xRealIP
    22	  }
    23	
    24	  // Check Cloudflare's CF-Connecting-IP
    25	  const cfConnectingIP = req.headers['cf-connecting-ip']
    26	  if (cfConnectingIP) {
    27	    return cfConnectingIP
    28	  }
    29	
    30	  // Fall back to direct connection
    31	  return req.connection?.remoteAddress ||
    32	         req.socket?.remoteAddress ||
    33	         req.ip || // Express specific
    34	         'unknown'
    35	}
    36	
    37	/**
    38	 * Check if the request is using HTTPS
    39	 * @param {Object} req - The request object
    40	 * @returns {boolean} True if the request is secure
    41	 */
    42	export function isSecure (req) {
    43	  // Express-specific property
    44	  if (req.secure !== undefined) {
    45	    return req.secure
    46	  }
    47	
    48	  // Check X-Forwarded-Proto header (used by proxies)
    49	  const xForwardedProto = req.headers['x-forwarded-proto']
    50	  if (xForwardedProto) {
    51	    return xForwardedProto === 'https'
    52	  }
    53	
    54	  // Check if connection is encrypted (Node.js)
    55	  if (req.connection?.encrypted) {
    56	    return true
    57	  }
    58	
    59	  // Check protocol directly
    60	  if (req.protocol === 'https') {
    61	    return true
    62	  }
    63	
    64	  // Default to false
    65	  return false
    66	}
    67	
    68	/**
    69	 * Get the hostname from the request
    70	 * @param {Object} req - The request object
    71	 * @returns {string} The hostname
    72	 */
    73	export function getHostname (req) {
    74	  // Express-specific property
    75	  if (req.hostname) {
    76	    return req.hostname
    77	  }
    78	
    79	  // Check Host header
    80	  const hostHeader = req.headers.host
    81	  if (hostHeader) {
    82	    // Remove port if present
    83	    const colonIndex = hostHeader.indexOf(':')
    84	    return colonIndex === -1 ? hostHeader : hostHeader.substring(0, colonIndex)
    85	  }
    86	
    87	  // Fall back
    88	  return 'localhost'
    89	}
    90	
    91	/**
    92	 * Get the port from the request
    93	 * @param {Object} req - The request object
    94	 * @returns {number|null} The port number or null
    95	 */
    96	export function getPort (req) {
    97	  // Check Host header for port
    98	  const hostHeader = req.headers.host
    99	  if (hostHeader) {
   100	    const colonIndex = hostHeader.indexOf(':')
   101	    if (colonIndex !== -1) {
   102	      const port = parseInt(hostHeader.substring(colonIndex + 1), 10)
   103	      if (!isNaN(port)) {
   104	        return port
   105	      }
   106	    }
   107	  }
   108	
   109	  // Check connection local port
   110	  if (req.connection?.localPort) {
   111	    return req.connection.localPort
   112	  }
   113	
   114	  if (req.socket?.localPort) {
   115	    return req.socket.localPort
   116	  }
   117	
   118	  // Default ports based on protocol
   119	  return isSecure(req) ? 443 : 80
   120	}
   121	
   122	/**
   123	 * Parse cookies from cookie header string
   124	 * @param {string} cookieString - The cookie header value
   125	 * @returns {Object} Parsed cookies as key-value pairs
   126	 */
   127	export function parseCookies (cookieString) {
   128	  const cookies = {}
   129	
   130	  if (!cookieString || typeof cookieString !== 'string') {
   131	    return cookies
   132	  }
   133	
   134	  // Split by semicolons and parse each cookie
   135	  cookieString.split(';').forEach(cookie => {
   136	    const trimmedCookie = cookie.trim()
   137	    if (trimmedCookie) {
   138	      const eqIndex = trimmedCookie.indexOf('=')
   139	      if (eqIndex !== -1) {
   140	        const name = trimmedCookie.substring(0, eqIndex).trim()
   141	        const value = trimmedCookie.substring(eqIndex + 1).trim()
   142	        if (name) {
   143	          // Remove quotes if present
   144	          cookies[name] = value.replace(/^"(.*)"$/, '$1')
   145	        }
   146	      }
   147	    }
   148	  })
   149	
   150	  return cookies
   151	}
   152	
   153	/**
   154	 * Extract token from request based on common patterns
   155	 * @param {Object} req - The request object
   156	 * @returns {string|null} The extracted token or null
   157	 */
   158	export function extractToken (req) {
   159	  // Check Authorization header for Bearer token
   160	  const authHeader = req.headers.authorization || req.headers.Authorization
   161	  if (authHeader && typeof authHeader === 'string') {
   162	    const bearerMatch = authHeader.match(/^Bearer\s+(.+)$/i)
   163	    if (bearerMatch) {
   164	      return bearerMatch[1]
   165	    }
   166	  }
   167	
   168	  // Check cookies for session token (Auth.js pattern)
   169	  const cookies = parseCookies(req.headers.cookie || '')
   170	  if (cookies['__Secure-authjs.session-token']) {
   171	    return cookies['__Secure-authjs.session-token']
   172	  }
   173	  if (cookies['authjs.session-token']) {
   174	    return cookies['authjs.session-token']
   175	  }
   176	
   177	  return null
   178	}
   179	
   180	/**
   181	 * Create a context object for the request
   182	 * @param {Object} req - The request object
   183	 * @param {Object} res - The response object
   184	 * @param {string} source - The source plugin ('http' or 'express')
   185	 * @returns {Object} The context object
   186	 */
   187	export function createContext (req, res, source) {
   188	  const context = {
   189	    source,
   190	    auth: {
   191	      userId: null,
   192	      claims: null
   193	    },
   194	    request: {
   195	      ip: getClientIP(req),
   196	      userAgent: req.headers['user-agent'] || null,
   197	      protocol: isSecure(req) ? 'https' : 'http',
   198	      hostname: getHostname(req),
   199	      port: getPort(req),
   200	      method: req.method,
   201	      path: req.url || req.path,
   202	      headers: req.headers,
   203	      cookies: parseCookies(req.headers.cookie || ''),
   204	      token: extractToken(req)
   205	    },
   206	    raw: { req, res },
   207	    handled: false,
   208	    rejection: null
   209	  }
   210	
   211	  // Add reject function
   212	  context.reject = (status, message, details = {}) => {
   213	    context.handled = true
   214	    context.rejection = {
   215	      status,
   216	      message,
   217	      ...details
   218	    }
   219	  }
   220	
   221	  return context
   222	}
