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:
parent
ff666396b8
commit
4612dfa1cd
|
@ -8,6 +8,7 @@
|
|||
.bloop
|
||||
.metals
|
||||
.vscode
|
||||
.bsp
|
||||
|
||||
# for python test env
|
||||
/testEnv/
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue