1
0
mirror of https://github.com/square/okhttp.git synced 2025-11-24 18:41:06 +03:00

Idiomatic Kotlin in MockResponse.kt

This commit is contained in:
Masaru Nomura
2019-05-30 02:04:18 -04:00
parent 4258c913b3
commit 1294e11fc5
5 changed files with 85 additions and 48 deletions

View File

@@ -31,7 +31,8 @@ abstract class Dispatcher {
* request bodies. * request bodies.
*/ */
open fun peek(): MockResponse { open fun peek(): MockResponse {
return MockResponse().setSocketPolicy(SocketPolicy.KEEP_OPEN) val mockResponse = MockResponse()
return mockResponse.apply { mockResponse.socketPolicy = SocketPolicy.KEEP_OPEN }
} }
/** /**

View File

@@ -25,7 +25,10 @@ import java.util.concurrent.TimeUnit
/** A scripted response to be replayed by the mock web server. */ /** A scripted response to be replayed by the mock web server. */
class MockResponse : Cloneable { class MockResponse : Cloneable {
private var status: String = "" /** Returns the HTTP response line, such as "HTTP/1.1 200 OK". */
@set:JvmName("status")
var status: String = ""
private var headers = Headers.Builder() private var headers = Headers.Builder()
private var trailers = Headers.Builder() private var trailers = Headers.Builder()
@@ -36,8 +39,16 @@ class MockResponse : Cloneable {
private var throttlePeriodAmount = 1L private var throttlePeriodAmount = 1L
private var throttlePeriodUnit = TimeUnit.SECONDS private var throttlePeriodUnit = TimeUnit.SECONDS
private var socketPolicy = SocketPolicy.KEEP_OPEN @set:JvmName("socketPolicy")
private var http2ErrorCode = -1 var socketPolicy = SocketPolicy.KEEP_OPEN
/**
* Sets the [HTTP/2 error code](https://tools.ietf.org/html/rfc7540#section-7) to be
* returned when resetting the stream.
* This is only valid with [SocketPolicy.RESET_STREAM_AT_START].
*/
@set:JvmName("http2ErrorCode")
var http2ErrorCode = -1
private var bodyDelayAmount = 0L private var bodyDelayAmount = 0L
private var bodyDelayUnit = TimeUnit.MILLISECONDS private var bodyDelayUnit = TimeUnit.MILLISECONDS
@@ -72,9 +83,21 @@ class MockResponse : Cloneable {
return result return result
} }
/** Returns the HTTP response line, such as "HTTP/1.1 200 OK". */ @JvmName("-deprecated_getStatus")
@Deprecated(
message = "moved to var",
replaceWith = ReplaceWith(expression = "status"),
level = DeprecationLevel.WARNING)
fun getStatus(): String = status fun getStatus(): String = status
@Deprecated(
message = "moved to var. Replace setStatus(...) with status(...) to fix Java",
replaceWith = ReplaceWith(expression = "apply { this.status = status }"),
level = DeprecationLevel.WARNING)
fun setStatus(status: String) = apply {
this.status = status
}
fun setResponseCode(code: Int): MockResponse { fun setResponseCode(code: Int): MockResponse {
val reason = when (code) { val reason = when (code) {
in 100..199 -> "Informational" in 100..199 -> "Informational"
@@ -84,11 +107,7 @@ class MockResponse : Cloneable {
in 500..599 -> "Server Error" in 500..599 -> "Server Error"
else -> "Mock Response" else -> "Mock Response"
} }
return setStatus("HTTP/1.1 $code $reason") return apply { status = "HTTP/1.1 $code $reason" }
}
fun setStatus(status: String) = apply {
this.status = status
} }
/** Returns the HTTP headers, such as "Content-Length: 0". */ /** Returns the HTTP headers, such as "Content-Length: 0". */
@@ -193,19 +212,32 @@ class MockResponse : Cloneable {
fun setChunkedBody(body: String, maxChunkSize: Int): MockResponse = fun setChunkedBody(body: String, maxChunkSize: Int): MockResponse =
setChunkedBody(Buffer().writeUtf8(body), maxChunkSize) setChunkedBody(Buffer().writeUtf8(body), maxChunkSize)
@JvmName("-deprecated_getSocketPolicy")
@Deprecated(
message = "moved to var",
replaceWith = ReplaceWith(expression = "socketPolicy"),
level = DeprecationLevel.WARNING)
fun getSocketPolicy() = socketPolicy fun getSocketPolicy() = socketPolicy
@Deprecated(
message = "moved to var. Replace setSocketPolicy(...) with socketPolicy(...) to fix Java",
replaceWith = ReplaceWith(expression = "apply { this.socketPolicy = socketPolicy }"),
level = DeprecationLevel.WARNING)
fun setSocketPolicy(socketPolicy: SocketPolicy) = apply { fun setSocketPolicy(socketPolicy: SocketPolicy) = apply {
this.socketPolicy = socketPolicy this.socketPolicy = socketPolicy
} }
@JvmName("-deprecated_getHttp2ErrorCode")
@Deprecated(
message = "moved to var",
replaceWith = ReplaceWith(expression = "http2ErrorCode"),
level = DeprecationLevel.WARNING)
fun getHttp2ErrorCode() = http2ErrorCode fun getHttp2ErrorCode() = http2ErrorCode
/** @Deprecated(
* Sets the [HTTP/2 error code](https://tools.ietf.org/html/rfc7540#section-7) to be message = "moved to var. Replace setHttp2ErrorCode(...) with http2ErrorCode(...) to fix Java",
* returned when resetting the stream. replaceWith = ReplaceWith(expression = "apply { this.http2ErrorCode = http2ErrorCode }"),
* This is only valid with [SocketPolicy.RESET_STREAM_AT_START]. level = DeprecationLevel.WARNING)
*/
fun setHttp2ErrorCode(http2ErrorCode: Int) = apply { fun setHttp2ErrorCode(http2ErrorCode: Int) = apply {
this.http2ErrorCode = http2ErrorCode this.http2ErrorCode = http2ErrorCode
} }
@@ -264,7 +296,7 @@ class MockResponse : Cloneable {
* This will overwrite any previously set status or body. * This will overwrite any previously set status or body.
*/ */
fun withWebSocketUpgrade(listener: WebSocketListener) = apply { fun withWebSocketUpgrade(listener: WebSocketListener) = apply {
setStatus("HTTP/1.1 101 Switching Protocols") status = "HTTP/1.1 101 Switching Protocols"
setHeader("Connection", "Upgrade") setHeader("Connection", "Upgrade")
setHeader("Upgrade", "websocket") setHeader("Upgrade", "websocket")
body = null body = null

View File

@@ -370,7 +370,7 @@ class MockWebServer : ExternalResource(), Closeable {
return return
} }
val socketPolicy = dispatcher.peek().getSocketPolicy() val socketPolicy = dispatcher.peek().socketPolicy
if (socketPolicy === DISCONNECT_AT_START) { if (socketPolicy === DISCONNECT_AT_START) {
dispatchBookkeepingRequest(0, socket) dispatchBookkeepingRequest(0, socket)
socket.close() socket.close()
@@ -426,7 +426,7 @@ class MockWebServer : ExternalResource(), Closeable {
@Throws(Exception::class) @Throws(Exception::class)
fun handle() { fun handle() {
val socketPolicy = dispatcher.peek().getSocketPolicy() val socketPolicy = dispatcher.peek().socketPolicy
var protocol = Protocol.HTTP_1_1 var protocol = Protocol.HTTP_1_1
val socket: Socket val socket: Socket
when { when {
@@ -513,7 +513,7 @@ class MockWebServer : ExternalResource(), Closeable {
val source = raw.source().buffer() val source = raw.source().buffer()
val sink = raw.sink().buffer() val sink = raw.sink().buffer()
while (true) { while (true) {
val socketPolicy = dispatcher.peek().getSocketPolicy() val socketPolicy = dispatcher.peek().socketPolicy
check(processOneRequest(raw, source, sink)) { "Tunnel without any CONNECT!" } check(processOneRequest(raw, source, sink)) { "Tunnel without any CONNECT!" }
if (socketPolicy === UPGRADE_TO_SSL_AT_END) return if (socketPolicy === UPGRADE_TO_SSL_AT_END) return
} }
@@ -535,11 +535,11 @@ class MockWebServer : ExternalResource(), Closeable {
requestQueue.add(request) requestQueue.add(request)
val response = dispatcher.dispatch(request) val response = dispatcher.dispatch(request)
if (response.getSocketPolicy() === DISCONNECT_AFTER_REQUEST) { if (response.socketPolicy === DISCONNECT_AFTER_REQUEST) {
socket.close() socket.close()
return false return false
} }
if (response.getSocketPolicy() === NO_RESPONSE) { if (response.socketPolicy === NO_RESPONSE) {
// This read should block until the socket is closed. (Because nobody is writing.) // This read should block until the socket is closed. (Because nobody is writing.)
if (source.exhausted()) return false if (source.exhausted()) return false
throw ProtocolException("unexpected data") throw ProtocolException("unexpected data")
@@ -563,7 +563,7 @@ class MockWebServer : ExternalResource(), Closeable {
} }
// See warnings associated with these socket policies in SocketPolicy. // See warnings associated with these socket policies in SocketPolicy.
when (response.getSocketPolicy()) { when (response.socketPolicy) {
DISCONNECT_AT_END -> { DISCONNECT_AT_END -> {
socket.close() socket.close()
return false return false
@@ -644,7 +644,7 @@ class MockWebServer : ExternalResource(), Closeable {
} }
} }
val socketPolicy = dispatcher.peek().getSocketPolicy() val socketPolicy = dispatcher.peek().socketPolicy
if (expectContinue && socketPolicy === EXPECT_CONTINUE || socketPolicy === CONTINUE_ALWAYS) { if (expectContinue && socketPolicy === EXPECT_CONTINUE || socketPolicy === CONTINUE_ALWAYS) {
sink.writeUtf8("HTTP/1.1 100 Continue\r\n") sink.writeUtf8("HTTP/1.1 100 Continue\r\n")
sink.writeUtf8("Content-Length: 0\r\n") sink.writeUtf8("Content-Length: 0\r\n")
@@ -703,7 +703,7 @@ class MockWebServer : ExternalResource(), Closeable {
.url("$scheme://$authority/") .url("$scheme://$authority/")
.headers(request.headers) .headers(request.headers)
.build() .build()
val statusParts = response.getStatus().split(' ', limit = 3) val statusParts = response.status.split(' ', limit = 3)
val fancyResponse = Response.Builder() val fancyResponse = Response.Builder()
.code(Integer.parseInt(statusParts[1])) .code(Integer.parseInt(statusParts[1]))
.message(statusParts[2]) .message(statusParts[2])
@@ -735,7 +735,7 @@ class MockWebServer : ExternalResource(), Closeable {
@Throws(IOException::class) @Throws(IOException::class)
private fun writeHttpResponse(socket: Socket, sink: BufferedSink, response: MockResponse) { private fun writeHttpResponse(socket: Socket, sink: BufferedSink, response: MockResponse) {
sleepIfDelayed(response.getHeadersDelay(TimeUnit.MILLISECONDS)) sleepIfDelayed(response.getHeadersDelay(TimeUnit.MILLISECONDS))
sink.writeUtf8(response.getStatus()) sink.writeUtf8(response.status)
sink.writeUtf8("\r\n") sink.writeUtf8("\r\n")
writeHeaders(sink, response.getHeaders()) writeHeaders(sink, response.getHeaders())
@@ -789,9 +789,9 @@ class MockWebServer : ExternalResource(), Closeable {
val halfByteCount = byteCountNum / 2 val halfByteCount = byteCountNum / 2
val disconnectHalfway = if (isRequest) { val disconnectHalfway = if (isRequest) {
policy.getSocketPolicy() === DISCONNECT_DURING_REQUEST_BODY policy.socketPolicy === DISCONNECT_DURING_REQUEST_BODY
} else { } else {
policy.getSocketPolicy() === DISCONNECT_DURING_RESPONSE_BODY policy.socketPolicy === DISCONNECT_DURING_RESPONSE_BODY
} }
while (!socket.isClosed) { while (!socket.isClosed) {
@@ -879,9 +879,9 @@ class MockWebServer : ExternalResource(), Closeable {
@Throws(IOException::class) @Throws(IOException::class)
override fun onStream(stream: Http2Stream) { override fun onStream(stream: Http2Stream) {
val peekedResponse = dispatcher.peek() val peekedResponse = dispatcher.peek()
if (peekedResponse.getSocketPolicy() === RESET_STREAM_AT_START) { if (peekedResponse.socketPolicy === RESET_STREAM_AT_START) {
dispatchBookkeepingRequest(sequenceNumber.getAndIncrement(), socket) dispatchBookkeepingRequest(sequenceNumber.getAndIncrement(), socket)
stream.close(ErrorCode.fromHttp2(peekedResponse.getHttp2ErrorCode())!!, null) stream.close(ErrorCode.fromHttp2(peekedResponse.http2ErrorCode)!!, null)
return return
} }
@@ -891,7 +891,7 @@ class MockWebServer : ExternalResource(), Closeable {
val response: MockResponse = dispatcher.dispatch(request) val response: MockResponse = dispatcher.dispatch(request)
if (response.getSocketPolicy() === DISCONNECT_AFTER_REQUEST) { if (response.socketPolicy === DISCONNECT_AFTER_REQUEST) {
socket.close() socket.close()
return return
} }
@@ -902,7 +902,7 @@ class MockWebServer : ExternalResource(), Closeable {
"and responded: $response protocol is $protocol") "and responded: $response protocol is $protocol")
} }
if (response.getSocketPolicy() === DISCONNECT_AT_END) { if (response.socketPolicy === DISCONNECT_AT_END) {
val connection = stream.connection val connection = stream.connection
connection.shutdown(ErrorCode.NO_ERROR) connection.shutdown(ErrorCode.NO_ERROR)
} }
@@ -933,7 +933,7 @@ class MockWebServer : ExternalResource(), Closeable {
val headers = httpHeaders.build() val headers = httpHeaders.build()
val peek = dispatcher.peek() val peek = dispatcher.peek()
if (!readBody && peek.getSocketPolicy() === EXPECT_CONTINUE) { if (!readBody && peek.socketPolicy === EXPECT_CONTINUE) {
val continueHeaders = val continueHeaders =
listOf(Header(Header.RESPONSE_STATUS, "100 Continue".encodeUtf8())) listOf(Header(Header.RESPONSE_STATUS, "100 Continue".encodeUtf8()))
stream.writeHeaders(continueHeaders, outFinished = false, flushHeaders = true) stream.writeHeaders(continueHeaders, outFinished = false, flushHeaders = true)
@@ -964,13 +964,14 @@ class MockWebServer : ExternalResource(), Closeable {
val settings = response.settings val settings = response.settings
stream.connection.setSettings(settings) stream.connection.setSettings(settings)
if (response.getSocketPolicy() === NO_RESPONSE) { if (response.socketPolicy === NO_RESPONSE) {
return return
} }
val http2Headers = mutableListOf<Header>() val http2Headers = mutableListOf<Header>()
val statusParts = response.getStatus().split(' ', limit = 3) val statusParts = response.status.split(' ', limit = 3)
if (statusParts.size < 2) { if (statusParts.size < 2) {
throw AssertionError("Unexpected status: ${response.getStatus()}") throw AssertionError("Unexpected status: ${response.status}")
} }
// TODO: constants for well-known header names. // TODO: constants for well-known header names.
http2Headers.add(Header(Header.RESPONSE_STATUS, statusParts[1])) http2Headers.add(Header(Header.RESPONSE_STATUS, statusParts[1]))

View File

@@ -81,8 +81,10 @@ open class QueueDispatcher : Dispatcher() {
* Enqueued on shutdown to release threads waiting on [dispatch]. Note that this response * Enqueued on shutdown to release threads waiting on [dispatch]. Note that this response
* isn't transmitted because the connection is closed before this response is returned. * isn't transmitted because the connection is closed before this response is returned.
*/ */
private val DEAD_LETTER = MockResponse() private val DEAD_LETTER = run {
.setStatus("HTTP/1.1 $HTTP_UNAVAILABLE shutting down") val mockResponse = MockResponse()
mockResponse.apply { mockResponse.status = "HTTP/1.1 $HTTP_UNAVAILABLE shutting down" }
}
private val logger = Logger.getLogger(QueueDispatcher::class.java.name) private val logger = Logger.getLogger(QueueDispatcher::class.java.name)
} }

View File

@@ -682,10 +682,10 @@ class KotlinSourceModernTest {
@Test @Ignore @Test @Ignore
fun mockResponse() { fun mockResponse() {
var mockResponse: MockResponse = MockResponse() var mockResponse: MockResponse = MockResponse()
var status: String = mockResponse.getStatus() var status: String = mockResponse.status
status = mockResponse.getStatus() status = mockResponse.status
mockResponse = mockResponse.setStatus("") mockResponse = mockResponse.apply { mockResponse.status = "" }
mockResponse.setStatus("") mockResponse.status = ""
mockResponse = mockResponse.setResponseCode(0) mockResponse = mockResponse.setResponseCode(0)
var headers: Headers = mockResponse.getHeaders() var headers: Headers = mockResponse.getHeaders()
headers = mockResponse.getHeaders() headers = mockResponse.getHeaders()
@@ -705,13 +705,14 @@ class KotlinSourceModernTest {
mockResponse.setBody(Buffer()) mockResponse.setBody(Buffer())
mockResponse = mockResponse.setChunkedBody(Buffer(), 0) mockResponse = mockResponse.setChunkedBody(Buffer(), 0)
mockResponse = mockResponse.setChunkedBody("", 0) mockResponse = mockResponse.setChunkedBody("", 0)
var socketPolicy: SocketPolicy = mockResponse.getSocketPolicy() var socketPolicy: SocketPolicy = mockResponse.socketPolicy
socketPolicy = mockResponse.getSocketPolicy() socketPolicy = mockResponse.socketPolicy
mockResponse = mockResponse.setSocketPolicy(SocketPolicy.KEEP_OPEN) mockResponse = mockResponse.apply { mockResponse.socketPolicy = SocketPolicy.KEEP_OPEN }
var http2ErrorCode: Int = mockResponse.getHttp2ErrorCode() mockResponse.socketPolicy = SocketPolicy.KEEP_OPEN
http2ErrorCode = mockResponse.getHttp2ErrorCode() var http2ErrorCode: Int = mockResponse.http2ErrorCode
mockResponse = mockResponse.setHttp2ErrorCode(0) http2ErrorCode = mockResponse.http2ErrorCode
mockResponse.setHttp2ErrorCode(0) mockResponse = mockResponse.apply { mockResponse.http2ErrorCode = 0 }
mockResponse.http2ErrorCode = 0
mockResponse = mockResponse.throttleBody(0L, 0L, TimeUnit.SECONDS) mockResponse = mockResponse.throttleBody(0L, 0L, TimeUnit.SECONDS)
var throttleBytesPerPeriod: Long = mockResponse.throttleBytesPerPeriod var throttleBytesPerPeriod: Long = mockResponse.throttleBytesPerPeriod
throttleBytesPerPeriod = mockResponse.throttleBytesPerPeriod throttleBytesPerPeriod = mockResponse.throttleBytesPerPeriod