Skip to content

Commit

Permalink
Merge pull request #627 from hexagonkt/develop
Browse files Browse the repository at this point in the history
Add utility functions
  • Loading branch information
jaguililla authored May 18, 2023
2 parents b52ac89 + 898529e commit 78c6cb2
Show file tree
Hide file tree
Showing 20 changed files with 181 additions and 151 deletions.
4 changes: 0 additions & 4 deletions .github/workflows/site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ jobs:
cache: gradle

- name: Build Project
run: ./gradlew build

- name: Diagnose Build
if: failure()
run: ./gradlew --info --stacktrace build

- name: Build Site
Expand Down
6 changes: 3 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ plugins {
id("eclipse")
id("project-report")
id("org.jetbrains.dokka") version("1.8.10")
id("com.github.jk1.dependency-license-report") version("2.1")
id("com.github.jk1.dependency-license-report") version("2.2")
id("org.jetbrains.kotlinx.binary-compatibility-validator") version("0.13.1")
id("org.graalvm.buildtools.native") version("0.9.21") apply(false)
id("org.graalvm.buildtools.native") version("0.9.22") apply(false)
id("io.gitlab.arturbosch.detekt") version("1.22.0") apply(false)
id("me.champeau.jmh") version("0.7.0") apply(false)
id("me.champeau.jmh") version("0.7.1") apply(false)
}

apply(from = "gradle/certificates.gradle")
Expand Down
3 changes: 3 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/Network.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ fun isPortOpened(port: Int): Boolean =
false
}

fun urlOf(url: String): URL =
URI(url).toURL()

fun URL.responseCode(): Int =
try {
(openConnection() as HttpURLConnection).responseCode
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/kotlin/com/hexagonkt/core/media/MediaTypes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.hexagonkt.core.media.MediaTypeGroup.*
import java.io.File
import java.net.URI
import java.net.URL
import java.nio.file.Path
import kotlin.io.path.extension

val MEDIA_TYPE_FORMAT: Regex = """\*|([\w+.-]+)""".toRegex()

Expand Down Expand Up @@ -206,6 +208,9 @@ fun mediaTypeOfOrNull(url: URL): MediaType? =
fun mediaTypeOfOrNull(file: File): MediaType? =
mediaTypeOfOrNull(file.extension)

fun mediaTypeOfOrNull(path: Path): MediaType? =
mediaTypeOfOrNull(path.extension)

fun mediaTypeOfOrNull(extension: String): MediaType? =
MEDIA_TYPES_EXTENSIONS[extension]

Expand All @@ -218,6 +223,9 @@ fun mediaTypeOf(url: URL): MediaType =
fun mediaTypeOf(file: File): MediaType =
mediaTypeOfOrNull(file) ?: error("Media type not found for: '$file' file")

fun mediaTypeOf(path: Path): MediaType =
mediaTypeOfOrNull(path) ?: error("Media type not found for: '$path' extension")

fun mediaTypeOf(extension: String): MediaType =
mediaTypeOfOrNull(extension) ?: error("Media type not found for: '$extension' extension")

Expand Down
51 changes: 25 additions & 26 deletions core/src/test/kotlin/com/hexagonkt/core/NetworkTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,57 +3,56 @@ package com.hexagonkt.core
import java.net.ServerSocket
import kotlin.test.Test
import java.net.InetAddress
import java.net.URL
import java.util.*
import kotlin.test.*

internal class NetworkTest {

// TODO Replace URL accesses by local started HTTP servers
@Test fun `Check URL exists`() {
assert(URL("http://example.com").exists())
assert(URL("https://example.com").exists())
assert(URL("file:README.md").exists())
assert(URL("file:src/test/resources/build.properties").exists())
assert(URL("classpath:locales/data.json").exists())
assert(urlOf("http://example.com").exists())
assert(urlOf("https://example.com").exists())
assert(urlOf("file:README.md").exists())
assert(urlOf("file:src/test/resources/build.properties").exists())
assert(urlOf("classpath:locales/data.json").exists())

assert(!URL("http://example.com/a.txt").exists())
assert(!URL("https://example.com/b.html").exists())
assert(!URL("file:not_existing.txt").exists())
assert(!URL("file:src").exists())
assert(!URL("classpath:data.json").exists())
assert(!urlOf("http://example.com/a.txt").exists())
assert(!urlOf("https://example.com/b.html").exists())
assert(!urlOf("file:not_existing.txt").exists())
assert(!urlOf("file:src").exists())
assert(!urlOf("classpath:data.json").exists())

assert(!URL("ftp://example.com").exists())
assert(!urlOf("ftp://example.com").exists())
}

@Test fun `Check URL first variant`() {
URL("classpath:locales/data.json").let {
urlOf("classpath:locales/data.json").let {
assertEquals(it, it.firstVariant("_it_IT", "_it"))
}
assertEquals(
URL("classpath:locales/data_en_US.json"),
URL("classpath:locales/data.json").firstVariant("_en_US", "_en"),
urlOf("classpath:locales/data_en_US.json"),
urlOf("classpath:locales/data.json").firstVariant("_en_US", "_en"),
)
assertEquals(
URL("classpath:locales/data_en.json"),
URL("classpath:locales/data.json").firstVariant("_en_GB", "_en"),
urlOf("classpath:locales/data_en.json"),
urlOf("classpath:locales/data.json").firstVariant("_en_GB", "_en"),
)
}

@Test fun `Check localized URL`() {
fun locale(language: String, region: String): Locale =
Locale.Builder().setLanguage(language).setRegion(region).build()

URL("classpath:locales/data.json").let {
urlOf("classpath:locales/data.json").let {
assertEquals(it, it.localized(locale("it", "IT")))
}
assertEquals(
URL("classpath:locales/data_en_US.json"),
URL("classpath:locales/data.json").localized(locale("en", "US")),
urlOf("classpath:locales/data_en_US.json"),
urlOf("classpath:locales/data.json").localized(locale("en", "US")),
)
assertEquals(
URL("classpath:locales/data_en.json"),
URL("classpath:locales/data.json").localized(locale("en", "GB")),
urlOf("classpath:locales/data_en.json"),
urlOf("classpath:locales/data.json").localized(locale("en", "GB")),
)
}

Expand All @@ -71,9 +70,9 @@ internal class NetworkTest {
}

@Test fun `URL check works properly`() {
assertTrue { URL("http://example.com").responseSuccessful() }
assertFalse { URL("http://invalid-domain.z").responseSuccessful() }
assertTrue { URL("http://example.com").responseFound() }
assertFalse { URL("http://example.com/nothing").responseFound() }
assertTrue { urlOf("http://example.com").responseSuccessful() }
assertFalse { urlOf("http://invalid-domain.z").responseSuccessful() }
assertTrue { urlOf("http://example.com").responseFound() }
assertFalse { urlOf("http://example.com/nothing").responseFound() }
}
}
11 changes: 11 additions & 0 deletions core/src/test/kotlin/com/hexagonkt/core/media/MediaTypesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.net.URI
import kotlin.IllegalArgumentException
import kotlin.IllegalStateException
import java.net.URL
import java.nio.file.Path
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertNull
Expand Down Expand Up @@ -64,6 +65,16 @@ internal class MediaTypesTest {
assertNull(mediaTypeOfOrNull(File("file.baz")))
assertNull(mediaTypeOfOrNull(URI("http://localhost/file.baz")))
assertNull(mediaTypeOfOrNull(URL("http://localhost/file.baz")))
assertEquals(TEXT_HTML, mediaTypeOfOrNull(Path.of("file.html")))
assertEquals(TEXT_HTML, mediaTypeOfOrNull(Path.of("file.htm")))
assertEquals(APPLICATION_YAML, mediaTypeOfOrNull(Path.of("file.yaml")))
assertEquals(APPLICATION_YAML, mediaTypeOfOrNull(Path.of("file.yml")))
assertEquals(TEXT_HTML, mediaTypeOf(Path.of("file.html")))
assertEquals(TEXT_HTML, mediaTypeOf(Path.of("file.htm")))
assertEquals(APPLICATION_YAML, mediaTypeOf(Path.of("file.yaml")))
assertEquals(APPLICATION_YAML, mediaTypeOf(Path.of("file.yml")))
assertNull(mediaTypeOfOrNull(Path.of("file")))
assertNull(mediaTypeOfOrNull(Path.of("file.baz")))
}

@Test fun `Exception is thrown if the media type is not found`() {
Expand Down
10 changes: 5 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ org.gradle.warning.mode=all
org.gradle.console=plain

# Gradle
version=2.8.4
version=2.8.5
group=com.hexagonkt
description=The atoms of your platform

Expand Down Expand Up @@ -37,19 +37,19 @@ kotlinVersion=1.8.21
dokkaVersion=1.8.10
mockkVersion=1.13.5
junitVersion=5.9.3
gatlingVersion=3.9.3
gatlingVersion=3.9.5
jmhVersion=1.36
mkdocsMaterialVersion=9.1.9
mkdocsMaterialVersion=9.1.13
mermaidDokkaVersion=0.4.4
nativeToolsVersion=0.9.21
nativeToolsVersion=0.9.22

# http_server_servlet
servletVersion=5.0.0
jettyVersion=11.0.15

# http_server_netty
nettyVersion=4.1.92.Final
nettyTcNativeVersion=2.0.60.Final
nettyTcNativeVersion=2.0.61.Final

# logging
slf4jVersion=2.0.7
Expand Down
2 changes: 2 additions & 0 deletions http/src/main/kotlin/com/hexagonkt/http/model/Cookie.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ data class Cookie(
val sameSite: Boolean = true,
val expires: Instant? = null,
) {
val deleted: Boolean by lazy { value == "" && maxAge <= 0L }

init {
require(name.isNotBlank()) { "Cookie name can not be blank: $name" }
}
Expand Down
3 changes: 3 additions & 0 deletions http/src/test/kotlin/com/hexagonkt/http/model/CookieTest.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.hexagonkt.http.model

import org.junit.jupiter.api.Assertions.assertFalse
import kotlin.test.Test
import kotlin.IllegalArgumentException
import kotlin.test.assertEquals
Expand All @@ -18,6 +19,7 @@ internal class CookieTest {
assertEquals("value", cookie.value)
assertEquals(5, cookie.maxAge)
assertTrue(cookie.secure)
assertFalse(cookie.deleted)
}

@Test fun `Cookie can be deleted`() {
Expand All @@ -26,5 +28,6 @@ internal class CookieTest {
assertEquals("", cookie.value)
assertEquals(0, cookie.maxAge)
assertTrue(cookie.secure)
assertTrue(cookie.deleted)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class HandlerBuilder(var handlers: List<HttpHandler> = emptyList()) {
predicate: HttpPredicate = HttpPredicate(),
callback: HttpCallback
) {
use(com.hexagonkt.http.handlers.on(predicate, callback))
use(OnHandler(predicate, callback))
}

fun on(
Expand All @@ -44,22 +44,22 @@ class HandlerBuilder(var handlers: List<HttpHandler> = emptyList()) {
status: HttpStatus? = null,
callback: HttpCallback,
) {
use(com.hexagonkt.http.handlers.on(methods, pattern, exception, status, callback))
use(OnHandler(methods, pattern, exception, status, callback))
}

fun on(method: HttpMethod, pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.on(method, pattern, callback))
use(OnHandler(method, pattern, callback))
}

fun on(pattern: String, callback: HttpCallback) {
use(com.hexagonkt.http.handlers.on(pattern, callback))
use(OnHandler(pattern, callback))
}

fun filter(
predicate: HttpPredicate = HttpPredicate(),
callback: HttpCallback
) {
use(com.hexagonkt.http.handlers.filter(predicate, callback))
use(FilterHandler(predicate, callback))
}

fun filter(
Expand All @@ -70,23 +70,23 @@ class HandlerBuilder(var handlers: List<HttpHandler> = emptyList()) {
callback: HttpCallback,
) {
use(
com.hexagonkt.http.handlers.filter(methods, pattern, exception, status, callback)
FilterHandler(methods, pattern, exception, status, callback)
)
}

fun filter(method: HttpMethod, pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.filter(method, pattern, callback))
use(FilterHandler(method, pattern, callback))
}

fun filter(pattern: String, callback: HttpCallback) {
use(com.hexagonkt.http.handlers.filter(pattern, callback))
use(FilterHandler(pattern, callback))
}

fun after(
predicate: HttpPredicate = HttpPredicate(),
callback: HttpCallback
) {
use(com.hexagonkt.http.handlers.after(predicate, callback))
use(AfterHandler(predicate, callback))
}

fun after(
Expand All @@ -96,65 +96,65 @@ class HandlerBuilder(var handlers: List<HttpHandler> = emptyList()) {
status: HttpStatus? = null,
callback: HttpCallback,
) {
use(com.hexagonkt.http.handlers.after(methods, pattern, exception, status, callback))
use(AfterHandler(methods, pattern, exception, status, callback))
}

fun after(method: HttpMethod, pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.after(method, pattern, callback))
use(AfterHandler(method, pattern, callback))
}

fun after(pattern: String, callback: HttpCallback) {
use(com.hexagonkt.http.handlers.after(pattern, callback))
use(AfterHandler(pattern, callback))
}

fun <T : Exception> exception(
exception: KClass<T>? = null,
status: HttpStatus? = null,
callback: HttpExceptionCallback<T>,
) {
use(com.hexagonkt.http.handlers.exception(exception, status, callback))
use(Exception(exception, status, callback))
}

inline fun <reified T : Exception> exception(
status: HttpStatus? = null,
noinline callback: HttpExceptionCallback<T>,
) {
use(com.hexagonkt.http.handlers.exception(T::class, status, callback))
use(Exception(T::class, status, callback))
}

fun get(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.get(pattern, callback))
use(Get(pattern, callback))
}

fun ws(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.ws(pattern, callback))
use(Ws(pattern, callback))
}

fun head(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.head(pattern, callback))
use(Head(pattern, callback))
}

fun post(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.post(pattern, callback))
use(Post(pattern, callback))
}

fun put(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.put(pattern, callback))
use(Put(pattern, callback))
}

fun delete(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.delete(pattern, callback))
use(Delete(pattern, callback))
}

fun trace(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.trace(pattern, callback))
use(Trace(pattern, callback))
}

fun options(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.options(pattern, callback))
use(Options(pattern, callback))
}

fun patch(pattern: String = "", callback: HttpCallback) {
use(com.hexagonkt.http.handlers.patch(pattern, callback))
use(Patch(pattern, callback))
}
}
Loading

0 comments on commit 78c6cb2

Please sign in to comment.