From 7b31f106135d77d2c2d539aced122c8374c8dc67 Mon Sep 17 00:00:00 2001 From: Weili Kuo Date: Wed, 18 Sep 2024 22:21:14 -0700 Subject: [PATCH] Ensure plus character in query parameter is percent encoded --- Sources/Endpoints/Endpoint+URLRequest.swift | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/Sources/Endpoints/Endpoint+URLRequest.swift b/Sources/Endpoints/Endpoint+URLRequest.swift index 2d9ea19..4357d7a 100644 --- a/Sources/Endpoints/Endpoint+URLRequest.swift +++ b/Sources/Endpoints/Endpoint+URLRequest.swift @@ -12,6 +12,14 @@ import Foundation import FoundationNetworking #endif +extension CharacterSet { + static let urlQueryAllowedWithoutPlus: CharacterSet = { + var characterSet = CharacterSet.urlQueryAllowed + characterSet.remove(charactersIn: "+") + return characterSet + }() +} + extension Endpoint { /// Generates a `URLRequest` given the associated request value. @@ -75,8 +83,19 @@ extension Endpoint { return nil } + // URLComponents does not encode "+" by default because it is included in `.urlQueryAllowed`. To ensure + // that it is encoded, we manually encode the query items using our custom character set, then assign the + // result to `percentEncodedQuery` to prevent URLComponents from re-encoding the values. if !urlQueryItems.isEmpty { - components.queryItems = urlQueryItems + let queryString = urlQueryItems.compactMap { item -> String? in + guard + let value = item.value, + let encodedName = item.name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowedWithoutPlus), + let encodedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowedWithoutPlus) + else { return nil } + return "\(encodedName)=\(encodedValue)" + }.joined(separator: "&") + components.percentEncodedQuery = queryString } let baseUrl = environment.baseUrl