@azure/functions
Version:
Microsoft Azure Functions NodeJS Framework
166 lines (135 loc) • 5.31 kB
text/typescript
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License.
import * as types from '@azure/functions';
import { HttpRequestParams, HttpRequestUser } from '@azure/functions';
import { RpcHttpData, RpcTypedData } from '@azure/functions-core';
import { Blob } from 'buffer';
import { IncomingMessage } from 'http';
import * as stream from 'stream';
import { ReadableStream } from 'stream/web';
import { FormData, Headers, HeadersInit, Request as uRequest } from 'undici';
import { URLSearchParams } from 'url';
import { fromNullableMapping } from '../converters/fromRpcNullable';
import { fromRpcTypedData } from '../converters/fromRpcTypedData';
import { AzFuncSystemError } from '../errors';
import { isDefined, nonNullProp } from '../utils/nonNull';
import { extractHttpUserFromHeaders } from './extractHttpUserFromHeaders';
interface InternalHttpRequestInit extends RpcHttpData {
undiciRequest?: uRequest;
}
export class HttpRequest implements types.HttpRequest {
readonly query: URLSearchParams;
readonly params: HttpRequestParams;
constructor(init: InternalHttpRequestInit) {
this.
let uReq = init.undiciRequest;
if (!uReq) {
const url = nonNullProp(init, 'url');
let body: Buffer | string | undefined;
if (init.body?.bytes) {
body = Buffer.from(init.body?.bytes);
} else if (init.body?.string) {
body = init.body.string;
}
uReq = new uRequest(url, {
body,
method: nonNullProp(init, 'method'),
headers: fromNullableMapping(init.nullableHeaders, init.headers),
});
}
this.
if (init.nullableQuery || init.query) {
this.query = new URLSearchParams(fromNullableMapping(init.nullableQuery, init.query));
} else {
this.query = new URL(this.
}
this.params = fromNullableMapping(init.nullableParams, init.params);
}
get url(): string {
return this.
}
get method(): string {
return this.
}
get headers(): Headers {
return this.
}
get user(): HttpRequestUser | null {
if (this.
this.
}
return this.
}
get body(): ReadableStream<any> | null {
return this.
}
get bodyUsed(): boolean {
return this.
}
async arrayBuffer(): Promise<ArrayBuffer> {
return this.
}
async blob(): Promise<Blob> {
return this.
}
async formData(): Promise<FormData> {
return this.
}
async json(): Promise<unknown> {
return this.
}
async text(): Promise<string> {
return this.
}
clone(): HttpRequest {
const newInit = structuredClone(this.
newInit.undiciRequest = this.
return new HttpRequest(newInit);
}
}
export function createStreamRequest(
proxyReq: IncomingMessage,
triggerMetadata: Record<string, RpcTypedData>
): HttpRequest {
const hostHeaderName = 'x-forwarded-host';
const protoHeaderName = 'x-forwarded-proto';
const host = proxyReq.headers[hostHeaderName];
const proto = proxyReq.headers[protoHeaderName];
if (typeof host !== 'string' || typeof proto !== 'string') {
throw new AzFuncSystemError(`Expected headers "${hostHeaderName}" and "${protoHeaderName}" to be set.`);
}
const url = `${proto}://${host}${nonNullProp(proxyReq, 'url')}`;
let body: stream.Readable | undefined;
const lowerMethod = proxyReq.method?.toLowerCase();
if (lowerMethod !== 'get' && lowerMethod !== 'head') {
body = proxyReq;
}
// Get headers and params from trigger metadata
// See here for more info: https://github.com/Azure/azure-functions-host/issues/9840
// NOTE: We ignore query info because it has this bug: https://github.com/Azure/azure-functions-nodejs-library/issues/168
const { Query: rpcQueryIgnored, Headers: rpcHeaders, ...rpcParams } = triggerMetadata;
let headers: HeadersInit | undefined;
const headersData = fromRpcTypedData(rpcHeaders);
if (typeof headersData === 'object' && isDefined(headersData)) {
headers = <HeadersInit>headersData;
}
const uReq = new uRequest(url, {
body,
duplex: 'half',
method: nonNullProp(proxyReq, 'method'),
headers,
});
const params: Record<string, string> = {};
for (const [key, rpcValue] of Object.entries(rpcParams)) {
if (isDefined(rpcValue.string)) {
params[key] = rpcValue.string;
}
}
return new HttpRequest({
undiciRequest: uReq,
params,
});
}