angular/packages/platform-server/src/http.ts
Alan Agius 405ec8c796 fix(platform-server): resolve relative requests URL (#52326)
Prior to this commit relative HTTP requests were not being resolved to absolute even thought the behaviour is documented in https://angular.io/guide/universal#using-absolute-urls-for-http-data-requests-on-the-server.

This caused relative HTTP requests to fail when done on the server because of missing request context. This change is also required to eventually support HTTP requests handled during prerendering (SSG).

Closes #51626

PR Close #52326
2023-10-23 12:02:21 -07:00

66 lines
2.1 KiB
TypeScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {PlatformLocation, XhrFactory} from '@angular/common';
import {HttpEvent, HttpHandlerFn, HttpRequest, ɵHTTP_ROOT_INTERCEPTOR_FNS as HTTP_ROOT_INTERCEPTOR_FNS} from '@angular/common/http';
import {inject, Injectable, Provider} from '@angular/core';
import {Observable} from 'rxjs';
@Injectable()
export class ServerXhr implements XhrFactory {
private xhrImpl: typeof import('xhr2')|undefined;
// The `xhr2` dependency has a side-effect of accessing and modifying a
// global scope. Loading `xhr2` dynamically allows us to delay the loading
// and start the process once the global scope is established by the underlying
// server platform (via shims, etc).
private async ɵloadImpl(): Promise<void> {
if (!this.xhrImpl) {
const {default: xhr} = await import('xhr2');
this.xhrImpl = xhr;
}
}
build(): XMLHttpRequest {
const impl = this.xhrImpl;
if (!impl) {
throw new Error('Unexpected state in ServerXhr: XHR implementation is not loaded.');
}
return new impl.XMLHttpRequest();
}
}
function relativeUrlsTransformerInterceptorFn(
request: HttpRequest<unknown>, next: HttpHandlerFn): Observable<HttpEvent<unknown>> {
const platformLocation = inject(PlatformLocation);
const {href, protocol, hostname, port} = platformLocation;
if (!protocol.startsWith('http')) {
return next(request);
}
let urlPrefix = `${protocol}//${hostname}`;
if (port) {
urlPrefix += `:${port}`;
}
const baseHref = platformLocation.getBaseHrefFromDOM() || href;
const baseUrl = new URL(baseHref, urlPrefix);
const newUrl = new URL(request.url, baseUrl).toString();
return next(request.clone({url: newUrl}));
}
export const SERVER_HTTP_PROVIDERS: Provider[] = [
{provide: XhrFactory, useClass: ServerXhr},
{
provide: HTTP_ROOT_INTERCEPTOR_FNS,
useValue: relativeUrlsTransformerInterceptorFn,
multi: true,
},
];