Repository: h3js/h3 (TypeScript)

Issue:
Add `cookieDomainRewrite`, `cookiePathRewrite` options to `sendProxy` and `proxyRequest`
### Describe the feature

Usually, when using proxy, it is necessary to rewrite `domain` and `path` attributes in `set-cookie` headers. I suggest to add `cookieDomainRewrite` and `cookiePathRewrite` options to `proxyRequest` (similar to how `http-proxy` handles it).

Example:
```ts
proxyRequest(event, target, {
  cookieDomainRewrite: 'new.domain',
  cookiePathRewrite: '/api'
})
```

So if target server returns:
```http
Set-Cookie: foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT
Set-Cookie: bar=38afes7a8; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT
```
the resulted cookies should be:
```http
Set-Cookie: foo=219ffwef9w0f; Domain=new.domain; Path=/api; Expires=Wed, 30 Aug 2022 00:00:00 GMT
Set-Cookie: bar=38afes7a8; Domain=new.domain; Path=/api; Expires=Wed, 30 Aug 2022 00:00:00 GMT
```
The same should work for comma separated cookies in one `set-cookie` header (https://www.rfc-editor.org/rfc/rfc2616#section-4.2):
```http
Set-Cookie: foo=219ffwef9w0f; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2022 00:00:00 GMT, bar=38afes7a8; Domain=somecompany.co.uk; Path=/; Expires=Wed, 30 Aug 2019 00:00:00 GMT
```
rewrites into this: 
```http
Set-Cookie: foo=219ffwef9w0f; Domain=new.domain; Path=/api; Expires=Wed, 30 Aug 2022 00:00:00 GMT, bar=38afes7a8; Domain=new.domain; Path=/api; Expires=Wed, 30 Aug 2019 00:00:00 GMT
```



### Additional information

- [X] Would you be willing to help implement this feature?



Fix (patch):
diff --git a/README.md b/README.md
index 3f78992a1..a6a36d2e6 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,19 @@ app.use(eventHandler(() => '<h1>Hello world!</h1>'))
 app.use('/1', eventHandler(() => '<h1>Hello world!</h1>'))
    .use('/2', eventHandler(() => '<h1>Goodbye!</h1>'))
 
+// We can proxy requests and rewrite cookie's domain and path
+app.use('/api', eventHandler((event) => proxyRequest('https://example.com', {
+  // f.e. keep one domain unchanged, rewrite one domain and remove other domains
+  cookieDomainRewrite: {
+    "example.com": "example.com",
+    "example.com": "somecompany.co.uk",
+    "*": "",
+  },
+  cookiePathRewrite: {
+    "/": "/api"
+  },
+}))
+
 // Legacy middleware with 3rd argument are automatically promisified
 app.use(fromNodeMiddleware((req, res, next) => { req.setHeader('x-foo', 'bar'); next() }))
 
@@ -146,8 +159,8 @@ H3 has a concept of composable utilities that accept `event` (from `eventHandler
 - `isMethod(event, expected, allowHead?)`
 - `assertMethod(event, expected, allowHead?)`
 - `createError({ statusCode, statusMessage, data? })`
-- `sendProxy(event, { target, headers?, fetchOptions?, fetch?, sendStream? })`
-- `proxyRequest(event, { target, headers?, fetchOptions?, fetch?, sendStream? })`
+- `sendProxy(event, { target, ...options })`
+- `proxyRequest(event, { target, ...options })`
 - `fetchWithEvent(event, req, init, { fetch? }?)`
 - `getProxyRequestHeaders(event)`
 - `sendNoContent(event, code = 204)`
diff --git a/src/utils/internal/cookies.ts b/src/utils/internal/cookies.ts
index d96162a7a..8e8b7ef58 100644
--- a/src/utils/internal/cookies.ts
+++ b/src/utils/internal/cookies.ts
@@ -7,7 +7,7 @@
  * Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
  * @source https://github.com/nfriedly/set-cookie-parser/blob/3eab8b7d5d12c8ed87832532861c1a35520cf5b3/lib/set-cookie.js#L144
  */
-export default function splitCookiesString(cookiesString: string) {
+export default function splitCookiesString(cookiesString: string): string[] {
   if (typeof cookiesString !== "string") {
     return [];
   }
diff --git a/src/utils/proxy.ts b/src/utils/proxy.ts
index 0069edb0d..46757d32e 100644
--- a/src/utils/proxy.ts
+++ b/src/utils/proxy.ts
@@ -9,6 +9,8 @@ export interface ProxyOptions {
   fetchOptions?: RequestInit;
   fetch?: typeof fetch;
   sendStream?: boolean;
+  cookieDomainRewrite?: string | Record<string, string>;
+  cookiePathRewrite?: string | Record<string, string>;
 }
 
 const PayloadMethods = new Set(["PATCH", "POST", "PUT", "DELETE"]);
@@ -75,9 +77,27 @@ export async function sendProxy(
       continue;
     }
     if (key === "set-cookie") {
-      event.node.res.setHeader("set-cookie", splitCookiesString(value));
+      const cookies = splitCookiesString(value).map((cookie) => {
+        if (opts.cookieDomainRewrite) {
+          cookie = rewriteCookieProperty(
+            cookie,
+            opts.cookieDomainRewrite,
+            "domain"
+          );
+        }
+        if (opts.cookiePathRewrite) {
+          cookie = rewriteCookieProperty(
+            cookie,
+            opts.cookiePathRewrite,
+            "path"
+          );
+        }
+        return cookie;
+      });
+      event.node.res.setHeader("set-cookie", cookies);
       continue;
     }
+
     event.node.res.setHeader(key, value);
   }
 
@@ -140,3 +160,25 @@ function _getFetch(_fetch?: typeof fetch) {
     "fetch is not available. Try importing `node-fetch-native/polyfill` for Node.js."
   );
 }
+
+function rewriteCookieProperty(
+  header: string,
+  map: string | Record<string, string>,
+  property: string
+) {
+  const _map = typeof map === "string" ? { "*": map } : map;
+  return header.replace(
+    new RegExp(`(;\\s*${property}=)([^;]+)`, "gi"),
+    (match, prefix, previousValue) => {
+      let newValue;
+      if (previousValue in _map) {
+        newValue = _map[previousValue];
+      } else if ("*" in _map) {
+        newValue = _map["*"];
+      } else {
+        return match;
+      }
+      return newValue ? prefix + newValue : "";
+    }
+  );
+}