Skip to content

Commit e9b07f7

Browse files
committed
fix: Deal with URL base
1 parent 3c15719 commit e9b07f7

File tree

2 files changed

+26
-11
lines changed

2 files changed

+26
-11
lines changed

packages/core/src/fetch.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable complexity */
12
import { getClient } from './currentScopes';
23
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes';
34
import { SPAN_STATUS_ERROR, setHttpStatus, startInactiveSpan } from './tracing';
@@ -53,7 +54,20 @@ export function instrumentFetchRequest(
5354
return undefined;
5455
}
5556

56-
const parsedUrl = parseStringToURL(url);
57+
// Curious about `thismessage:/`? See: https://www.rfc-editor.org/rfc/rfc2557.html
58+
// > When the methods above do not yield an absolute URI, a base URL
59+
// > of "thismessage:/" MUST be employed. This base URL has been
60+
// > defined for the sole purpose of resolving relative references
61+
// > within a multipart/related structure when no other base URI is
62+
// > specified.
63+
//
64+
// We need to provide a base URL to `parseStringToURL` because the fetch API gives us a
65+
// relative URL sometimes.
66+
//
67+
// This is the only case where we need to provide a base URL to `parseStringToURL`
68+
// because the relative URL is not valid on its own.
69+
const parsedUrl = url.startsWith('/') ? parseStringToURL(url, 'thismessage:/') : parseStringToURL(url);
70+
const fullUrl = url.startsWith('/') ? undefined : parsedUrl?.href;
5771

5872
const hasParent = !!getActiveSpan();
5973

@@ -65,10 +79,11 @@ export function instrumentFetchRequest(
6579
url,
6680
type: 'fetch',
6781
'http.method': method,
68-
'http.url': parsedUrl?.href || url,
82+
'http.url': parsedUrl?.href,
6983
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin,
7084
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client',
71-
...(parsedUrl?.host && { 'server.address': parsedUrl.host }),
85+
...(fullUrl && { 'http.url': fullUrl }),
86+
...(fullUrl && parsedUrl?.host && { 'server.address': parsedUrl.host }),
7287
...(parsedUrl?.search && { 'http.query': parsedUrl.search }),
7388
...(parsedUrl?.hash && { 'http.fragment': parsedUrl.hash }),
7489
},

packages/core/src/utils-hoist/url.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type PartialURL = {
88
};
99

1010
interface URLwithCanParse extends URL {
11-
canParse: (url: string) => boolean;
11+
canParse: (url: string, base?: string | URL | undefined) => boolean;
1212
}
1313

1414
/**
@@ -17,16 +17,16 @@ interface URLwithCanParse extends URL {
1717
* @param url - The URL to parse
1818
* @returns The parsed URL object or undefined if the URL is invalid
1919
*/
20-
export function parseStringToURL(url: string): URL | undefined {
20+
export function parseStringToURL(url: string, base?: string | URL | undefined): URL | undefined {
2121
try {
22+
// Use `canParse` to short-circuit the URL constructor if it's not a valid URL
23+
// This is faster than trying to construct the URL and catching the error
2224
// Node 20+, Chrome 120+, Firefox 115+, Safari 17+
23-
if ('canParse' in URL) {
24-
// Use `canParse` to short-circuit the URL constructor if it's not a valid URL
25-
// This is faster than trying to construct the URL and catching the error
26-
return (URL as unknown as URLwithCanParse).canParse(url) ? new URL(url) : undefined;
27-
} else {
28-
return new URL(url);
25+
if ('canParse' in URL && !(URL as unknown as URLwithCanParse).canParse(url, base)) {
26+
return undefined;
2927
}
28+
29+
return new URL(url, base);
3030
} catch {
3131
// empty body
3232
}

0 commit comments

Comments
 (0)