Skip to content

Commit

Permalink
Migrate to HttpServer (#76)
Browse files Browse the repository at this point in the history
* Migrate to Fibry

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Improve error handling

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Update models and fields

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Enable fibry server

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Update .gitignore

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Convert captcha class to object

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Rollback error handling

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Update models

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Migrate to sun http server

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Refactor: Linter and formatter

Signed-off-by: Rahul Rudragoudar <[email protected]>

* Remove redundant dependancy

Signed-off-by: Rahul Rudragoudar <[email protected]>
  • Loading branch information
rr83019 committed Apr 11, 2021
1 parent ff66639 commit 4612dfa
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 3,100 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
.bloop
.metals
.vscode
.bsp

# for python test env
/testEnv/
Expand Down
3,042 changes: 0 additions & 3,042 deletions src/main/java/lc/server/HTTPServer.java

This file was deleted.

6 changes: 2 additions & 4 deletions src/main/scala/lc/Main.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
package lc

import lc.core.{Captcha, CaptchaProviders}
import lc.core.CaptchaProviders
import lc.server.Server
import lc.background.BackgroundTask
import lc.core.Config

object LCFramework {
def main(args: scala.Array[String]): Unit = {
val captcha = new Captcha()
val server = new Server(port = Config.port, captcha = captcha)
val backgroundTask = new BackgroundTask(
captcha = captcha,
throttle = Config.throttle,
timeLimit = Config.captchaExpiryTimeLimit
)
backgroundTask.beginThread(delay = Config.threadDelay)
val server = new Server(port = Config.port)
server.start()
}
}
Expand Down
7 changes: 3 additions & 4 deletions src/main/scala/lc/background/taskThread.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
import lc.core.Captcha
import lc.core.{Parameters, Size}

class BackgroundTask(captcha: Captcha, throttle: Int, timeLimit: Int) {
class BackgroundTask(throttle: Int, timeLimit: Int) {

private val task = new Runnable {
def run(): Unit = {
try {

val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
mapIdGCPstmt.setInt(1, timeLimit)
mapIdGCPstmt.executeUpdate()
Expand All @@ -23,10 +22,10 @@ class BackgroundTask(captcha: Captcha, throttle: Int, timeLimit: Int) {
if (imageNum.next())
throttleIn = (throttleIn - imageNum.getInt("total"))
while (0 < throttleIn) {
captcha.generateChallenge(Parameters("medium", "image/png", "text", Option(Size(0, 0))))
Captcha.generateChallenge(Parameters("medium", "image/png", "text", Option(Size(0, 0))))
throttleIn -= 1
}
} catch { case e: Exception => println(e) }
} catch { case exception: Exception => println(exception.getStackTrace()) }
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/main/scala/lc/core/captcha.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package lc.core

import java.sql.{Blob, ResultSet}
import java.sql.ResultSet
import java.util.UUID
import java.io.ByteArrayInputStream
import lc.database.Statements
import lc.core.CaptchaProviders
import lc.captchas.interfaces.ChallengeProvider
import java.sql.Blob

class Captcha {

object Captcha {

def getCaptcha(id: Id): Array[Byte] = {
var image: Array[Byte] = null
Expand All @@ -32,7 +33,6 @@ class Captcha {

def generateChallenge(param: Parameters): Int = {
val provider = CaptchaProviders.getProvider(param)
if (!provider.isInstanceOf[ChallengeProvider]) return -1
val providerId = provider.getId()
val challenge = provider.returnChallenge()
val blob = new ByteArrayInputStream(challenge.content)
Expand Down Expand Up @@ -68,7 +68,7 @@ class Captcha {
return false
}

def getChallenge(param: Parameters): ChallengeResult = {
def getChallenge(param: Parameters): ChallengeResult = {
try {
val validParam = validateParam(param)
if (validParam) {
Expand Down
3 changes: 2 additions & 1 deletion src/main/scala/lc/core/captchaFields.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ object ErrorMessageEnum extends Enumeration {
type ErrorMessage = Value

val SMW: Value = Value("Oops, something went worng!")
val INVALID_PARAM: Value = Value("Invalid Pramaters")
val INVALID_PARAM: Value = Value("Parameters invalid or missing")
val NO_CAPTCHA: Value = Value("No captcha for the provided parameters")
val BAD_METHOD: Value = Value("Bad request method")
}
1 change: 1 addition & 0 deletions src/main/scala/lc/core/captchaProviders.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ object CaptchaProviders {

def getProvider(param: Parameters): ChallengeProvider = {
val providerConfig = filterProviderByParam(param).toList
if (providerConfig.length == 0) throw new NoSuchElementException(ErrorMessageEnum.NO_CAPTCHA.toString)
val randomIndex = getNextRandomInt(providerConfig.length)
val providerIndex = providerConfig(randomIndex)._1
val selectedProvider = providers(providerIndex)
Expand Down
7 changes: 6 additions & 1 deletion src/main/scala/lc/core/config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ object Config {
configFile.close
configFileContent
} catch {
case _: FileNotFoundException =>
case _: FileNotFoundException => {
val configFileContent = getDefaultConfig()
val configFile = new PrintWriter(new File(configFilePath))
configFile.write(configFileContent)
configFile.close
configFileContent
}
case exception: Exception => {
println(exception.getStackTrace)
throw new Exception(exception.getMessage)
}
}

private val configJson = parse(configString)
Expand Down
4 changes: 3 additions & 1 deletion src/main/scala/lc/core/models.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ sealed trait ChallengeResult
case class Size(height: Int, width: Int)
case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
case class Id(id: String) extends ChallengeResult
case class Error(message: String) extends ChallengeResult
case class Image(image: Array[Byte]) extends ChallengeResult
case class Answer(answer: String, id: String)
case class Result(result: String)
case class Error(message: String) extends ChallengeResult
case class Response(statusCode: Int, message: Array[Byte])
case class CaptchaConfig(
name: String,
allowedLevels: List[String],
Expand Down
144 changes: 102 additions & 42 deletions src/main/scala/lc/server/Server.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,59 +4,119 @@ import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods.parse
import org.json4s.jackson.Serialization.write
import lc.core.Captcha
import lc.core.{Parameters, Id, Answer}
import lc.server.HTTPServer
import lc.core.ErrorMessageEnum
import lc.core.{Parameters, Id, Answer, Response}
import org.json4s.JsonAST.JValue
import com.sun.net.httpserver.{HttpServer, HttpExchange}
import java.net.InetSocketAddress

class Server(port: Int, captcha: Captcha) {
val server = new HTTPServer(port)
val host: HTTPServer.VirtualHost = server.getVirtualHost(null)
class Server(port: Int) {

implicit val formats: DefaultFormats.type = DefaultFormats
val server: HttpServer = HttpServer.create(new InetSocketAddress(port), 32)

host.addContext(
private def getRequestJson(ex: HttpExchange): JValue = {
val requestBody = ex.getRequestBody
val bytes = requestBody.readAllBytes
val string = bytes.map(_.toChar).mkString
parse(string)
}

private def getPathParameter(ex: HttpExchange): String = {
try {
val uri = ex.getRequestURI.toString
val param = uri.split("\\?")(1)
param.split("=")(1)
} catch {
case exception: ArrayIndexOutOfBoundsException => {
println(exception.getStackTrace)
throw new Exception(ErrorMessageEnum.INVALID_PARAM.toString)
}
}
}

private def sendResponse(statusCode: Int, response: Array[Byte], ex: HttpExchange): Unit = {
ex.sendResponseHeaders(statusCode, response.length)
val os = ex.getResponseBody
os.write(response)
os.close
}

private def getException(exception: Exception): Response = {
println(exception.printStackTrace)
val message = ("message" -> exception.getMessage)
val messageByte = write(message).getBytes
Response(500, messageByte)
}

private def getBadRequestError(): Response = {
val message = ("message" -> ErrorMessageEnum.BAD_METHOD.toString)
Response(405, write(message).getBytes)
}

private def makeApiWorker(path: String, f: (String, HttpExchange) => Response): Unit = {
server.createContext(
path,
ex => {
val requestMethod = ex.getRequestMethod
val response =
try {
f(requestMethod, ex)
} catch {
case exception: Exception => {
getException(exception)
}
}
sendResponse(statusCode = response.statusCode, response = response.message, ex = ex)
}
)
}

def start(): Unit = {
println("Starting server on port:" + port)
server.start()
}

makeApiWorker(
"/v1/captcha",
(req, resp) => {
val body = req.getJson()
val json = parse(body)
val param = json.extract[Parameters]
val id = captcha.getChallenge(param)
resp.getHeaders().add("Content-Type", "application/json")
resp.send(200, write(id))
0
},
"POST"
(method: String, ex: HttpExchange) => {
if (method == "POST") {
val json = getRequestJson(ex)
val param = json.extract[Parameters]
val id = Captcha.getChallenge(param)
Response(200, write(id).getBytes)
} else {
getBadRequestError()
}
}
)

host.addContext(
makeApiWorker(
"/v1/media",
(req, resp) => {
val params = req.getParams()
val id = Id(params.get("id"))
val image = captcha.getCaptcha(id)
resp.getHeaders().add("Content-Type", "image/png")
resp.send(200, image)
0
},
"GET"
(method: String, ex: HttpExchange) => {
if (method == "GET") {
val param = getPathParameter(ex)
val id = Id(param)
val image = Captcha.getCaptcha(id)
Response(200, image)
} else {
getBadRequestError()
}
}
)

host.addContext(
makeApiWorker(
"/v1/answer",
(req, resp) => {
val body = req.getJson()
val json = parse(body)
val answer = json.extract[Answer]
val result = captcha.checkAnswer(answer)
resp.getHeaders().add("Content-Type", "application/json")
resp.send(200, write(result))
0
},
"POST"
(method: String, ex: HttpExchange) => {
if (method == "POST") {
val json = getRequestJson(ex)
val answer = json.extract[Answer]
val result = Captcha.checkAnswer(answer)
Response(200, write(result).getBytes)
} else {
getBadRequestError()
}
}
)

def start(): Unit = {
println("Starting server on port:" + port)
server.start()
}

}

0 comments on commit 4612dfa

Please sign in to comment.