Skip to content

Commit

Permalink
fix(core): Remove HTTP body for GET, HEAD, and OPTIONS requests (#3621)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonathan Bennetts <[email protected]>
Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <[email protected]>
  • Loading branch information
3 people authored Mar 14, 2024
1 parent bcbff76 commit d85d0ec
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/core/src/NodeExecuteFunctions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { access as fsAccess, writeFile as fsWriteFile } from 'fs/promises';
import { IncomingMessage, type IncomingHttpHeaders } from 'http';
import { Agent, type AgentOptions } from 'https';
import get from 'lodash/get';
import isEmpty from 'lodash/isEmpty';
import pick from 'lodash/pick';
import { extension, lookup } from 'mime-types';
import type {
Expand Down Expand Up @@ -962,16 +963,29 @@ function convertN8nRequestToAxios(n8nRequest: IHttpRequestOptions): AxiosRequest
return axiosRequest;
}

const NoBodyHttpMethods = ['GET', 'HEAD', 'OPTIONS'];

/** Remove empty request body on GET, HEAD, and OPTIONS requests */
export const removeEmptyBody = (requestOptions: IHttpRequestOptions | IRequestOptions) => {
const method = requestOptions.method || 'GET';
if (NoBodyHttpMethods.includes(method) && isEmpty(requestOptions.body)) {
delete requestOptions.body;
}
};

async function httpRequest(
requestOptions: IHttpRequestOptions,
): Promise<IN8nHttpFullResponse | IN8nHttpResponse> {
removeEmptyBody(requestOptions);

let axiosRequest = convertN8nRequestToAxios(requestOptions);
if (
axiosRequest.data === undefined ||
(axiosRequest.method !== undefined && axiosRequest.method.toUpperCase() === 'GET')
) {
delete axiosRequest.data;
}

let result: AxiosResponse<any>;
try {
result = await axios(axiosRequest);
Expand Down Expand Up @@ -1276,6 +1290,8 @@ export async function requestOAuth2(
oAuth2Options?: IOAuth2Options,
isN8nRequest = false,
) {
removeEmptyBody(requestOptions);

const credentials = (await this.getCredentials(
credentialsType,
)) as unknown as OAuth2CredentialData;
Expand Down Expand Up @@ -1511,6 +1527,8 @@ export async function requestOAuth1(
requestOptions: IHttpRequestOptions | IRequestOptions,
isN8nRequest = false,
) {
removeEmptyBody(requestOptions);

const credentials = await this.getCredentials(credentialsType);

if (credentials === undefined) {
Expand Down Expand Up @@ -1584,9 +1602,12 @@ export async function httpRequestWithAuthentication(
additionalData: IWorkflowExecuteAdditionalData,
additionalCredentialOptions?: IAdditionalCredentialOptions,
) {
removeEmptyBody(requestOptions);

let credentialsDecrypted: ICredentialDataDecryptedObject | undefined;
try {
const parentTypes = additionalData.credentialsHelper.getParentTypes(credentialsType);

if (parentTypes.includes('oAuth1Api')) {
return await requestOAuth1.call(this, credentialsType, requestOptions, true);
}
Expand Down Expand Up @@ -1777,6 +1798,8 @@ export async function requestWithAuthentication(
additionalCredentialOptions?: IAdditionalCredentialOptions,
itemIndex?: number,
) {
removeEmptyBody(requestOptions);

let credentialsDecrypted: ICredentialDataDecryptedObject | undefined;

try {
Expand Down
41 changes: 41 additions & 0 deletions packages/core/test/NodeExecuteFunctions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
parseIncomingMessage,
parseRequestObject,
proxyRequestToAxios,
removeEmptyBody,
setBinaryDataBuffer,
} from '@/NodeExecuteFunctions';
import { mkdtempSync, readFileSync } from 'fs';
Expand All @@ -12,7 +13,9 @@ import { mock } from 'jest-mock-extended';
import type {
IBinaryData,
IHttpRequestMethods,
IHttpRequestOptions,
INode,
IRequestOptions,
ITaskDataConnections,
IWorkflowExecuteAdditionalData,
Workflow,
Expand Down Expand Up @@ -458,4 +461,42 @@ describe('NodeExecuteFunctions', () => {
expect(output[0].a === input.a).toEqual(false);
});
});

describe('removeEmptyBody', () => {
test.each(['GET', 'HEAD', 'OPTIONS'] as IHttpRequestMethods[])(
'Should remove empty body for %s',
async (method) => {
const requestOptions = {
method,
body: {},
} as IHttpRequestOptions | IRequestOptions;
removeEmptyBody(requestOptions);
expect(requestOptions.body).toEqual(undefined);
},
);

test.each(['GET', 'HEAD', 'OPTIONS'] as IHttpRequestMethods[])(
'Should not remove non-empty body for %s',
async (method) => {
const requestOptions = {
method,
body: { test: true },
} as IHttpRequestOptions | IRequestOptions;
removeEmptyBody(requestOptions);
expect(requestOptions.body).toEqual({ test: true });
},
);

test.each(['POST', 'PUT', 'PATCH', 'DELETE'] as IHttpRequestMethods[])(
'Should not remove empty body for %s',
async (method) => {
const requestOptions = {
method,
body: {},
} as IHttpRequestOptions | IRequestOptions;
removeEmptyBody(requestOptions);
expect(requestOptions.body).toEqual({});
},
);
});
});

0 comments on commit d85d0ec

Please sign in to comment.