From e31d2f39c10285cbc5169e349c473f73e4668692 Mon Sep 17 00:00:00 2001 From: Kostiantyn Horozhanov Date: Tue, 30 Jan 2024 11:53:31 +0100 Subject: [PATCH 1/2] x-www-form-urlencoded support --- template.js | 49 ++++++++++++++++++++++++++++++++++++++++++++++- template.tpl | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 3 deletions(-) diff --git a/template.js b/template.js index d636a9b..e81a1e1 100644 --- a/template.js +++ b/template.js @@ -21,6 +21,9 @@ const computeEffectiveTldPlusOne = require('computeEffectiveTldPlusOne'); const getRequestQueryParameter = require('getRequestQueryParameter'); const getType = require('getType'); const Promise = require('Promise'); +const decodeUriComponent = require('decodeUriComponent'); +const createRegex = require('createRegex'); +const makeString = require('makeString'); const requestMethod = getRequestMethod(); const path = getRequestPath(); @@ -522,7 +525,11 @@ function getEventModels(baseEventModel) { const body = getRequestBody(); if (body) { - let bodyJson = JSON.parse(body); + const contentType = getRequestHeader('content-type'); + const isFormUrlEncoded = + !!contentType && + contentType.indexOf('application/x-www-form-urlencoded') !== -1; + let bodyJson = isFormUrlEncoded ? parseUrlEncoded(body) : JSON.parse(body); if (bodyJson) { const bodyType = getType(bodyJson); const shouldUseOriginalBody = @@ -586,3 +593,43 @@ function assign() { } return target; } + +function parseUrlEncoded(data) { + const pairs = data.split('&'); + const parsedData = {}; + const regex = createRegex('\\+', 'g'); + for (const pair of pairs) { + const pairValue = pair.split('='); + const key = pairValue[0]; + const value = pairValue[1]; + const keys = key + .split('.') + .map((k) => decodeUriComponent(k.replace(regex, ' '))); + + let currentObject = parsedData; + + for (let i = 0; i < keys.length - 1; i++) { + const currentKey = keys[i]; + + if (!currentObject[currentKey]) { + const nextKey = keys[i + 1]; + const nextKeyIsNumber = makeString(makeInteger(nextKey)) === nextKey; + currentObject[currentKey] = nextKeyIsNumber ? [] : {}; + } + + currentObject = currentObject[currentKey]; + } + + const lastKey = keys[keys.length - 1]; + const decodedValue = decodeUriComponent(value.replace(regex, ' ')); + const parsedValue = JSON.parse(decodedValue) || decodedValue; + + if (getType(currentObject) === 'array') { + currentObject.push(parsedValue); + } else { + currentObject[lastKey] = parsedValue; + } + } + + return parsedData; +} diff --git a/template.tpl b/template.tpl index aec4c20..1c346f4 100644 --- a/template.tpl +++ b/template.tpl @@ -297,6 +297,9 @@ const computeEffectiveTldPlusOne = require('computeEffectiveTldPlusOne'); const getRequestQueryParameter = require('getRequestQueryParameter'); const getType = require('getType'); const Promise = require('Promise'); +const decodeUriComponent = require('decodeUriComponent'); +const createRegex = require('createRegex'); +const makeString = require('makeString'); const requestMethod = getRequestMethod(); const path = getRequestPath(); @@ -798,7 +801,11 @@ function getEventModels(baseEventModel) { const body = getRequestBody(); if (body) { - let bodyJson = JSON.parse(body); + const contentType = getRequestHeader('content-type'); + const isFormUrlEncoded = + !!contentType && + contentType.indexOf('application/x-www-form-urlencoded') !== -1; + let bodyJson = isFormUrlEncoded ? parseUrlEncoded(body) : JSON.parse(body); if (bodyJson) { const bodyType = getType(bodyJson); const shouldUseOriginalBody = @@ -863,6 +870,46 @@ function assign() { return target; } +function parseUrlEncoded(data) { + const pairs = data.split('&'); + const parsedData = {}; + const regex = createRegex('\\+', 'g'); + for (const pair of pairs) { + const pairValue = pair.split('='); + const key = pairValue[0]; + const value = pairValue[1]; + const keys = key + .split('.') + .map((k) => decodeUriComponent(k.replace(regex, ' '))); + + let currentObject = parsedData; + + for (let i = 0; i < keys.length - 1; i++) { + const currentKey = keys[i]; + + if (!currentObject[currentKey]) { + const nextKey = keys[i + 1]; + const nextKeyIsNumber = makeString(makeInteger(nextKey)) === nextKey; + currentObject[currentKey] = nextKeyIsNumber ? [] : {}; + } + + currentObject = currentObject[currentKey]; + } + + const lastKey = keys[keys.length - 1]; + const decodedValue = decodeUriComponent(value.replace(regex, ' ')); + const parsedValue = JSON.parse(decodedValue) || decodedValue; + + if (getType(currentObject) === 'array') { + currentObject.push(parsedValue); + } else { + currentObject[lastKey] = parsedValue; + } + } + + return parsedData; +} + ___SERVER_PERMISSIONS___ @@ -1163,7 +1210,10 @@ ___SERVER_PERMISSIONS___ ___TESTS___ -scenarios: [] +scenarios: +- name: Quick Test + code: runCode(); +setup: '' ___NOTES___ From 1fc551bd8c4b9e6cb7523e971ab9ae5b4166a074 Mon Sep 17 00:00:00 2001 From: Kostiantyn Horozhanov Date: Tue, 30 Jan 2024 15:02:28 +0100 Subject: [PATCH 2/2] Meta update --- metadata.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/metadata.yaml b/metadata.yaml index 8614594..2801606 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -1,5 +1,7 @@ homepage: "https://stape.io/" versions: + - sha: e31d2f39c10285cbc5169e349c473f73e4668692 + changeNotes: Added support for x-www-form-urlencoded. - sha: 4d9f4d4fbded5414cb091efac70224d31c522e5c changeNotes: Fix user address data. - sha: 905af50fe8eb5f61e58a6d04c33fd6faf01e7f98