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 .bloop
.metals .metals
.vscode .vscode
.bsp
# for python test env # for python test env
/testEnv/ /testEnv/

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,18 @@
package lc package lc
import lc.core.{Captcha, CaptchaProviders} import lc.core.CaptchaProviders
import lc.server.Server import lc.server.Server
import lc.background.BackgroundTask import lc.background.BackgroundTask
import lc.core.Config import lc.core.Config
object LCFramework { object LCFramework {
def main(args: scala.Array[String]): Unit = { def main(args: scala.Array[String]): Unit = {
val captcha = new Captcha()
val server = new Server(port = Config.port, captcha = captcha)
val backgroundTask = new BackgroundTask( val backgroundTask = new BackgroundTask(
captcha = captcha,
throttle = Config.throttle, throttle = Config.throttle,
timeLimit = Config.captchaExpiryTimeLimit timeLimit = Config.captchaExpiryTimeLimit
) )
backgroundTask.beginThread(delay = Config.threadDelay) backgroundTask.beginThread(delay = Config.threadDelay)
val server = new Server(port = Config.port)
server.start() server.start()
} }
} }

View File

@ -5,12 +5,11 @@ import java.util.concurrent.{ScheduledThreadPoolExecutor, TimeUnit}
import lc.core.Captcha import lc.core.Captcha
import lc.core.{Parameters, Size} import lc.core.{Parameters, Size}
class BackgroundTask(captcha: Captcha, throttle: Int, timeLimit: Int) { class BackgroundTask(throttle: Int, timeLimit: Int) {
private val task = new Runnable { private val task = new Runnable {
def run(): Unit = { def run(): Unit = {
try { try {
val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt val mapIdGCPstmt = Statements.tlStmts.get.mapIdGCPstmt
mapIdGCPstmt.setInt(1, timeLimit) mapIdGCPstmt.setInt(1, timeLimit)
mapIdGCPstmt.executeUpdate() mapIdGCPstmt.executeUpdate()
@ -23,10 +22,10 @@ class BackgroundTask(captcha: Captcha, throttle: Int, timeLimit: Int) {
if (imageNum.next()) if (imageNum.next())
throttleIn = (throttleIn - imageNum.getInt("total")) throttleIn = (throttleIn - imageNum.getInt("total"))
while (0 < throttleIn) { 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 throttleIn -= 1
} }
} catch { case e: Exception => println(e) } } catch { case exception: Exception => println(exception.getStackTrace()) }
} }
} }

View File

@ -1,13 +1,14 @@
package lc.core package lc.core
import java.sql.{Blob, ResultSet} import java.sql.ResultSet
import java.util.UUID import java.util.UUID
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import lc.database.Statements import lc.database.Statements
import lc.core.CaptchaProviders import lc.core.CaptchaProviders
import lc.captchas.interfaces.ChallengeProvider import java.sql.Blob
class Captcha {
object Captcha {
def getCaptcha(id: Id): Array[Byte] = { def getCaptcha(id: Id): Array[Byte] = {
var image: Array[Byte] = null var image: Array[Byte] = null
@ -32,7 +33,6 @@ class Captcha {
def generateChallenge(param: Parameters): Int = { def generateChallenge(param: Parameters): Int = {
val provider = CaptchaProviders.getProvider(param) val provider = CaptchaProviders.getProvider(param)
if (!provider.isInstanceOf[ChallengeProvider]) return -1
val providerId = provider.getId() val providerId = provider.getId()
val challenge = provider.returnChallenge() val challenge = provider.returnChallenge()
val blob = new ByteArrayInputStream(challenge.content) val blob = new ByteArrayInputStream(challenge.content)
@ -68,7 +68,7 @@ class Captcha {
return false return false
} }
def getChallenge(param: Parameters): ChallengeResult = { def getChallenge(param: Parameters): ChallengeResult = {
try { try {
val validParam = validateParam(param) val validParam = validateParam(param)
if (validParam) { if (validParam) {

View File

@ -37,6 +37,7 @@ object ErrorMessageEnum extends Enumeration {
type ErrorMessage = Value type ErrorMessage = Value
val SMW: Value = Value("Oops, something went worng!") 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 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 = { def getProvider(param: Parameters): ChallengeProvider = {
val providerConfig = filterProviderByParam(param).toList val providerConfig = filterProviderByParam(param).toList
if (providerConfig.length == 0) throw new NoSuchElementException(ErrorMessageEnum.NO_CAPTCHA.toString)
val randomIndex = getNextRandomInt(providerConfig.length) val randomIndex = getNextRandomInt(providerConfig.length)
val providerIndex = providerConfig(randomIndex)._1 val providerIndex = providerConfig(randomIndex)._1
val selectedProvider = providers(providerIndex) val selectedProvider = providers(providerIndex)

View File

@ -18,12 +18,17 @@ object Config {
configFile.close configFile.close
configFileContent configFileContent
} catch { } catch {
case _: FileNotFoundException => case _: FileNotFoundException => {
val configFileContent = getDefaultConfig() val configFileContent = getDefaultConfig()
val configFile = new PrintWriter(new File(configFilePath)) val configFile = new PrintWriter(new File(configFilePath))
configFile.write(configFileContent) configFile.write(configFileContent)
configFile.close configFile.close
configFileContent configFileContent
}
case exception: Exception => {
println(exception.getStackTrace)
throw new Exception(exception.getMessage)
}
} }
private val configJson = parse(configString) private val configJson = parse(configString)

View File

@ -4,9 +4,11 @@ sealed trait ChallengeResult
case class Size(height: Int, width: Int) case class Size(height: Int, width: Int)
case class Parameters(level: String, media: String, input_type: String, size: Option[Size]) case class Parameters(level: String, media: String, input_type: String, size: Option[Size])
case class Id(id: String) extends ChallengeResult 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 Answer(answer: String, id: String)
case class Result(result: String) case class Result(result: String)
case class Error(message: String) extends ChallengeResult
case class Response(statusCode: Int, message: Array[Byte])
case class CaptchaConfig( case class CaptchaConfig(
name: String, name: String,
allowedLevels: List[String], allowedLevels: List[String],

View File

@ -4,59 +4,119 @@ import org.json4s.DefaultFormats
import org.json4s.jackson.JsonMethods.parse import org.json4s.jackson.JsonMethods.parse
import org.json4s.jackson.Serialization.write import org.json4s.jackson.Serialization.write
import lc.core.Captcha import lc.core.Captcha
import lc.core.{Parameters, Id, Answer} import lc.core.ErrorMessageEnum
import lc.server.HTTPServer 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) { class Server(port: Int) {
val server = new HTTPServer(port)
val host: HTTPServer.VirtualHost = server.getVirtualHost(null)
implicit val formats: DefaultFormats.type = DefaultFormats implicit val formats: DefaultFormats.type = DefaultFormats
val server: HttpServer = HttpServer.create(new InetSocketAddress(port), 32)
host.addContext( private def getRequestJson(ex: HttpExchange): JValue = {
"/v1/captcha", val requestBody = ex.getRequestBody
(req, resp) => { val bytes = requestBody.readAllBytes
val body = req.getJson() val string = bytes.map(_.toChar).mkString
val json = parse(body) parse(string)
val param = json.extract[Parameters] }
val id = captcha.getChallenge(param)
resp.getHeaders().add("Content-Type", "application/json")
resp.send(200, write(id))
0
},
"POST"
)
host.addContext( private def getPathParameter(ex: HttpExchange): String = {
"/v1/media", try {
(req, resp) => { val uri = ex.getRequestURI.toString
val params = req.getParams() val param = uri.split("\\?")(1)
val id = Id(params.get("id")) param.split("=")(1)
val image = captcha.getCaptcha(id) } catch {
resp.getHeaders().add("Content-Type", "image/png") case exception: ArrayIndexOutOfBoundsException => {
resp.send(200, image) println(exception.getStackTrace)
0 throw new Exception(ErrorMessageEnum.INVALID_PARAM.toString)
}, }
"GET" }
) }
host.addContext( private def sendResponse(statusCode: Int, response: Array[Byte], ex: HttpExchange): Unit = {
"/v1/answer", ex.sendResponseHeaders(statusCode, response.length)
(req, resp) => { val os = ex.getResponseBody
val body = req.getJson() os.write(response)
val json = parse(body) os.close
val answer = json.extract[Answer] }
val result = captcha.checkAnswer(answer)
resp.getHeaders().add("Content-Type", "application/json") private def getException(exception: Exception): Response = {
resp.send(200, write(result)) println(exception.printStackTrace)
0 val message = ("message" -> exception.getMessage)
}, val messageByte = write(message).getBytes
"POST" 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 = { def start(): Unit = {
println("Starting server on port:" + port) println("Starting server on port:" + port)
server.start() 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()
}
}
)
} }