Migrate to HttpServer (#76)

* Migrate to Fibry

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Improve error handling

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Update models and fields

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Enable fibry server

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Update .gitignore

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Convert captcha class to object

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Rollback error handling

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Update models

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Migrate to sun http server

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Refactor: Linter and formatter

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>

* Remove redundant dependancy

Signed-off-by: Rahul Rudragoudar <rr83019@gmail.com>
This commit is contained in:
Rahul Rudragoudar 2021-04-11 17:57:15 +05:30 committed by GitHub
parent ff666396b8
commit 4612dfa1cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 126 additions and 3101 deletions

1
.gitignore vendored
View File

@ -8,6 +8,7 @@
.bloop
.metals
.vscode
.bsp
# for python test env
/testEnv/

File diff suppressed because it is too large Load Diff

View File

@ -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()
}
}

View File

@ -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()
@ -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()) }
}
}

View File

@ -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
@ -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)
@ -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) {

View File

@ -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")
}

View File

@ -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)

View File

@ -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)

View File

@ -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],

View File

@ -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(
"/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"
)
private def getRequestJson(ex: HttpExchange): JValue = {
val requestBody = ex.getRequestBody
val bytes = requestBody.readAllBytes
val string = bytes.map(_.toChar).mkString
parse(string)
}
host.addContext(
"/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"
)
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)
}
}
}
host.addContext(
"/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"
)
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",
(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()
}
}
)
makeApiWorker(
"/v1/media",
(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()
}
}
)
makeApiWorker(
"/v1/answer",
(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()
}
}
)
}