Skip to content

Commit

Permalink
KTOR-6850 Fix the query parameters object for each engine but Netty t… (
Browse files Browse the repository at this point in the history
#4015)

* KTOR-6850 Fix the query parameters object for each engine but Netty to return an empty string for the query parameter without value

(cherry picked from commit 7c76fa7)
  • Loading branch information
Stexxe authored and e5l committed Apr 5, 2024
1 parent dc0d311 commit 2cd05c6
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ internal class CIOApplicationRequest(

override val headers: Headers = CIOHeaders(request.headers)

override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters) }
override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters).toQueryParameters() }

override val rawQueryParameters: Parameters by lazy {
val uri = request.uri.toString()
Expand All @@ -47,6 +47,26 @@ internal class CIOApplicationRequest(
}
}

/**
* Converts parameters to query parameters by fixing the [Parameters.get] method
* to make it return an empty string for the query parameter without value
*/
private fun Parameters.toQueryParameters(): Parameters {
val parameters = this
return object : Parameters {
override fun get(name: String): String? {
val values = getAll(name) ?: return null
return if (values.isEmpty()) "" else values.first()
}
override val caseInsensitiveName: Boolean
get() = parameters.caseInsensitiveName
override fun getAll(name: String): List<String>? = parameters.getAll(name)
override fun names(): Set<String> = parameters.names()
override fun entries(): Set<Map.Entry<String, List<String>>> = parameters.entries()
override fun isEmpty(): Boolean = parameters.isEmpty()
}
}

internal class CIOConnectionPoint(
private val remoteNetworkAddress: NetworkAddress?,
private val localNetworkAddress: NetworkAddress?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class ServletApplicationRequest(

override val local: RequestConnectionPoint = ServletConnectionPoint(servletRequest)

override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters) }
override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters).toQueryParameters() }

override val rawQueryParameters: Parameters by lazy(LazyThreadSafetyMode.NONE) {
val uri = servletRequest.queryString ?: return@lazy Parameters.Empty
Expand All @@ -29,3 +29,23 @@ public abstract class ServletApplicationRequest(
@Suppress("LeakingThis") // this is safe because we don't access any content in the request
override val cookies: RequestCookies = ServletApplicationRequestCookies(servletRequest, this)
}

/**
* Converts parameters to query parameters by fixing the [Parameters.get] method
* to make it return an empty string for the query parameter without value
*/
private fun Parameters.toQueryParameters(): Parameters {
val parameters = this
return object : Parameters {
override fun get(name: String): String? {
val values = getAll(name) ?: return null
return if (values.isEmpty()) "" else values.first()
}
override val caseInsensitiveName: Boolean
get() = parameters.caseInsensitiveName
override fun getAll(name: String): List<String>? = parameters.getAll(name)
override fun names(): Set<String> = parameters.names()
override fun entries(): Set<Map.Entry<String, List<String>>> = parameters.entries()
override fun isEmpty(): Boolean = parameters.isEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public abstract class ServletApplicationRequest(

override val local: RequestConnectionPoint = ServletConnectionPoint(servletRequest)

override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters) }
override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters).toQueryParameters() }

override val rawQueryParameters: Parameters by lazy(LazyThreadSafetyMode.NONE) {
val uri = servletRequest.queryString ?: return@lazy Parameters.Empty
Expand All @@ -29,3 +29,23 @@ public abstract class ServletApplicationRequest(
@Suppress("LeakingThis") // this is safe because we don't access any content in the request
override val cookies: RequestCookies = ServletApplicationRequestCookies(servletRequest, this)
}

/**
* Converts parameters to query parameters by fixing the [Parameters.get] method
* to make it return an empty string for the query parameter without value
*/
private fun Parameters.toQueryParameters(): Parameters {
val parameters = this
return object : Parameters {
override fun get(name: String): String? {
val values = getAll(name) ?: return null
return if (values.isEmpty()) "" else values.first()
}
override val caseInsensitiveName: Boolean
get() = parameters.caseInsensitiveName
override fun getAll(name: String): List<String>? = parameters.getAll(name)
override fun names(): Set<String> = parameters.names()
override fun entries(): Set<Map.Entry<String, List<String>>> = parameters.entries()
override fun isEmpty(): Boolean = parameters.isEmpty()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public class TestApplicationRequest constructor(
*/
var bodyChannel: ByteReadChannel = if (closeRequest) ByteReadChannel.Empty else ByteChannel()

override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters) }
override val queryParameters: Parameters by lazy { encodeParameters(rawQueryParameters).toQueryParameters() }

override val rawQueryParameters: Parameters by lazy {
parseQueryString(queryString(), decode = false)
Expand Down Expand Up @@ -121,6 +121,26 @@ public class TestApplicationRequest constructor(
override fun receiveChannel(): ByteReadChannel = bodyChannel
}

/**
* Converts parameters to query parameters by fixing the [Parameters.get] method
* to make it return an empty string for the query parameter without value
*/
private fun Parameters.toQueryParameters(): Parameters {
val parameters = this
return object : Parameters {
override fun get(name: String): String? {
val values = getAll(name) ?: return null
return if (values.isEmpty()) "" else values.first()
}
override val caseInsensitiveName: Boolean
get() = parameters.caseInsensitiveName
override fun getAll(name: String): List<String>? = parameters.getAll(name)
override fun names(): Set<String> = parameters.names()
override fun entries(): Set<Map.Entry<String, List<String>>> = parameters.entries()
override fun isEmpty(): Boolean = parameters.isEmpty()
}
}

/**
* Sets an HTTP request body text content.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,19 @@ abstract class ContentTestSuite<TEngine : ApplicationEngine, TConfiguration : Ap
}
}

@Test
fun testAccessingQueryParameterWithoutValue() {
createAndStartServer {
get("/") {
call.respondText(call.request.queryParameters["auto"].toString())
}
}

withUrl("/?auto") {
assertEquals("", bodyAsText())
}
}

companion object {
const val classesDir: String = "build/classes/"
}
Expand Down

0 comments on commit 2cd05c6

Please sign in to comment.