Repository: chimurai/http-proxy-middleware (ts)

Issue:
Removes port from target
### Checks

- [X] I understand project setup issues should be asked on [StackOverflow](https://stackoverflow.com/questions/tagged/http-proxy-middleware) or in [GitHub Discussions](https://github.com/chimurai/http-proxy-middleware/discussions).
- [X] I updated to latest `http-proxy-middleware`.

### Describe the bug (be clear and concise)

When a target with a port is given, it gets proxied to the domain without port.
E.g. when using http://127.0.0.1:3001 as a target, it will proxy to http://127.0.0.1

### Step-by-step reproduction instructions

```shell
1. Set target to http://127.0.0.1:3001
2. Use the proxy 
3. Redirects to the url without the port
```


### Expected behavior (be clear and concise)

Go to the correct port

### How is http-proxy-middleware used in your project?

```shell
- "_project_#custom-proxy" depends on it
   - Hoisted from "_project_#custom-proxy#http-proxy-middleware"
```


### What http-proxy-middleware configuration are you using?

```typescript
3.0.0 - createProxyMiddleware()
```


### What OS/version and node/version are you seeing the problem?

```shell
MacOS 14.4.1 - Node v20.8.1
```


### Additional context (optional)

_No response_

Pull request description:
fix(logger-plugin): fix missing target port
fixes #985

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d25e2d..dcc44e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
 
 - fix(type): fix RequestHandler return type
 - refactor(errors): improve pathFilter error message
+- fix(logger-plugin): fix missing target port
 
 ## [v3.0.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v3.0.0)
 
diff --git a/src/plugins/default/logger-plugin.ts b/src/plugins/default/logger-plugin.ts
index 00c70ba..5e36803 100644
--- a/src/plugins/default/logger-plugin.ts
+++ b/src/plugins/default/logger-plugin.ts
@@ -1,5 +1,20 @@
+import { URL } from 'url';
 import { Plugin } from '../../types';
 import { getLogger } from '../../logger';
+import type { IncomingMessage } from 'node:http';
+
+type ExpressRequest = {
+  /** Express req.baseUrl */
+  baseUrl?: string;
+};
+
+type BrowserSyncRequest = {
+  /** BrowserSync req.originalUrl */
+  originalUrl?: string;
+};
+
+/** Request Types from different server libs */
+type FrameworkRequest = IncomingMessage & ExpressRequest & BrowserSyncRequest;
 
 export const loggerPlugin: Plugin = (proxyServer, options) => {
   const logger = getLogger(options);
@@ -22,11 +37,17 @@ export const loggerPlugin: Plugin = (proxyServer, options) => {
    * [HPM] GET /users/ -> http://jsonplaceholder.typicode.com/users/ [304]
    * ```
    */
-  proxyServer.on('proxyRes', (proxyRes: any, req: any, res) => {
+  proxyServer.on('proxyRes', (proxyRes: any, req: FrameworkRequest, res) => {
     // BrowserSync uses req.originalUrl
     // Next.js doesn't have req.baseUrl
     const originalUrl = req.originalUrl ?? `${req.baseUrl || ''}${req.url}`;
-    const exchange = `[HPM] ${req.method} ${originalUrl} -> ${proxyRes.req.protocol}//${proxyRes.req.host}${proxyRes.req.path} [${proxyRes.statusCode}]`;
+
+    // construct targetUrl
+    const target = new URL(options.target as URL);
+    target.pathname = proxyRes.req.path;
+    const targetUrl = target.toString();
+
+    const exchange = `[HPM] ${req.method} ${originalUrl} -> ${targetUrl} [${proxyRes.statusCode}]`;
     logger.info(exchange);
   });
 


Test patch:
diff --git a/test/e2e/http-proxy-middleware.spec.ts b/test/e2e/http-proxy-middleware.spec.ts
index db6ddb3..96f2cd8 100644
--- a/test/e2e/http-proxy-middleware.spec.ts
+++ b/test/e2e/http-proxy-middleware.spec.ts
@@ -459,7 +459,9 @@ describe('E2E http-proxy-middleware', () => {
 
         expect(logMessages).not.toBeUndefined();
         expect(logMessages.length).toBe(1);
-        expect(logMessages[0]).toBe('[HPM] GET /api/foo/bar -> http://localhost/api/foo/bar [200]');
+        expect(logMessages.at(0)).toBe(
+          `[HPM] GET /api/foo/bar -> http://localhost:${mockTargetServer.port}/api/foo/bar [200]`,
+        );
       });
     });
   });