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

org.organicdesign.testUtils.http.FakeHttpServletRequest.kt Maven / Gradle / Ivy

package org.organicdesign.testUtils.http

import org.organicdesign.indented.IndentedStringable
import org.organicdesign.indented.StringUtils.oneFieldPerLineK
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.security.Principal
import java.util.Enumeration
import java.util.Locale
import javax.servlet.AsyncContext
import javax.servlet.DispatcherType
import javax.servlet.ReadListener
import javax.servlet.RequestDispatcher
import javax.servlet.ServletContext
import javax.servlet.ServletInputStream
import javax.servlet.ServletRequest
import javax.servlet.ServletResponse
import javax.servlet.http.Cookie
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.servlet.http.HttpSession
import javax.servlet.http.HttpUpgradeHandler
import javax.servlet.http.Part


/**
 * This mocks an HttpServletRequest - EXPERIMENTAL
 */
class FakeHttpServletRequest
internal constructor(
        reqB: ReqB
) : HttpServletRequest, IndentedStringable {
    override fun indentedStr(indent: Int): String =
            oneFieldPerLineK(
                    indent, "FakeHttpServletRequest",
                    listOf("url" to requestURL.toString(),
                           "remoteAddr" to remoteAddr,
                           "method" to method,
                           "encoding" to characterEncoding,
                           "locale" to locale,
                           "requestedSessionId" to requestedSessionId,
                           "inputStream" to inStream,
                           "attributes" to attributes,
                           "cookies" to cookies.map{ CookiePrinter(it) },
                           "params" to params,
                           "headers" to heads
                    ).filter { it.second != null })

    override fun toString(): String = indentedStr(0)

    private val baseUrl: String = reqB.baseUrl
    // TODO: What about nulls?  Also, Needs to return a Protocol, server name, port num, and server path (but no query string)
    override fun getRequestURL(): StringBuffer {
        val sB = StringBuffer(baseUrl)
        if (uri != null) {
            sB.append(uri)
        }
        return sB
    }

    private val uri: String? = reqB.uri
    // 2018-03-02: Tomcat 8 can return null here.  Jetty does not.
    override fun getPathInfo(): String? = uri
    override fun getRequestURI(): String? = uri
    // TODO: Not sure if this always starts with a slash or not, but maybe can't be null?
    override fun getServletPath(): String? = uri

    // Looks like an IP address
    // My code elsewhere assumes that this can never be null, so I'm going with that, at least for Jetty.
    private val remoteAddr = reqB.remoteAddr
    override fun getRemoteAddr(): String = remoteAddr

    private val method: String = reqB.method
    override fun getMethod(): String = method

    private var characterEncoding: String? = reqB.characterEncoding
    override fun getCharacterEncoding(): String? = characterEncoding
    override fun setCharacterEncoding(s: String?) { characterEncoding = s }

    private val locale: Locale? = reqB.locale
    // TODO: Can this be null?
    override fun getLocale(): Locale? = locale

    // TODO: locales that are acceptable to the client based on the Accept-Language header.
    override fun getLocales(): Enumeration =
            enumeration(listOf(locale))

    private val requestedSessionId: String? = reqB.requestedSessionId
    override fun getRequestedSessionId(): String? = requestedSessionId

    private val inStream = reqB.inStream
    private val inStreamSize = reqB.inStreamSize
    override fun getInputStream(): ServletInputStream? =
            if (inStream == null) {
                null
            } else {
                FakeServletInputStream(inStream)
            }

    override fun getContentLength(): Int =
            if (inStreamSize > Int.MAX_VALUE) {
                -1
            } else {
                inStreamSize.toInt()
            }

    override fun getContentLengthLong(): Long = inStreamSize

    private val attributes: MutableMap = reqB.attributes
    override fun getAttribute(s: String): Any? = attributes[s]
    override fun getAttributeNames(): Enumeration = enumeration(attributes.keys)
    override fun setAttribute(s: String, o: Any) { attributes[s] = o }
    override fun removeAttribute(s: String) { attributes.remove(s) }

    private val params: Map> = reqB.params.toMap()
    override fun getParameterNames(): Enumeration = enumeration(params.keys)
    override fun getParameterValues(s: String): Array? = params[s]?.toTypedArray()
    override fun getParameter(s: String): String? = getParameterValues(s)?.get(0)
    override fun getParameterMap(): Map> {
        val ret: MutableMap> = mutableMapOf()
        for (entry in params.entries) {
            ret[entry.key] = entry.value.toTypedArray()
        }
        return ret
    }

    override fun getQueryString(): String? {
        val sB = StringBuilder()
        for ((key, value) in params) {
            for (v in value) {
                sB.append(if (sB.isNotEmpty()) "&" else "")
                        .append(key).append("=")
                        .append(v)
            }
        }
        return sB.toString()
    }

    // HTTP headers are case-insensitive.
    // https://stackoverflow.com/questions/8236945/case-insensitive-string-as-hashmap-key
    // For case insensitive hack:
    // https://stackoverflow.com/questions/8236945/case-insensitive-string-as-hashmap-key
    // Actual implementation in Jetty uses an *array*.
    private val heads: Array> = when {
        reqB.headers.any{ HTTP_HEAD_HOST.equals(it.key, ignoreCase = true) } -> reqB.headers.toTypedArray()
        else -> {
            val hs = reqB.headers.toMutableList()
            hs.add(Kv(HTTP_HEAD_HOST, reqB.baseUrl.substringAfter("//")))
            hs.toTypedArray()
        }
    }

    override fun getHeader(p0: String?): String? {
        if (p0 != null) {
            for (head in heads) {
                if (p0.equals(head.key, ignoreCase = true)) {
                    return head.value
                }
            }
        }
        return null
    }

    override fun getHeaders(s: String?): Enumeration? {
        val header = getHeader(s)
        return if (header == null) null else enumeration(listOf(header))
    }

    override fun getHeaderNames(): Enumeration {
        return enumeration(heads.map { it.key }.toList())
    }

    override fun getIntHeader(s: String): Int =
            try {
                Integer.parseInt(getHeader(s))
            } catch (_: Exception) {
                -1
            }

    override fun getDateHeader(p0: String?): Long {
        return getHeader(p0)?.toLong() ?: -1
    }

    override fun getContentType(): String? = getHeader("Content-Type")

    private val cookies: MutableList = reqB.cookies
    override fun getCookies(): Array? =
            when {
                cookies.isEmpty() -> null
                else              -> cookies.toTypedArray()
            }

    override fun getAuthType(): String {
        throw UnsupportedOperationException("Not Implemented")
    }

    override fun getPathTranslated(): String? {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getContextPath(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getRemoteUser(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isUserInRole(s: String): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getUserPrincipal(): Principal {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getSession(b: Boolean): HttpSession {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getSession(): HttpSession {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun changeSessionId(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isRequestedSessionIdValid(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isRequestedSessionIdFromCookie(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isRequestedSessionIdFromURL(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    @Deprecated("")
    override fun isRequestedSessionIdFromUrl(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun authenticate(httpServletResponse: HttpServletResponse): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun login(s: String, s1: String) {
        throw UnsupportedOperationException("Not implemented")
    }
    override fun logout() {
        throw UnsupportedOperationException("Not implemented")
    }
    override fun getParts(): Collection {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getPart(s: String): Part {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun  upgrade(aClass: Class): T {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getProtocol(): String? {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getScheme(): String = baseUrl.substringBefore("://")

    override fun getServerName(): String = getHeader(HTTP_HEAD_HOST)!!.substringBefore(":")

    override fun getServerPort(): Int {
        val host: String = getHeader(HTTP_HEAD_HOST)!!
        return when {
            host.contains(":") -> host.substringAfter(":").toInt()
            else               -> schemePorts.getOrDefault(scheme, 0)
        }
    }

    override fun getReader(): BufferedReader {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getRemoteHost(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isSecure(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getRequestDispatcher(s: String): RequestDispatcher {
        throw UnsupportedOperationException("Not implemented")
    }

    @Deprecated("")
    override fun getRealPath(s: String): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getRemotePort(): Int {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getLocalName(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getLocalAddr(): String {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getLocalPort(): Int {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getServletContext(): ServletContext {
        throw UnsupportedOperationException("Not implemented")
    }

    @Throws(IllegalStateException::class)
    override fun startAsync(): AsyncContext {
        throw UnsupportedOperationException("Not implemented")
    }

    @Throws(IllegalStateException::class)
    override fun startAsync(servletRequest: ServletRequest, servletResponse: ServletResponse): AsyncContext {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isAsyncStarted(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun isAsyncSupported(): Boolean {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getAsyncContext(): AsyncContext {
        throw UnsupportedOperationException("Not implemented")
    }

    override fun getDispatcherType(): DispatcherType {
        throw UnsupportedOperationException("Not implemented")
    }

    companion object {

        const val HTTP_HEAD_HOST = "Host"

        private val schemePorts: Map = mapOf("https" to 443,
                                                         "http" to 80,
                                                         "ftp" to 21)

        fun  enumeration(iterable: Iterable): Enumeration {
            return object : Enumeration {
                var iter = iterable.iterator()
                override fun hasMoreElements(): Boolean {
                    return iter.hasNext()
                }

                override fun nextElement(): E {
                    return iter.next()
                }
            }
        }

        private class FakeServletInputStream(
                private val inStream: InputStream
        ) : ServletInputStream() {

            private var eosReached = false

            override fun isReady(): Boolean = !eosReached

            override fun isFinished(): Boolean = eosReached

            override fun setReadListener(p0: ReadListener?) {
                throw UnsupportedOperationException("Not Implemented")
            }

            @Throws(IOException::class)
            override fun read(): Int {
                val ret = inStream.read()
                if (ret == -1) {
                    eosReached = true
                }
                return ret
            }

            @Throws(IOException::class)
            override fun read(b: ByteArray, off: Int, len: Int): Int {
                val ret = inStream.read(b, off, len)
                if (ret == -1) {
                    eosReached = true
                }
                return ret
            }

        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy