Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: heroku-devcent…
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 155 lines (136 sloc) 5.658 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
package com.typesafe.webwords.common

import java.net.URL
import org.eclipse.jetty.server.handler.AbstractHandler
import org.eclipse.jetty.server.Request
import org.eclipse.jetty.server.Server
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import java.net.URI
import java.util.Random
import org.eclipse.jetty.util.IO

class TestHttpServer(extraResourceClass: Option[Class[_]] = None) {
    private var maybeServer: Option[Server] = None

    // this bool is just to detect bugs, it's not thread-safe
    private var latencyPushed = false
    // this is max latency (we pick a random amount up to this)
    private var latencyMs: Int = 0

    private val random = new Random()
    private def randomInRange(min: Int, max: Int) = {
        random.nextInt(max - min + 1) + min
    }

    private def writeResource(resource: String, response: HttpServletResponse) = {
        val url = extraResourceClass flatMap { klass =>
            Option(klass.getResource(resource))
        } getOrElse {
            Option(getClass.getResource(resource)) getOrElse {
                throw new Exception("resource not found: '" + resource + "'")
            }
        }

        val in = url.openStream()
        val out = response.getOutputStream()
        IO.copy(in, out)
    }

    private class TestHandler extends AbstractHandler {
        override def handle(target: String, jettyRequest: Request, servletRequest: HttpServletRequest, response: HttpServletResponse) = {
            if (latencyMs > 0) {
                Thread.sleep(randomInRange((latencyMs * 0.1).intValue, latencyMs))
            }

            target match {
                case "/hello" => {
                    response.setContentType("text/plain")
                    response.setStatus(HttpServletResponse.SC_OK)
                    response.getWriter().println("Hello")
                    jettyRequest.setHandled(true)
                }
                case "/echo" => {
                    val what = servletRequest.getParameter("what")
                    if (what != null) {
                        response.setContentType("text/plain")
                        response.setStatus(HttpServletResponse.SC_OK)
                        response.getWriter().print(what)
                    } else {
                        response.setStatus(HttpServletResponse.SC_BAD_REQUEST)
                    }
                    jettyRequest.setHandled(true)
                }
                case resourcePath: String if resourcePath.startsWith("/resource/") =>
                    val resource = resourcePath.substring("/resource/".length)
                    try {
                        if (resource.endsWith(".html"))
                            response.setContentType("text/html")
                        else
                            throw new Exception("Unable to serve resource with unknown type")
                        writeResource(resource, response)
                        response.setStatus(HttpServletResponse.SC_OK)
                    } catch {
                        case e: Throwable =>
                            response.setStatus(HttpServletResponse.SC_NOT_FOUND)
                            response.setContentType("text/plain")
                            val writer = response.getWriter
                            writer.append(e.getClass.getName + ": " + e.getMessage + "\n")
                            // (if this were an internet-facing web server we might not want this
                            // since it could leak information, of course)
                            writer.append(e.getStackTraceString)
                            writer.append("\n")
                    }
                    jettyRequest.setHandled(true)
                case _ =>
                    response.setStatus(HttpServletResponse.SC_NOT_FOUND)
                    jettyRequest.setHandled(true)
            }
        }
    }

    def start(): Unit = {
        if (maybeServer.isDefined)
            throw new IllegalStateException("can't start http server twice")

        import scala.collection.JavaConverters._

        val handler = new TestHandler

        val server = new Server(0)
        server.setHandler(handler)
        server.start()

        maybeServer = Some(server)
    }

    def stop() = {
        maybeServer foreach { server =>
            server.stop()
        }
        maybeServer = None
    }

    def url: URL = {
        maybeServer map { server =>
            val ports = for (c <- server.getConnectors()) yield c.getLocalPort()
            val port = ports.head
            new URL("http://127.0.0.1:" + port + "/")
        } get
    }

    def resolve(path: String): URL = {
        url.toURI.resolve(path).toURL
    }

    def resolve(path: String, params: String*): URL = {
        val uri = url.toURI.resolve(path)
        val pairs = params.sliding(2) map { p =>
            require(p.length == 2)
            java.net.URLEncoder.encode(p(0), "utf-8") + "=" +
                java.net.URLEncoder.encode(p(1), "utf-8")
        }
        val query = pairs.mkString("", "&", "")
        val uriWithQuery = new URI(uri.getScheme(),
            uri.getAuthority(),
            uri.getPath(),
            query,
            uri.getFragment())
        uriWithQuery.toURL
    }

    def withRandomLatency[T](maxMs: Int)(body: => T): T = {
        require(!latencyPushed)
        val old = latencyMs
        latencyPushed = true // just to detect bugs
        latencyMs = maxMs
        try {
            body
        } finally {
            latencyMs = old
            latencyPushed = false
        }
    }
}
Something went wrong with that request. Please try again.