Skip to content

Commit

Permalink
Add support for all HTTP methods
Browse files Browse the repository at this point in the history
  • Loading branch information
LukynkaCZE committed Mar 30, 2024
1 parent a9a6012 commit 1b727ed
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 63 deletions.
51 changes: 20 additions & 31 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@ plugins {
}

group = "cz.lukynka"
version = project.property("version").toString()
version = "1.0"

val githubUser: String = project.findProperty("gpr.user") as String? ?: System.getenv("GITHUB_USER")
val githubPassword: String = project.findProperty("gpr.key") as String? ?: System.getenv("GITHUB_TOKEN")

repositories {
mavenCentral()
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/LukynkaCZE/PrettyLog")
credentials {username = githubUser; password = githubPassword}
}
}

dependencies {
testImplementation(kotlin("test"))
implementation("cz.lukynka:pretty-log:1.2")
}

tasks.test {
Expand All @@ -32,40 +41,20 @@ application {
mainClass.set("MainKt")
}

publishing {
publications {
create<MavenPublication>("maven") {
groupId = group.toString()
artifactId = "LKWS"
version = version

from(components["java"])
}
}
publishing {
repositories {
maven {
name = "test"
url = layout.buildDirectory.dir("/pom/").get().asFile.toURI()
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/LukynkaCZE/LKWS")
credentials {username = githubUser; password = githubPassword}
}
}
}

tasks.jar {
val pomFile = File("./build/pom/cz/lukynka/LKWS/0.4/LKWS-0.4.pom")
val dotFile = File("./LKWS-0.4.pom")
val xmlFile = File("./pom.xml")
if(pomFile.exists()) {
dotFile.createNewFile()
xmlFile.createNewFile()
dotFile.writeText(pomFile.readText())
xmlFile.writeText(pomFile.readText())
}
project.logger.lifecycle(dotFile.exists().toString())
project.logger.lifecycle(dotFile.path)
from(dotFile.path) {
into("META-INF/maven/cz.lukynka/LKWS-0.4")
}
from(dotFile.path) {
into("/")
publications {
register<MavenPublication>("gpr") {
artifactId = "lkws"
version = version
from(components["java"])
}
}
}
1 change: 0 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
kotlin.code.style=official
version=0.4
2 changes: 1 addition & 1 deletion src/main/kotlin/Endpoint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ data class Error(
)


enum class EndpointType {
enum class EndpointType() {
GET,
PUT,
POST,
Expand Down
67 changes: 52 additions & 15 deletions src/main/kotlin/LightweightWebServer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.sun.net.httpserver.HttpExchange
import com.sun.net.httpserver.HttpHandler
import com.sun.net.httpserver.HttpServer
import responses.ErrorResponse
import responses.GetResponse
import responses.Response
import java.net.InetSocketAddress

Expand All @@ -19,34 +18,55 @@ class LightweightWebServer(port: Int = 7270) {
try {
server = HttpServer.create(InetSocketAddress(port), 0)
} catch (ex: Exception) {
ex.printStackTrace()
log(ex)
throw Exception(ex.message)
}

server.createContext("/", Handler())
server.executor = null
server.start()

println("Running Lightweight Web Server on port $port")
log("Running Lightweight Web Server on port $port", LogType.SUCCESS)
}

fun end(exitCode: Int = 0) {
server.stop(exitCode)
println("Lightweight Web Server stopped with exit code $exitCode")
log("Lightweight Web Server stopped with exit code $exitCode", LogType.ERROR)
}

fun get(path: String, function: (res: GetResponse) -> Unit) {
private fun path(path: String, type: EndpointType, function: (res: Response) -> Unit) {
val endpointPath = if(path.startsWith("/")) path else "/$path"
endpointPath.removeSuffix("/")

val tokens = endpointPath.split("/")
val replacables = mutableListOf<Int>()
val urlParams = mutableListOf<Int>()

tokens.forEachIndexed { index, it ->
if(it.startsWith("{") && it.endsWith("}"))
replacables.add(index)
urlParams.add(index)
}
endpoints.add(Endpoint(endpointPath, function as (Response) -> Unit, EndpointType.GET, replacables))

endpoints.add(Endpoint(endpointPath, function, type, urlParams))
}

fun get(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.GET, function)
}
fun put(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.PUT, function)
}
fun post(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.POST, function)
}
fun patch(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.PATCH, function)
}
fun delete(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.DELETE, function)
}
fun options(path: String, function: (res: Response) -> Unit) {
path(path, EndpointType.OPTIONS, function)
}
fun error(function: (res: ErrorResponse) -> Unit) {
errorResponse = function
}
Expand All @@ -56,14 +76,15 @@ class LightweightWebServer(port: Int = 7270) {
try {
val path = exchange.requestURI.path

val type = EndpointType.valueOf(exchange.requestMethod);

// Throw if there is no endpoint with that path
val matchingEndpoint = findMatchingEndpoint(path)
val endpoint: Endpoint = matchingEndpoint.first ?: throw Exception("No such path as `$path` exists!")
val matchingEndpoint = findMatchingEndpoint(path, type)

val response = when(endpoint.type) {
EndpointType.GET -> GetResponse(exchange)
else -> Response(exchange)
}
if(matchingEndpoint.first == null || matchingEndpoint.first!!.type != type) throw Exception("No such path as `$path` exists for type $type")
val endpoint = matchingEndpoint.first!!

val response = Response(exchange)

//Get response and execute the Unit in Endpoint whose path matches the uri path
response.URLParameters = matchingEndpoint.second
Expand All @@ -77,17 +98,33 @@ class LightweightWebServer(port: Int = 7270) {
// Default response if `errorResponse` is not set by the user
if(errorResponse == null) {
response.respond("Something went wrong: $exception", 500)
var isPathError = false
if(exception.message != null) {
val split = exception.message!!.split("`")
if (split[0].contains("No such path as")) {
val splitType = split[2].split("type ")
log("${exchange.remoteAddress} tried to access ${split[1]} but that path with type ${splitType[1]} does not exist", LogType.WARNING)
isPathError = true
}
}
if(!isPathError) {
log(exception)
}
return
}

errorResponse!!.invoke(response)
}
}

private fun findMatchingEndpoint(path: String): Pair<Endpoint?, MutableMap<String, String>> {
private fun findMatchingEndpoint(path: String, type: EndpointType): Pair<Endpoint?, MutableMap<String, String>> {
val replaceableValues = mutableMapOf<String, String>()

val matchedEndpoint = endpoints.firstOrNull { endpoint ->
if (endpoint.type != type) {
return@firstOrNull false
}

val endpointPath = endpoint.path.split("/")
val requestPath = path.split("/")

Expand Down
2 changes: 2 additions & 0 deletions src/main/kotlin/Main.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@

fun main(args: Array<String>) {
LoggerSettings.loggerStyle = LoggerStyle.BRACKET_PREFIX_WHITE_TEXT
//hello person reading this. I hope you are having a nice day <3
}
7 changes: 0 additions & 7 deletions src/main/kotlin/responses/GetResponse.kt

This file was deleted.

7 changes: 0 additions & 7 deletions src/main/kotlin/responses/PostResponse.kt

This file was deleted.

2 changes: 1 addition & 1 deletion src/main/kotlin/responses/Response.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ open class Response(var httpExchange: HttpExchange) {
val os = httpExchange.responseBody

responseHeaders.forEach { httpExchange.responseHeaders.add(it.first, it.second) }
httpExchange.sendResponseHeaders(this.responseStatusCode, byteArray.size.toLong())

httpExchange.sendResponseHeaders(this.responseStatusCode, byteArray.size.toLong())
os.write(byteArray)
os.close()
}
Expand Down
43 changes: 43 additions & 0 deletions src/test/kotlin/TypeTests.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals

class TypeTests {

init {
newServer()
}

companion object {
private var server: LightweightWebServer? = null

private fun newServer() {
server?.end()
server = LightweightWebServer(6900)
}

@JvmStatic
@AfterAll
fun `end server`(): Unit {
server?.end()
}
}

@Test
fun `test types pass`() {
server?.get("/body") {
it.respond("yep :3")
}
val response = testRequest("/body", "you there?")
assertEquals(200, response.statusCode())
}

@Test
fun `test types fail`() {
server?.post("/body") {
it.respond("yep :3")
}
val response = testRequest("/body", "you there?")
assertEquals(500, response.statusCode())
}
}

0 comments on commit 1b727ed

Please sign in to comment.