All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.apollographql.apollo3.mockserver.MockServerCommon.kt Maven / Gradle / Ivy

package com.apollographql.apollo3.mockserver

import okio.Buffer
import okio.BufferedSink
import okio.BufferedSource
import okio.ByteString
import okio.ByteString.Companion.encodeUtf8
import kotlin.jvm.JvmOverloads
import kotlin.jvm.JvmStatic

fun parseHeader(line: String): Pair {
  val index = line.indexOfFirst { it == ':' }
  check (index >= 0) {
    "Invalid header: $line"
  }

  return line.substring(0, index).trim() to line.substring(index + 1, line.length).trim()
}

class MockRecordedRequest(
    val method: String,
    val path: String,
    val version: String,
    val headers: Map = emptyMap(),
    val body: ByteString = ByteString.EMPTY
)

fun writeResponse(sink: BufferedSink, mockResponse: MockResponse, version: String) {
  sink.writeUtf8("${version} ${mockResponse.statusCode}\r\n")
  val contentLengthHeader = mapOf("Content-Length" to mockResponse.body.size.toString())

  (contentLengthHeader + mockResponse.headers).forEach {
    sink.writeUtf8("${it.key}: ${it.value}\r\n")
  }
  sink.writeUtf8("\r\n")
  sink.flush()

  if (mockResponse.body.size > 0) {
    sink.write(mockResponse.body)
  }
  sink.flush()
}

class MockResponse(
    val statusCode: Int = 200,
    val body: ByteString = ByteString.EMPTY,
    val headers: Map = emptyMap(),
    val delayMillis: Long = 0,
) {
  @JvmOverloads
  constructor(
      body: String,
      statusCode: Int = 200,
      headers: Map = emptyMap()
  ) : this(statusCode, body.encodeUtf8(), headers)
}

internal fun readRequest(source: BufferedSource): MockRecordedRequest? {
  var line = source.readUtf8Line()
  if (line == null) {
    // the connection was closed
    return null
  }

  val (method, path, version) = parseRequestLine(line)

  val headers = mutableMapOf()
  /**
   * Read headers
   */
  while(true) {
    line = source.readUtf8Line()
    //println("Header Line: $line")
    if (line.isNullOrBlank()) {
      break
    }

    val (key, value) = parseHeader(line)
    headers.put(key, value)
  }

  val contentLength = headers["Content-Length"]?.toLongOrNull() ?: 0
  val transferEncoding = headers["Transfer-Encoding"]?.lowercase()
  check(transferEncoding == null || transferEncoding == "identity") {
    "Transfer-Encoding $transferEncoding is not supported"
  }

  val buffer = Buffer()
  if (contentLength > 0) {
    source.read(buffer, contentLength)
  }

  return MockRecordedRequest(
      method = method,
      path = path,
      version = version,
      headers = headers,
      body = buffer.readByteString()
  )
}

fun parseRequestLine(line: String): Triple {
  val regex = Regex("([A-Z-a-z]*) ([^ ]*) (.*)")
  val match = regex.matchEntire(line)
  check (match != null) {
    "Cannot match request line: $line"
  }

  val method = match.groupValues[1].uppercase()
  check (method in listOf("GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS", "PATCH")) {
    "Unkown method $method"
  }

  return Triple(method, match.groupValues[2], match.groupValues[3])
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy