├── javalin-ssl └── src │ ├── test │ ├── resources │ │ ├── server │ │ │ ├── wrong.pem │ │ │ ├── norway.jks │ │ │ ├── norway.p12 │ │ │ ├── keystore.jks │ │ │ ├── keystore.p12 │ │ │ ├── malformed.p12 │ │ │ ├── cert.crt │ │ │ ├── malformed.jks │ │ │ ├── passwordless.key │ │ │ └── encrypted.key │ │ ├── client │ │ │ ├── cert.der │ │ │ ├── cert.jks │ │ │ ├── cert.p12 │ │ │ ├── cert.pem │ │ │ ├── cert.p7b │ │ │ └── key.pem │ │ └── ca │ │ │ ├── keystores │ │ │ ├── client.p12 │ │ │ ├── server.p12 │ │ │ ├── issuer-ca.p12 │ │ │ └── root-ca.p12 │ │ │ ├── client-nochain.cer │ │ │ ├── root-ca.cer │ │ │ ├── client.key │ │ │ └── server.key │ └── kotlin │ │ └── io │ │ └── javalin │ │ └── community │ │ └── ssl │ │ └── SslConfigExceptionTest.kt │ └── main │ ├── module-info.java │ └── kotlin │ └── io │ └── javalin │ └── community │ └── ssl │ └── SslConfigException.kt ├── javalin ├── src │ ├── test │ │ ├── resources │ │ │ ├── public │ │ │ │ ├── file │ │ │ │ ├── subpage │ │ │ │ │ └── index.html │ │ │ │ ├── file.javalin │ │ │ │ ├── subdir │ │ │ │ │ └── index.html │ │ │ │ ├── protected │ │ │ │ │ └── secret.html │ │ │ │ ├── script.js │ │ │ │ ├── immutable │ │ │ │ │ └── library-1.0.0.min.js │ │ │ │ ├── styles.css │ │ │ │ ├── module.mjs │ │ │ │ ├── assets │ │ │ │ │ └── filtered-styles.css │ │ │ │ ├── readme.md.br │ │ │ │ ├── html.html │ │ │ │ └── sse │ │ │ │ │ └── sse-example.html │ │ │ ├── markdown │ │ │ │ └── test.md │ │ │ ├── vue │ │ │ │ ├── scripts-dev.js │ │ │ │ ├── scripts-not-dev.js │ │ │ │ ├── scripts.js │ │ │ │ ├── dependency-1.vue │ │ │ │ ├── view-one.vue │ │ │ │ ├── view-two.vue │ │ │ │ ├── dependency-123.vue │ │ │ │ ├── dependency-one.vue │ │ │ │ ├── test-component.vue │ │ │ │ ├── view-one-3.vue │ │ │ │ ├── view-three.vue │ │ │ │ ├── dependency-two.vue │ │ │ │ ├── dependency-1-foo.vue │ │ │ │ ├── dependency-one-3.vue │ │ │ │ ├── test-component-3.vue │ │ │ │ ├── dependency-two-3.vue │ │ │ │ ├── view-nested-dependency.vue │ │ │ │ ├── nested-dependency.vue │ │ │ │ ├── view-number-dependency.vue │ │ │ │ ├── multi-dependency.vue │ │ │ │ ├── view-multiline-dependency.vue │ │ │ │ └── layout.html │ │ │ ├── upload-test │ │ │ │ ├── text.txt │ │ │ │ ├── image.png │ │ │ │ └── sound.mp3 │ │ │ └── keystore.jks │ │ ├── external │ │ │ ├── txt.txt │ │ │ └── html.html │ │ ├── java │ │ │ └── io │ │ │ │ └── javalin │ │ │ │ ├── testing │ │ │ │ ├── TestDependency.kt │ │ │ │ ├── NonSerializableObject.java │ │ │ │ ├── FasterJacksonMapper.kt │ │ │ │ ├── SerializableObject.java │ │ │ │ ├── RunResult.java │ │ │ │ ├── UploadInfo.kt │ │ │ │ ├── TypedException.java │ │ │ │ ├── ThrowingBiConsumer.java │ │ │ │ ├── TestEnvironment.kt │ │ │ │ ├── TestServlet.java │ │ │ │ ├── WebDriverUtil.kt │ │ │ │ └── HttpUtil.kt │ │ │ │ ├── examples │ │ │ │ ├── HelloWorld.java │ │ │ │ ├── HelloWorldStaticFiles.java │ │ │ │ ├── HelloWorldStaticFiles_external.java │ │ │ │ ├── HelloWorldFuture.java │ │ │ │ ├── HelloWorldAsync.java │ │ │ │ ├── FileUploadExample.java │ │ │ │ ├── HelloWorldBasicAuth.java │ │ │ │ ├── HelloWorldApi.java │ │ │ │ ├── HelloWorldSse.java │ │ │ │ ├── HelloWorldCors.java │ │ │ │ ├── HelloWorldServlet.java │ │ │ │ ├── HelloWorldStaticFiles_linked.java │ │ │ │ ├── HelloWorldCustomJsonMapper.java │ │ │ │ ├── HelloWorldSecure.java │ │ │ │ ├── HelloWorldWebSockets.java │ │ │ │ └── HelloWorldPlugin.java │ │ │ │ ├── TestServerHeader.kt │ │ │ │ ├── javalinvue │ │ │ │ └── VueTestUtil.kt │ │ │ │ ├── TestRequest_Java.java │ │ │ │ ├── LargeSeekableInput.kt │ │ │ │ ├── TestDependencyUtil.kt │ │ │ │ ├── http │ │ │ │ ├── HttpResponseExceptionTest.kt │ │ │ │ └── ContentTypeTest.kt │ │ │ │ ├── TestContextHandlerType.java │ │ │ │ ├── TestConcurrencyUtil.kt │ │ │ │ ├── TestMethodNotAllowed.kt │ │ │ │ ├── routeoverview │ │ │ │ └── TestRouteOverviewInJava.java │ │ │ │ ├── TestCustomRequestLifecycle.kt │ │ │ │ ├── TestClose.kt │ │ │ │ ├── TestSslRedirectPlugin.kt │ │ │ │ ├── TestMaxRequestSize.kt │ │ │ │ ├── TestJsonMapper.kt │ │ │ │ └── TestHttpAllowedMethodsPlugin.kt │ │ └── kotlin │ │ │ └── io │ │ │ └── javalin │ │ │ └── examples │ │ │ ├── HelloWorld.kt │ │ │ ├── HelloWorldStaticFiles.kt │ │ │ ├── HelloWorldStaticFiles_external.kt │ │ │ ├── FileUploadExample.kt │ │ │ ├── HelloWorldApi.kt │ │ │ ├── HelloWorldSse.kt │ │ │ ├── HelloWorldCustomJsonMapper.kt │ │ │ ├── HelloWorldCors.kt │ │ │ ├── KotlinExample.kt │ │ │ ├── HelloWorldPlugin.kt │ │ │ ├── HelloWorldAsync.kt │ │ │ ├── HelloWorldSecure.kt │ │ │ ├── HelloWorldImages.kt │ │ │ ├── HelloWorldWebSockets.kt │ │ │ ├── HelloWorldAuth.kt │ │ │ └── AdvancedServerSentEvent.kt │ └── main │ │ └── java │ │ └── io │ │ └── javalin │ │ ├── compression │ │ ├── CompressionType.kt │ │ ├── Gzip.kt │ │ ├── Brotli.kt │ │ ├── Compressor.kt │ │ ├── GzipCompressor.kt │ │ └── Brotli4jCompressor.kt │ │ ├── router │ │ ├── exception │ │ │ └── JavaLangErrorHandler.kt │ │ ├── RoutingApi.kt │ │ ├── matcher │ │ │ ├── ParserExceptions.kt │ │ │ ├── RoutingRegexes.kt │ │ │ └── PathMatcher.kt │ │ ├── ParsedEndpoint.kt │ │ └── error │ │ │ └── ErrorMapper.kt │ │ ├── util │ │ ├── function │ │ │ └── ThrowingRunnable.java │ │ ├── JavalinExceptions.kt │ │ ├── FileUtil.kt │ │ ├── JavalinLogger.kt │ │ └── ReflectionUtil.kt │ │ ├── http │ │ ├── servlet │ │ │ ├── ServletEntry.kt │ │ │ ├── JavalinServletRequest.kt │ │ │ └── Task.kt │ │ ├── staticfiles │ │ │ └── ResourceHandler.java │ │ ├── util │ │ │ ├── JsonEscapeUtil.kt │ │ │ ├── MethodNotAllowedUtil.kt │ │ │ └── ETagGenerator.kt │ │ ├── RequestLogger.java │ │ ├── ExceptionHandler.java │ │ ├── Handler.java │ │ ├── sse │ │ │ ├── SseHandler.kt │ │ │ └── Emitter.kt │ │ └── UploadedFile.kt │ │ ├── vue │ │ ├── VueRenderer.kt │ │ ├── VueComponent.kt │ │ ├── VueStateRenderer.kt │ │ └── VueFileInliner.kt │ │ ├── apibuilder │ │ ├── EndpointGroup.java │ │ └── CrudHandler.kt │ │ ├── security │ │ ├── BasicAuthCredentials.kt │ │ └── RouteRole.kt │ │ ├── plugin │ │ ├── PluginExceptions.kt │ │ ├── bundled │ │ │ ├── BasicAuthPlugin.kt │ │ │ ├── RouteOverviewPlugin.kt │ │ │ └── HttpAllowedMethodsPlugin.kt │ │ └── PluginManager.kt │ │ ├── event │ │ └── LifecycleEventListener.kt │ │ ├── websocket │ │ ├── WsExceptionHandler.java │ │ ├── WsRouter.kt │ │ ├── WsHandlers.kt │ │ ├── WsAutomaticPing.kt │ │ └── WsExceptionMapper.kt │ │ ├── jetty │ │ └── JettyUtil.kt │ │ ├── validation │ │ ├── NullableValidator.kt │ │ ├── BodyValidator.kt │ │ └── Validator.kt │ │ ├── config │ │ ├── RequestLoggerConfig.kt │ │ ├── ValidationConfig.kt │ │ ├── AppDataManager.kt │ │ ├── JettyInternalConfig.kt │ │ ├── SpaRootConfig.kt │ │ └── ContextResolverConfig.kt │ │ ├── rendering │ │ └── FileRenderer.kt │ │ └── json │ │ └── PipedStreamUtil.kt └── module-info.java ├── javalin-utils ├── pom.properties ├── javalin-context-mock │ └── src │ │ └── main │ │ └── java │ │ └── io │ │ └── javalin │ │ └── mock │ │ ├── ContextMockConfigurer.kt │ │ ├── ContextMockConfig.kt │ │ └── servlet │ │ └── InMemoryPart.kt ├── pom.xml └── coverage │ └── README.md ├── javalin-rendering ├── src │ ├── test │ │ ├── resources │ │ │ ├── markdown │ │ │ │ └── test.md │ │ │ └── templates │ │ │ │ ├── st │ │ │ │ ├── withImport.st │ │ │ │ └── test.st │ │ │ │ ├── jtwig │ │ │ │ ├── test.jtwig │ │ │ │ ├── test.html.twig │ │ │ │ ├── multiple.dots.twig │ │ │ │ └── custom.jtwig │ │ │ │ ├── velocity │ │ │ │ ├── test.vm │ │ │ │ └── test-set.vm │ │ │ │ ├── freemarker │ │ │ │ ├── test.ftl │ │ │ │ └── test-with-base.ftl │ │ │ │ ├── pebble │ │ │ │ ├── test.peb │ │ │ │ └── test-empty-context-map.peb │ │ │ │ ├── mustache │ │ │ │ └── test.mustache │ │ │ │ ├── thymeleaf │ │ │ │ ├── test.html │ │ │ │ └── testUrls.html │ │ │ │ └── jte │ │ │ │ ├── multiple-params.jte │ │ │ │ ├── kte │ │ │ │ ├── multiple-params.kte │ │ │ │ └── test.kte │ │ │ │ └── test.jte │ │ └── java │ │ │ └── io │ │ │ └── javalin │ │ │ ├── jte │ │ │ └── JteTestPage.java │ │ │ └── TestTemplateUtil.kt │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── io.javalin.rendering.JavalinRenderer$FileRendererLoader │ │ └── java │ │ └── io │ │ └── javalin │ │ └── rendering │ │ ├── template │ │ ├── TemplateUtil.kt │ │ ├── JavalinMustache.kt │ │ ├── JavalinFreemarker.kt │ │ ├── JavalinPebble.kt │ │ ├── JavalinVelocity.kt │ │ ├── JavalinJte.kt │ │ └── JavalinThymeleaf.kt │ │ ├── markdown │ │ └── Commonmark.kt │ │ └── util │ │ └── RenderingDependency.kt └── meta │ └── module-info.java ├── .github ├── img │ └── javalin.png ├── dependabot.yml └── workflows │ └── publish-snapshots.yml ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── pom.properties ├── javalin-osgi └── README.md ├── javalin-testtools └── src │ ├── main │ └── java │ │ └── io │ │ └── javalin │ │ └── testtools │ │ ├── TestCase.java │ │ └── JavalinTest.kt │ └── test │ ├── kotlin │ └── io │ │ └── javalin │ │ └── testtools │ │ └── KotlinApp.kt │ └── java │ └── io │ └── javalin │ └── testtools │ └── JavaApp.java ├── .gitignore ├── javalin-bundle └── src │ └── main │ └── resources │ └── logback.xml ├── javalin-micrometer └── src │ └── test │ └── kotlin │ └── io │ └── javalin │ └── micrometer │ └── MicrometerPluginTest_Java.java ├── .editorconfig └── README.md /javalin-ssl/src/test/resources/server/wrong.pem: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/file: -------------------------------------------------------------------------------- 1 | TESTFILE -------------------------------------------------------------------------------- /javalin/src/test/external/txt.txt: -------------------------------------------------------------------------------- 1 | Sample text 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/subpage/index.html: -------------------------------------------------------------------------------- 1 | TEST -------------------------------------------------------------------------------- /javalin/src/test/resources/markdown/test.md: -------------------------------------------------------------------------------- 1 | # Hello Markdown! 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/file.javalin: -------------------------------------------------------------------------------- 1 | TESTFILE.javalin 2 | -------------------------------------------------------------------------------- /javalin-utils/pom.properties: -------------------------------------------------------------------------------- 1 | # Dummy file to satisfy javalin-parent 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/subdir/index.html: -------------------------------------------------------------------------------- 1 |

Welcome file

-------------------------------------------------------------------------------- /javalin/src/test/resources/vue/scripts-dev.js: -------------------------------------------------------------------------------- 1 | let b = "Included if dev" -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/markdown/test.md: -------------------------------------------------------------------------------- 1 | # Hello Markdown! 2 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/st/withImport.st: -------------------------------------------------------------------------------- 1 | $test()$ 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/protected/secret.html: -------------------------------------------------------------------------------- 1 |

Secret file

-------------------------------------------------------------------------------- /javalin/src/test/resources/vue/scripts-not-dev.js: -------------------------------------------------------------------------------- 1 | let b = "Included if not dev" -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jtwig/test.jtwig: -------------------------------------------------------------------------------- 1 |

{{message}}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/velocity/test.vm: -------------------------------------------------------------------------------- 1 |

$message

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/freemarker/test.ftl: -------------------------------------------------------------------------------- 1 |

${message}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jtwig/test.html.twig: -------------------------------------------------------------------------------- 1 |

{{message}}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/pebble/test.peb: -------------------------------------------------------------------------------- 1 |

{{ message }}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/st/test.st: -------------------------------------------------------------------------------- 1 |

Hello $message$

2 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jtwig/multiple.dots.twig: -------------------------------------------------------------------------------- 1 |

{{message}}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/mustache/test.mustache: -------------------------------------------------------------------------------- 1 |

{{message}}

-------------------------------------------------------------------------------- /javalin/src/test/resources/public/script.js: -------------------------------------------------------------------------------- 1 | document.write("

JavaScript works

"); 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/upload-test/text.txt: -------------------------------------------------------------------------------- 1 | This is my content. 2 | It's two lines. 3 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/scripts.js: -------------------------------------------------------------------------------- 1 | let a = "Always included";let $a = "Dollar works" -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/freemarker/test-with-base.ftl: -------------------------------------------------------------------------------- 1 |

${foo}

2 | -------------------------------------------------------------------------------- /.github/img/javalin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/.github/img/javalin.png -------------------------------------------------------------------------------- /javalin/src/test/resources/public/immutable/library-1.0.0.min.js: -------------------------------------------------------------------------------- 1 | console.log("Cached for a year") 2 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/styles.css: -------------------------------------------------------------------------------- 1 | .css-test:before { 2 | content: "CSS works" 3 | } 4 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/module.mjs: -------------------------------------------------------------------------------- 1 | export function test() { 2 | return 'mjs works' 3 | } 4 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/thymeleaf/test.html: -------------------------------------------------------------------------------- 1 |

Some default value

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/velocity/test-set.vm: -------------------------------------------------------------------------------- 1 | #set($message = "Set works") 2 |

$message

-------------------------------------------------------------------------------- /javalin/src/test/resources/public/assets/filtered-styles.css: -------------------------------------------------------------------------------- 1 | .css-test:before { 2 | content: "CSS works" 3 | } 4 | -------------------------------------------------------------------------------- /javalin/src/test/resources/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin/src/test/resources/keystore.jks -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jtwig/custom.jtwig: -------------------------------------------------------------------------------- 1 |

{{javalin("Javalin is the best framework you will ever get")}}

-------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jte/multiple-params.jte: -------------------------------------------------------------------------------- 1 | @param String one 2 | @param String two 3 |

${one} ${two}!

4 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jte/kte/multiple-params.kte: -------------------------------------------------------------------------------- 1 | @param one:String 2 | @param two:String 3 |

${one} ${two}!

4 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/thymeleaf/testUrls.html: -------------------------------------------------------------------------------- 1 | Link text -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/cert.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/client/cert.der -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/cert.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/client/cert.jks -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/cert.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/client/cert.p12 -------------------------------------------------------------------------------- /javalin/src/test/resources/public/readme.md.br: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin/src/test/resources/public/readme.md.br -------------------------------------------------------------------------------- /pom.properties: -------------------------------------------------------------------------------- 1 | artifactId=${project.artifactId} 2 | groupId=${project.groupId} 3 | version=${project.version} 4 | buildTime=${maven.build.timestamp} -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/norway.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/server/norway.jks -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/norway.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/server/norway.p12 -------------------------------------------------------------------------------- /javalin/src/test/resources/upload-test/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin/src/test/resources/upload-test/image.png -------------------------------------------------------------------------------- /javalin/src/test/resources/upload-test/sound.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin/src/test/resources/upload-test/sound.mp3 -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/keystore.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/server/keystore.jks -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/keystore.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/server/keystore.p12 -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/keystores/client.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/ca/keystores/client.p12 -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/keystores/server.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/ca/keystores/server.p12 -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/keystores/issuer-ca.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/ca/keystores/issuer-ca.p12 -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/keystores/root-ca.p12: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petersoj/javalin/master/javalin-ssl/src/test/resources/ca/keystores/root-ca.p12 -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/TestDependency.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testing 2 | 3 | object TestDependency { 4 | val swaggerVersion = "4.10.3"; 5 | } 6 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jte/kte/test.kte: -------------------------------------------------------------------------------- 1 | @import io.javalin.jte.JteTestPage 2 | @param page:JteTestPage 3 |

${page.hello} ${page.world}!

4 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/jte/test.jte: -------------------------------------------------------------------------------- 1 | @import io.javalin.jte.JteTestPage 2 | @param JteTestPage page 3 |

${page.getHello()} ${page.getWorld()}!

4 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/resources/templates/pebble/test-empty-context-map.peb: -------------------------------------------------------------------------------- 1 | {% set message = 'Hello world' %} 2 | 3 | {% block content %} 4 | {{ message }} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /javalin-osgi/README.md: -------------------------------------------------------------------------------- 1 | Produced OSGi bundle of Javalin, currently exporting all the packages. 2 | This is done by re-packaging Javalin JAR into OSGi bundle and generating a suitable OSGi manifest file. 3 | -------------------------------------------------------------------------------- /javalin/src/test/external/html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

HTML works

7 | 8 | 9 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-1.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-one.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-two.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-123.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-one.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/test-component.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-one-3.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-three.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-two.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-1-foo.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-one-3.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/test-component-3.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/dependency-two-3.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/NonSerializableObject.java: -------------------------------------------------------------------------------- 1 | package io.javalin.testing; 2 | 3 | public class NonSerializableObject { 4 | private final String value1 = "First value"; 5 | private final String value2 = "Second value"; 6 | } 7 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/CompressionType.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | enum class CompressionType(val typeName: String, val extension: String) { 4 | GZIP("gzip", ".gz"), 5 | BR("br", ".br"), 6 | NONE("", ""); 7 | } 8 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/FasterJacksonMapper.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testing 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper 4 | import io.javalin.json.JavalinJackson 5 | 6 | val fasterJacksonMapper = JavalinJackson(ObjectMapper()) 7 | -------------------------------------------------------------------------------- /javalin-testtools/src/main/java/io/javalin/testtools/TestCase.java: -------------------------------------------------------------------------------- 1 | package io.javalin.testtools; 2 | 3 | import io.javalin.Javalin; 4 | 5 | @FunctionalInterface 6 | public interface TestCase { 7 | void accept(Javalin server, HttpClient client) throws Exception; 8 | } 9 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-nested-dependency.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/exception/JavaLangErrorHandler.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.router.exception 2 | 3 | import jakarta.servlet.http.HttpServletResponse 4 | 5 | fun interface JavaLangErrorHandler { 6 | fun handle(res: HttpServletResponse, err: Error) 7 | } 8 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/util/function/ThrowingRunnable.java: -------------------------------------------------------------------------------- 1 | package io.javalin.util.function; 2 | 3 | /** Throwing version of {@link java.lang.Runnable} */ 4 | @FunctionalInterface 5 | public interface ThrowingRunnable { 6 | void run() throws E; 7 | } 8 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/SerializableObject.java: -------------------------------------------------------------------------------- 1 | package io.javalin.testing; 2 | 3 | import java.io.Serializable; 4 | 5 | public class SerializableObject implements Serializable { 6 | public String value1 = "FirstValue"; 7 | public String value2 = "SecondValue"; 8 | } 9 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/nested-dependency.vue: -------------------------------------------------------------------------------- 1 | 5 | 8 | 9 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-number-dependency.vue: -------------------------------------------------------------------------------- 1 | 5 | 8 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/servlet/ServletEntry.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.servlet 2 | 3 | import jakarta.servlet.Servlet 4 | import jakarta.servlet.ServletContainerInitializer 5 | 6 | data class ServletEntry( 7 | val initializer: ServletContainerInitializer? = null, 8 | val servlet: Servlet 9 | ) 10 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/vue/VueRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.vue 2 | 3 | import io.javalin.http.Context 4 | 5 | open class VueRenderer { 6 | open fun preRender(layout: String, ctx: Context) = layout // no changes by default 7 | open fun postRender(layout: String, ctx: Context) = layout // no changes by default 8 | } 9 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/RunResult.java: -------------------------------------------------------------------------------- 1 | package io.javalin.testing; 2 | 3 | public class RunResult { 4 | public String logs; 5 | public Exception exception; 6 | 7 | public RunResult(String logs, Exception exception) { 8 | this.logs = logs; 9 | this.exception = exception; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | pom.xml.tag 3 | pom.xml.releaseBackup 4 | pom.xml.versionsBackup 5 | pom.xml.next 6 | release.properties 7 | dependency-reduced-pom.xml 8 | buildNumber.properties 9 | .mvn/timing.properties 10 | 11 | .idea 12 | *.iml 13 | 14 | # ignore Eclipse specifics 15 | .classpath 16 | .settings 17 | .project 18 | .factorypath 19 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/html.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |

HTML works

7 | 8 | 9 |

10 | 11 | 12 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/apibuilder/EndpointGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.apibuilder; 8 | 9 | @FunctionalInterface 10 | public interface EndpointGroup { 11 | void addEndpoints(); 12 | } 13 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/security/BasicAuthCredentials.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.security 2 | 3 | /** 4 | * Auth credentials for basic HTTP authorization. 5 | * Contains the Base64 decoded [username] and [password] from the Authorization header. 6 | * @see io.javalin.http.Context.basicAuthCredentials 7 | */ 8 | data class BasicAuthCredentials(val username: String, val password: String) 9 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/UploadInfo.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.testing 8 | 9 | class UploadInfo(val filename: String = "", val size: Long = 0L, val contentType: String? = "", val extension: String = "") 10 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/Gzip.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | /** Configuration for Gzip compression 4 | * @param level Compression level. Higher yields better (but slower) compression. Range 0..9, default = 6 */ 5 | class Gzip(val level: Int = 6) { 6 | init { 7 | require(level in 0..9) { "Valid range for parameter level is 0 to 9" } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /javalin-bundle/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/Brotli.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | /** Configuration for Brotli compression 4 | * @param level Compression level. Higher yields better (but slower) compression. Range 0..11, default = 4 */ 5 | class Brotli(val level: Int = 4) { 6 | init { 7 | require(level in 0..11) { "Valid range for parameter level is 0 to 11" } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/multi-dependency.vue: -------------------------------------------------------------------------------- 1 | 4 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/TypedException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.testing; 9 | 10 | public class TypedException extends Exception { 11 | public String proofOfType() { 12 | return "I'm so typed"; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /javalin-micrometer/src/test/kotlin/io/javalin/micrometer/MicrometerPluginTest_Java.java: -------------------------------------------------------------------------------- 1 | package io.javalin.micrometer; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | public class MicrometerPluginTest_Java { 6 | 7 | @Test 8 | public void api_looks_ok_from_java() { 9 | var plugin = new MicrometerPlugin(config -> {}); 10 | var handler = MicrometerPlugin.exceptionHandler; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /javalin-utils/javalin-context-mock/src/main/java/io/javalin/mock/ContextMockConfigurer.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.mock 2 | 3 | fun interface ContextMockConfigurer { 4 | /** Apply changes to the [ContextMockConfig] instance. */ 5 | fun ContextMockConfig.configure() 6 | } 7 | 8 | internal fun invokeConfigWithConfigurerScope(configurer: ContextMockConfigurer, config: ContextMockConfig) { 9 | with(configurer) { config.configure() } 10 | } 11 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorld.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | 11 | fun main() { 12 | val app = Javalin.create(/*config*/) 13 | .get("/") { it.result("Hello World") } 14 | .start(7070) 15 | } 16 | -------------------------------------------------------------------------------- /javalin-rendering/meta/module-info.java: -------------------------------------------------------------------------------- 1 | module io.javalin.rendering { 2 | requires io.javalin; 3 | requires transitive kotlin.stdlib; 4 | 5 | requires static thymeleaf; 6 | requires static com.github.mustachejava; 7 | requires static freemarker; 8 | requires static gg.jte; 9 | requires static gg.jte.runtime; 10 | requires static io.pebbletemplates; 11 | 12 | exports io.javalin.rendering.template; 13 | exports io.javalin.rendering.markdown; 14 | } 15 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.staticfiles.Location 11 | 12 | fun main() { 13 | Javalin.create { it.staticFiles.add("/public", Location.CLASSPATH) }.start(7070) 14 | } 15 | 16 | -------------------------------------------------------------------------------- /javalin-ssl/src/main/module-info.java: -------------------------------------------------------------------------------- 1 | module io.javalin.community.ssl { 2 | exports io.javalin.community.ssl; 3 | exports io.javalin.community.ssl.util; 4 | 5 | requires transitive io.javalin; 6 | requires org.eclipse.jetty.server; 7 | requires org.eclipse.jetty.alpn.server; 8 | requires org.eclipse.jetty.http2.server; 9 | requires nl.altindag.ssl; 10 | requires nl.altindag.ssl.jetty; 11 | requires nl.altindag.ssl.pem; 12 | 13 | requires kotlin.stdlib; 14 | } 15 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/staticfiles/ResourceHandler.java: -------------------------------------------------------------------------------- 1 | package io.javalin.http.staticfiles; 2 | 3 | import io.javalin.http.Context; 4 | import io.javalin.security.RouteRole; 5 | import java.util.Set; 6 | 7 | public interface ResourceHandler { 8 | boolean canHandle(Context context); 9 | 10 | boolean handle(Context context); 11 | 12 | boolean addStaticFileConfig(StaticFileConfig config); 13 | 14 | Set getResourceRouteRoles(Context ctx); 15 | } 16 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/java/io/javalin/jte/JteTestPage.java: -------------------------------------------------------------------------------- 1 | package io.javalin.jte; 2 | 3 | public class JteTestPage { 4 | private final String hello; 5 | private final String world; 6 | 7 | public JteTestPage(String hello, String world) { 8 | this.hello = hello; 9 | this.world = world; 10 | } 11 | 12 | public String getHello() { 13 | return hello; 14 | } 15 | 16 | public String getWorld() { 17 | return world; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/view-multiline-dependency.vue: -------------------------------------------------------------------------------- 1 | 16 | 19 | -------------------------------------------------------------------------------- /javalin/src/test/resources/vue/layout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | @componentRegistration 4 | 5 | 6 | 7 | 8 | 9 | 10 | @routeComponent 11 | 12 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldStaticFiles_external.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.staticfiles.Location 11 | 12 | fun main() { 13 | Javalin.create { 14 | it.staticFiles.add("src/test/external/", Location.EXTERNAL) 15 | }.start(7070) 16 | } 17 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorld.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.examples; 9 | 10 | import io.javalin.Javalin; 11 | 12 | public class HelloWorld { 13 | public static void main(String[] args) { 14 | var app = Javalin.create(/*config*/) 15 | .get("/", ctx -> ctx.result("Hello World")) 16 | .start(7070); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/resources/META-INF/services/io.javalin.rendering.JavalinRenderer$FileRendererLoader: -------------------------------------------------------------------------------- 1 | io.javalin.rendering.markdown.JavalinCommonmark$Loader 2 | io.javalin.rendering.template.JavalinFreemarker$Loader 3 | io.javalin.rendering.template.JavalinJte$Loader 4 | io.javalin.rendering.template.JavalinMustache$Loader 5 | io.javalin.rendering.template.JavalinPebble$Loader 6 | io.javalin.rendering.template.JavalinStringTemplate4$Loader 7 | io.javalin.rendering.template.JavalinThymeleaf$Loader 8 | io.javalin.rendering.template.JavalinVelocity$Loader 9 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/RoutingApi.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.router 2 | 3 | import io.javalin.config.JavalinConfig 4 | 5 | interface RoutingApi 6 | 7 | fun interface RoutingApiInitializer { 8 | fun initialize(cfg: JavalinConfig, internalRouter: InternalRouter, setup: RoutingSetupScope) 9 | } 10 | 11 | fun interface RoutingSetupScope { 12 | fun SETUP.setup() 13 | } 14 | 15 | internal fun RoutingSetupScope.invokeAsSamWithReceiver(receiver: SETUP) { 16 | with(this) { receiver.setup() } 17 | } 18 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/util/JavalinExceptions.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.util 2 | 3 | open class JavalinException : RuntimeException { 4 | constructor(message: String) : super(message) 5 | constructor(message: String, cause: Throwable) : super(message, cause) 6 | constructor(cause: Throwable) : super(cause) 7 | } 8 | 9 | class JavalinBindException(message: String, cause: Throwable) : JavalinException(message, cause) 10 | 11 | class BodyAlreadyReadException(msg: String = "Request body has already been read") : JavalinException(msg) 12 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/servlet/JavalinServletRequest.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.servlet 2 | 3 | import jakarta.servlet.ServletInputStream 4 | import jakarta.servlet.http.HttpServletRequest 5 | import jakarta.servlet.http.HttpServletRequestWrapper 6 | 7 | class JavalinServletRequest(request: HttpServletRequest) : HttpServletRequestWrapper(request) { 8 | internal var inputStreamRead: Boolean = false 9 | private set 10 | 11 | override fun getInputStream(): ServletInputStream = 12 | super.getInputStream().also { inputStreamRead = true } 13 | } 14 | -------------------------------------------------------------------------------- /javalin-testtools/src/main/java/io/javalin/testtools/JavalinTest.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testtools 2 | 3 | import io.javalin.Javalin 4 | 5 | object JavalinTest { 6 | 7 | private val testConfig = TestConfig() 8 | private val testTool = TestTool(testConfig) 9 | 10 | @JvmStatic 11 | @JvmOverloads 12 | fun test(app: Javalin = Javalin.create(), config: TestConfig = this.testConfig, testCase: TestCase) = 13 | testTool.test(app, config, testCase) 14 | 15 | @JvmStatic 16 | fun captureStdOut(runnable: Runnable) = testTool.captureStdOut(runnable) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/security/RouteRole.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.security; 8 | 9 | import io.javalin.router.EndpointMetadata 10 | 11 | /** 12 | * Marker interface for roles used in route declarations. 13 | * See {@link Context#routeRoles()}. 14 | */ 15 | interface RouteRole 16 | 17 | /** 18 | * List of roles used in route declaration 19 | */ 20 | data class Roles(val roles: Set) : EndpointMetadata 21 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/plugin/PluginExceptions.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.plugin 2 | 3 | abstract class PluginException(pluginClass: Class>, override val message: String) : 4 | RuntimeException("Error in ${pluginClass.canonicalName}: $message") 5 | 6 | data class PluginAlreadyRegisteredException(val plugin: Plugin<*>) : 7 | PluginException(plugin::class.java, "${plugin.name()} is already registered") 8 | 9 | class PluginNotRegisteredException(pluginClass: Class>) : 10 | PluginException(pluginClass, "${pluginClass.canonicalName} was not registered as a plugin at startup") 11 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldStaticFiles.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.examples; 9 | 10 | import io.javalin.Javalin; 11 | import io.javalin.http.staticfiles.Location; 12 | 13 | public class HelloWorldStaticFiles { 14 | 15 | public static void main(String[] args) { 16 | Javalin.create(config -> { 17 | config.staticFiles.add("/public", Location.CLASSPATH); 18 | }).start(7070); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/servlet/Task.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.servlet 2 | 3 | import io.javalin.http.Context 4 | 5 | enum class SubmitOrder { 6 | FIRST, 7 | LAST 8 | } 9 | 10 | fun interface TaskInitializer { 11 | fun createTasks(submitTask: (SubmitOrder, Task) -> Unit, servlet: JavalinServlet, ctx: CTX, requestUri: String) 12 | } 13 | 14 | data class Task( 15 | val skipIfExceptionOccurred: Boolean = true, // tasks in this stage can be aborted by throwing an exception 16 | val handler: TaskHandler 17 | ) 18 | 19 | fun interface TaskHandler { 20 | fun handle(): R 21 | } 22 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = 200 11 | 12 | [*.java] 13 | ij_java_names_count_to_use_import_on_demand = 999 14 | ij_java_use_single_class_imports = true 15 | 16 | [*.kt] 17 | ij_kotlin_name_count_to_use_star_import = 999 18 | ij_kotlin_name_count_to_use_star_import_for_members = 999 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [*.{js,css,html}] 24 | insert_final_newline = false 25 | 26 | [*.{yml,yaml,json}] 27 | indent_size = 2 28 | 29 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/kotlin/io/javalin/community/ssl/SslConfigExceptionTest.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.community.ssl 2 | 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Test 5 | 6 | class SslConfigExceptionTest { 7 | @Test 8 | fun `test all exception types`(){ 9 | assertEquals("There is no certificate or key file provided",SslConfigException(SslConfigException.Types.MISSING_CERT_AND_KEY_FILE).message) 10 | assertEquals("Both the certificate and key must be provided using the same method",SslConfigException(SslConfigException.Types.MULTIPLE_IDENTITY_LOADING_OPTIONS).message) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestServerHeader.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.http.HttpStatus.OK 4 | import io.javalin.testing.TestUtil 5 | import kong.unirest.HttpMethod 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.junit.jupiter.api.Test 8 | 9 | class TestServerHeader { 10 | 11 | @Test 12 | fun `server header is not set by default`() = TestUtil.test { app, http -> 13 | app.get("/hello") { it.status(OK).result("Hello world") } 14 | val response = http.call(HttpMethod.GET, "/hello") 15 | assertThat(response.headers.getFirst("Server")).isEqualTo("") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /javalin-ssl/src/main/kotlin/io/javalin/community/ssl/SslConfigException.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.community.ssl 2 | 3 | /** 4 | * Exception thrown when the SslConfig is invalid. 5 | */ 6 | class SslConfigException : RuntimeException { 7 | constructor(type: Types) : super(type.message) 8 | 9 | /** 10 | * Types of errors that can occur when configuring SSL. 11 | */ 12 | enum class Types(val message: String) { 13 | MISSING_CERT_AND_KEY_FILE("There is no certificate or key file provided"), 14 | MULTIPLE_IDENTITY_LOADING_OPTIONS("Both the certificate and key must be provided using the same method") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/event/LifecycleEventListener.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.event 8 | 9 | /** 10 | * Main interface for Lifecycle Event Handlers. 11 | * A Runnable does not suffice because the event handler may throw a checked exception. 12 | * 13 | * @see Lifecycle Events in documentation 14 | */ 15 | fun interface LifecycleEventListener { 16 | @Throws(Exception::class) fun handleEvent() 17 | } 18 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldStaticFiles_external.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.http.staticfiles.Location; 11 | 12 | public class HelloWorldStaticFiles_external { 13 | 14 | public static void main(String[] args) { 15 | Javalin.create(config -> { 16 | config.staticFiles.add("src/test/external/", Location.EXTERNAL); 17 | }).start(7070); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/TemplateUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | object TemplateUtil { 10 | @JvmStatic 11 | fun model(vararg args: Any?): Map { 12 | if (args.size % 2 != 0) { 13 | throw IllegalArgumentException("Number of arguments must be even (key value pairs).") 14 | } 15 | return args.asSequence().chunked(2).associate { it[0] as String to it[1] } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /javalin/src/test/resources/public/sse/sse-example.html: -------------------------------------------------------------------------------- 1 |

Stats

2 |
onmessage:
3 |
addEventListener("counter"):
4 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/matcher/ParserExceptions.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.router.matcher 2 | 3 | class MissingBracketsException(segment: String, val path: String) : IllegalArgumentException( 4 | "This segment '$segment' is missing some brackets! Found in path '$path'" 5 | ) 6 | 7 | class WildcardBracketAdjacentException(segment: String, val path: String) : IllegalArgumentException( 8 | "Wildcard and a path parameter bracket are adjacent in segment '$segment' of path '$path'. This is forbidden" 9 | ) 10 | 11 | class ParameterNamesNotUniqueException(val path: String) : IllegalArgumentException( 12 | "Duplicate path param names detected! This is forbidden. Found in path '$path'" 13 | ) 14 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/ThrowingBiConsumer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.testing; 8 | 9 | import java.util.function.BiConsumer; 10 | 11 | @FunctionalInterface 12 | public interface ThrowingBiConsumer extends BiConsumer { 13 | @Override 14 | default void accept(T t, U u) { 15 | try { 16 | acceptThrows(t, u); 17 | } catch (Exception e) { 18 | throw new RuntimeException(e); 19 | } 20 | } 21 | 22 | void acceptThrows(T t, U u) throws Exception; 23 | } 24 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/websocket/WsExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.websocket; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * A handler for use with {@link io.javalin.Javalin#wsException(Class, WsExceptionHandler)}. 13 | * Is triggered when an exception is thrown by a handler in a {@link WsConfig}. 14 | * 15 | * @see WsContext 16 | */ 17 | @FunctionalInterface 18 | public interface WsExceptionHandler { 19 | void handle(@NotNull T exception, @NotNull WsContext ctx); 20 | } 21 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/jetty/JettyUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.jetty 2 | 3 | import io.javalin.config.JavalinConfig 4 | import io.javalin.http.servlet.ServletEntry 5 | import io.javalin.util.Util 6 | import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer 7 | 8 | internal object JettyUtil { 9 | 10 | fun createJettyServletWithWebsocketsIfAvailable(cfg: JavalinConfig): ServletEntry? = 11 | when { 12 | Util.classExists("org.eclipse.jetty.websocket.server.JettyWebSocketServlet") -> 13 | ServletEntry(JettyWebSocketServletContainerInitializer(null), JavalinJettyServlet(cfg)) 14 | else -> 15 | null 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/vue/VueComponent.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.vue 2 | 3 | import io.javalin.http.Context 4 | 5 | class VueComponent @JvmOverloads constructor( 6 | val component: String, val state: Any? = null, 7 | private val renderer: VueRenderer = VueRenderer() 8 | ) : VueHandler(component) { 9 | override fun state(ctx: Context) = this.state // we are extending VueHandler and just returning the state passed by the user 10 | override fun preRender(layout: String, ctx: Context) = renderer.preRender(layout, ctx) // default implementation does no pre rendering 11 | override fun postRender(layout: String, ctx: Context) = renderer.postRender(layout, ctx) // default implementation does no post rendering 12 | } 13 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/TestEnvironment.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testing 2 | 3 | object TestEnvironment { 4 | 5 | // CI server 6 | val isCiServer = System.getProperty("RunningOnCi")?.toBoolean() == true 7 | val isNotCiServer = !isCiServer 8 | 9 | // Operating systems 10 | val os: String = System.getProperty("os.name").lowercase() 11 | val isMac = os.contains("mac", "darwin") 12 | val isNotMac = !isMac 13 | val isWindows = os.contains("win") 14 | val isNotWindows = !isWindows 15 | val isLinux = os.contains("nix", "nux", "aix") 16 | val isNotLinux = !isLinux 17 | 18 | private fun String.contains(vararg strings: String) = strings.any { this.contains(it, ignoreCase = true) } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /javalin-testtools/src/test/kotlin/io/javalin/testtools/KotlinApp.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testtools 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.apibuilder.ApiBuilder.get 5 | import io.javalin.http.Context 6 | 7 | // We're using objects for simplicity's sake, but you could 8 | // make it classes and do dependency injection or whatever 9 | object KotlinApp { 10 | var app = Javalin.create { javalin -> 11 | javalin.router.ignoreTrailingSlashes = false 12 | javalin.router.apiBuilder { 13 | get("/hello", HelloController::hello) 14 | } 15 | } 16 | 17 | internal object HelloController { 18 | fun hello(ctx: Context) { 19 | ctx.result("Hello, app!") 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/apibuilder/CrudHandler.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.apibuilder 8 | 9 | import io.javalin.http.Context 10 | 11 | /** 12 | * The CrudHandler is an interface for handling the five most 13 | * common CRUD operations. It's only available through the ApiBuilder. 14 | * 15 | * @see ApiBuilder 16 | */ 17 | interface CrudHandler { 18 | fun getAll(ctx: Context) 19 | fun getOne(ctx: Context, resourceId: String) 20 | fun create(ctx: Context) 21 | fun update(ctx: Context, resourceId: String) 22 | fun delete(ctx: Context, resourceId: String) 23 | } 24 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/validation/NullableValidator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.validation 8 | 9 | /** 10 | * The NullableValidator is a [Validator] that allows null values, created by calling [Validator.allowNullable]. 11 | */ 12 | open class NullableValidator internal constructor(params: Params) : BaseValidator(params) { 13 | fun check(check: Check, error: String) = addRule(params.fieldName, check, error) as NullableValidator 14 | fun check(check: Check, error: ValidationError) = addRule(params.fieldName, check, error) as NullableValidator 15 | } 16 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/javalinvue/VueTestUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.javalinvue 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.config.JavalinConfig 5 | import io.javalin.http.staticfiles.Location 6 | import io.javalin.testing.HttpUtil 7 | import io.javalin.testing.TestUtil 8 | import io.javalin.testing.ThrowingBiConsumer 9 | import java.util.function.Consumer 10 | 11 | object VueTestUtil { 12 | @JvmOverloads 13 | @JvmStatic 14 | fun test(config: Consumer? = null, test: ThrowingBiConsumer) = 15 | TestUtil.test(Javalin.create { baseConfig -> 16 | baseConfig.vue.rootDirectory("src/test/resources/vue", Location.EXTERNAL) 17 | config?.accept(baseConfig) 18 | }, test) 19 | } 20 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/TestServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.testing; 8 | 9 | import jakarta.servlet.http.HttpServlet; 10 | import jakarta.servlet.http.HttpServletRequest; 11 | import jakarta.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | import java.io.PrintWriter; 14 | 15 | public class TestServlet extends HttpServlet { 16 | public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { 17 | PrintWriter out = res.getWriter(); 18 | out.println("Hello Servlet World!"); 19 | out.close(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/Compressor.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | import java.io.OutputStream 4 | 5 | /** A compressor is used to compress an output stream */ 6 | interface Compressor { 7 | 8 | /** The content encoding for this compressor (e.g. "gzip") 9 | * [MDN Content-Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding) 10 | * @return the content encoding (case-insensitive) */ 11 | fun encoding(): String 12 | 13 | /** @return the file extension for this compressor (empty string if none) */ 14 | fun extension(): String = "" 15 | 16 | /** @param out the output stream to compress 17 | * @return the compressed output stream */ 18 | fun compress(out: OutputStream): OutputStream 19 | } 20 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/ParsedEndpoint.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.router 2 | 3 | import io.javalin.config.RouterConfig 4 | import io.javalin.http.servlet.JavalinServletContext 5 | import io.javalin.router.matcher.PathParser 6 | 7 | class ParsedEndpoint( 8 | val endpoint: Endpoint, 9 | routerConfig: RouterConfig, 10 | ) { 11 | 12 | private val pathParser = PathParser(endpoint.path, routerConfig) 13 | 14 | fun handle(ctx: JavalinServletContext, requestUri: String) { 15 | endpoint.handle(ctx.update(this, requestUri)) 16 | } 17 | 18 | fun matches(requestUri: String): Boolean = 19 | pathParser.matches(requestUri) 20 | 21 | fun extractPathParams(requestUri: String): Map = 22 | pathParser.extractPathParams(requestUri) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /javalin-testtools/src/test/java/io/javalin/testtools/JavaApp.java: -------------------------------------------------------------------------------- 1 | package io.javalin.testtools; 2 | 3 | import io.javalin.Javalin; 4 | import io.javalin.http.Context; 5 | 6 | import static io.javalin.apibuilder.ApiBuilder.get; 7 | 8 | // We're using statics for simplicity's sake, but you could 9 | // make it non static and do dependency injection or whatever 10 | public class JavaApp { 11 | 12 | public Javalin app = Javalin.create(javalin -> { 13 | javalin.router.ignoreTrailingSlashes = false; 14 | javalin.router.apiBuilder(() -> { 15 | get("/hello", HelloController::hello); 16 | }); 17 | }); 18 | 19 | static class HelloController { 20 | public static void hello(Context ctx) { 21 | ctx.result("Hello, app!"); 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | time: "04:30" # this is in UTC time 8 | open-pull-requests-limit: 10 9 | commit-message: 10 | prefix: "[deps]" 11 | labels: 12 | - "dependencies" 13 | target-branch: "master" 14 | groups: 15 | dependencies: 16 | patterns: 17 | - "*" 18 | 19 | 20 | - package-ecosystem: "github-actions" 21 | directory: "/" 22 | schedule: 23 | interval: "weekly" 24 | open-pull-requests-limit: 5 25 | commit-message: 26 | prefix: "[workflow]" 27 | labels: 28 | - "dependencies" 29 | target-branch: "master" 30 | groups: 31 | dependencies: 32 | patterns: 33 | - "*" 34 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/GzipCompressor.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | import java.io.OutputStream 4 | import java.util.zip.GZIPOutputStream 5 | 6 | /** @param level Compression level. Higher yields better (but slower) compression. Range 0..9, default = 6 */ 7 | class GzipCompressor(val level: Int) : Compressor { 8 | init { 9 | require(level in 0..9) { "Valid range for parameter level is 0 to 9" } 10 | } 11 | 12 | override fun encoding() = CompressionType.GZIP.typeName 13 | override fun extension() = CompressionType.GZIP.extension 14 | override fun compress(out: OutputStream) = LeveledGzipStream(out, level) 15 | } 16 | 17 | class LeveledGzipStream(out: OutputStream, level: Int) : GZIPOutputStream(out) { 18 | init { 19 | this.def.setLevel(level) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /javalin-utils/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | javalin-parent 5 | io.javalin 6 | 6.7.1-SNAPSHOT 7 | 8 | 4.0.0 9 | 10 | javalin-utils 11 | pom 12 | 13 | 14 | javalin-context-mock 15 | coverage 16 | 17 | 18 | 19 | true 20 | 21 | 22 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestRequest_Java.java: -------------------------------------------------------------------------------- 1 | package io.javalin; 2 | 3 | import io.javalin.testing.TestUtil; 4 | import org.junit.jupiter.api.Test; 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | public class TestRequest_Java { 8 | 9 | @Test 10 | public void session_attribute_can_be_consumed_easily() { 11 | TestUtil.test((app, http) -> { 12 | app.get("/store-attr", ctx -> ctx.sessionAttribute("attr", "Rowin")); 13 | app.get("/read-attr", ctx -> ctx.result("" + ctx.consumeSessionAttribute("attr"))); 14 | http.getBody("/store-attr"); 15 | assertThat(http.getBody("/read-attr")).isEqualTo("Rowin"); // read (and consume) the attribute 16 | assertThat(http.getBody("/read-attr")).isEqualTo("null"); // fallback 17 | }); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/util/JsonEscapeUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http.util 8 | 9 | object JsonEscapeUtil { 10 | fun escape(str: String): String { 11 | val builder = StringBuilder(str.length) 12 | for (ch in str) { 13 | builder.append( 14 | when (ch) { 15 | '\"' -> "\\\"" 16 | '\n' -> "\\n" 17 | '\r' -> "\\r" 18 | '\\' -> "\\\\" 19 | '\t' -> "\\t" 20 | '\b' -> "\\b" 21 | else -> ch 22 | } 23 | ) 24 | } 25 | return builder.toString() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/RequestLoggerConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | import io.javalin.http.RequestLogger 4 | import io.javalin.websocket.WsConfig 5 | import java.util.function.Consumer 6 | 7 | /** 8 | * Configuration for http requests and websocket loggers. 9 | * 10 | * @param cfg the parent Javalin Configuration 11 | * @see [JavalinConfig.requestLogger] 12 | */ 13 | class RequestLoggerConfig(private val cfg: JavalinConfig) { 14 | 15 | /** Adds a request logger for HTTP requests. */ 16 | fun http(requestLogger: RequestLogger) { 17 | cfg.pvt.requestLogger = requestLogger 18 | } 19 | 20 | /** Adds a request logger for websocket requests. */ 21 | fun ws(ws: Consumer) { 22 | val logger = WsConfig() 23 | ws.accept(logger) 24 | cfg.pvt.wsLogger = logger 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/websocket/WsRouter.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.websocket 2 | 3 | import io.javalin.config.RouterConfig 4 | import io.javalin.security.RouteRole 5 | import java.util.function.Consumer 6 | 7 | class WsRouter(private val routerConfig: RouterConfig) { 8 | 9 | val wsExceptionMapper = WsExceptionMapper() 10 | val wsPathMatcher = WsPathMatcher() 11 | 12 | /** Add a WebSocket handler. */ 13 | fun addHandler(handlerType: WsHandlerType, path: String, ws: Consumer, roles: Set) { 14 | wsPathMatcher.add( 15 | WsHandlerEntry( 16 | type = handlerType, 17 | path = path, 18 | routerConfig = routerConfig, 19 | wsConfig = WsConfig().apply { ws.accept(this) }, 20 | roles = roles 21 | ) 22 | ) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/compression/Brotli4jCompressor.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.compression 2 | 3 | 4 | import com.aayushatharva.brotli4j.encoder.BrotliOutputStream 5 | import com.aayushatharva.brotli4j.encoder.Encoder 6 | import java.io.OutputStream 7 | 8 | /** @param level Compression level. Higher yields better (but slower) compression. Range 0..11, default = 4 */ 9 | class Brotli4jCompressor(val level: Int) : Compressor { 10 | init { 11 | require(level in 0..11) { "Valid range for parameter level is 0 to 11" } 12 | } 13 | 14 | override fun encoding(): String = CompressionType.BR.typeName 15 | override fun extension(): String = CompressionType.BR.extension 16 | override fun compress(out: OutputStream): OutputStream = LeveledBrotli4jStream(out, level) 17 | } 18 | 19 | class LeveledBrotli4jStream(out: OutputStream, level: Int) : 20 | BrotliOutputStream(out, Encoder.Parameters().setQuality(level)) 21 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldFuture.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.Executors; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | public class HelloWorldFuture { 15 | 16 | public static void main(String[] args) { 17 | Javalin app = Javalin.create().start(7070); 18 | app.get("/", ctx -> { 19 | CompletableFuture future = new CompletableFuture<>(); 20 | Executors.newSingleThreadScheduledExecutor().schedule(() -> future.complete("Hello World!"), 10, TimeUnit.MILLISECONDS); 21 | ctx.future(() -> future.thenApply(ctx::result)); 22 | }); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/RequestLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * Interface for logging requests. 13 | * 14 | * @see Context 15 | * @see RequestLogger in documentation 16 | */ 17 | @FunctionalInterface 18 | public interface RequestLogger { 19 | /** 20 | * Handles a request 21 | * 22 | * @param ctx the current request context 23 | * @param executionTimeMs the requests' execution time in milliseconds 24 | * @throws Exception any exception while logging information about the request 25 | */ 26 | void handle(@NotNull Context ctx, @NotNull Float executionTimeMs) throws Exception; 27 | } 28 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/util/MethodNotAllowedUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http.util 8 | 9 | import io.javalin.http.Context 10 | import io.javalin.http.HandlerType 11 | import io.javalin.http.servlet.acceptsHtml 12 | import io.javalin.router.InternalRouter 13 | 14 | object MethodNotAllowedUtil { 15 | 16 | fun findAvailableHttpHandlerTypes(router: InternalRouter, requestUri: String) = 17 | enumValues().filter { it.isHttpMethod && router.findHttpHandlerEntries(it, requestUri).findFirst().isPresent } 18 | 19 | fun getAvailableHandlerTypes(ctx: Context, availableHandlerTypes: List): Map = mapOf( 20 | (if (acceptsHtml(ctx)) "Available methods" else "availableMethods") to availableHandlerTypes.joinToString(", ") 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldAsync.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | 11 | public class HelloWorldAsync { 12 | 13 | public static void main(String[] args) { 14 | Javalin app = Javalin.create().start(7070); 15 | 16 | app.get("/", ctx -> { 17 | ctx.async( 18 | (asyncConfig) -> { 19 | asyncConfig.timeout = 1000L; 20 | asyncConfig.onTimeout(timeoutCtx -> timeoutCtx.result("Request timed out :<")); 21 | }, 22 | () -> { 23 | Thread.sleep((long) (Math.random() * 2000L)); 24 | ctx.result("Hello Javalin"); 25 | } 26 | ); 27 | }); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/FileUploadExample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.util.FileUtil 11 | 12 | fun main() { 13 | 14 | Javalin.create().apply { 15 | get("/") { ctx -> 16 | ctx.html( 17 | """ 18 |
19 | 20 | 21 |
22 | """ 23 | ) 24 | } 25 | post("/") { ctx -> 26 | ctx.uploadedFiles("files").forEach { 27 | FileUtil.streamToFile(it.content(), "upload/${it.filename()}") 28 | } 29 | } 30 | }.start(7070) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/ValidationConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | class ValidationConfig() { 4 | 5 | internal val converters = mutableMapOf, (String) -> Any?>( 6 | java.lang.Boolean::class.java to { it.toBoolean() }, 7 | java.lang.Double::class.java to { it.toDouble() }, 8 | java.lang.Float::class.java to { it.toFloat() }, 9 | java.lang.Integer::class.java to { it.toInt() }, 10 | java.lang.Long::class.java to { it.toLong() }, 11 | java.lang.String::class.java to { it }, 12 | Boolean::class.java to { it.toBoolean() }, 13 | Double::class.java to { it.toDouble() }, 14 | Float::class.java to { it.toFloat() }, 15 | Int::class.java to { it.toInt() }, 16 | Long::class.java to { it.toLong() }, 17 | String::class.java to { it } 18 | ) 19 | 20 | fun register(clazz: Class<*>, converter: (String) -> Any?) { 21 | converters[clazz] = converter 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http; 8 | 9 | import io.javalin.Javalin; 10 | import org.jetbrains.annotations.NotNull; 11 | 12 | /** 13 | * A handler for use with {@link Javalin#exception(Class, ExceptionHandler)}. 14 | * Is triggered when exceptions are thrown by a {@link Handler}. 15 | * 16 | * @see Context 17 | * @see Exception mapping in docs 18 | */ 19 | @FunctionalInterface 20 | public interface ExceptionHandler { 21 | /** 22 | * Handles an exception thrown while handling a request 23 | * 24 | * @param exception the thrown exception 25 | * @param ctx the context of the request 26 | */ 27 | void handle(@NotNull T exception, @NotNull Context ctx); 28 | } 29 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/websocket/WsHandlers.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | package io.javalin.websocket 7 | 8 | enum class WsHandlerType { WEBSOCKET_BEFORE, WEBSOCKET, WEBSOCKET_AFTER } 9 | 10 | fun interface WsConnectHandler { 11 | @Throws(Exception::class) 12 | fun handleConnect(ctx: WsConnectContext) 13 | } 14 | 15 | fun interface WsMessageHandler { 16 | @Throws(Exception::class) 17 | fun handleMessage(ctx: WsMessageContext) 18 | } 19 | 20 | fun interface WsBinaryMessageHandler { 21 | @Throws(Exception::class) 22 | fun handleBinaryMessage(ctx: WsBinaryMessageContext) 23 | } 24 | 25 | fun interface WsErrorHandler { 26 | @Throws(Exception::class) 27 | fun handleError(ctx: WsErrorContext) 28 | } 29 | 30 | fun interface WsCloseHandler { 31 | @Throws(Exception::class) 32 | fun handleClose(ctx: WsCloseContext) 33 | } 34 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/rendering/FileRenderer.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | package io.javalin.rendering 7 | 8 | import io.javalin.config.Key 9 | import io.javalin.http.Context 10 | 11 | /** Interface for creating renderers to be used with [Context.render]. */ 12 | fun interface FileRenderer { 13 | companion object { 14 | @JvmField val FileRendererKey = Key("javalin-file-renderer") 15 | } 16 | 17 | /** Renders the given file */ 18 | fun render(filePath: String, model: Map, context: Context): String 19 | } 20 | 21 | class NotImplementedRenderer : FileRenderer { 22 | override fun render(filePath: String, model: Map, context: Context): String { 23 | throw UnsupportedOperationException("No FileRenderer configured. You can configure one in config.fileRenderer(...)") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldApi.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.apibuilder.ApiBuilder.path 12 | import io.javalin.http.HttpStatus 13 | 14 | 15 | fun main() { 16 | Javalin.create { 17 | it.router.apiBuilder { 18 | get("/hello") { it.result("Hello World") } 19 | path("/api") { 20 | get("/test") { it.result("Hello World") } 21 | get("/tast") { it.status(HttpStatus.OK).result("Hello world") } 22 | get("/hest") { it.status(HttpStatus.OK).result("Hello World") } 23 | get("/hast") { it.status(HttpStatus.OK).result("Hello World").header("test", "tast") } 24 | } 25 | } 26 | }.start(7070) 27 | } 28 | 29 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/AppDataManager.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | data class Key(val id: String) 4 | 5 | class KeyAlreadyExistsException(key: Key<*>) : IllegalStateException("Key '$key' already exists") 6 | class NoValueForKeyException(key: Key<*>) : IllegalStateException("No value for key '$key'") 7 | 8 | class AppDataManager { 9 | 10 | private val data: MutableMap, Any?> = mutableMapOf() 11 | 12 | fun register(key: Key, value: T) { 13 | if (data.containsKey(key)) { 14 | throw KeyAlreadyExistsException(key) 15 | } 16 | data[key] = value 17 | } 18 | 19 | fun registerIfAbsent(key: Key, value: T) { 20 | registerResolverIfAbsent(key, value) 21 | } 22 | 23 | fun registerResolverIfAbsent(key: Key, value: T) { 24 | data.putIfAbsent(key, value) 25 | } 26 | 27 | fun get(key: Key): T { 28 | return data[key] as T? ?: throw NoValueForKeyException(key) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/LargeSeekableInput.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import java.io.InputStream 4 | 5 | class LargeSeekableInput(private val prefixSize: Long, private val contentSize: Long) : InputStream() { 6 | 7 | private var alreadyRead = 0L 8 | private fun remaining(): Long = prefixSize + contentSize - alreadyRead 9 | 10 | override fun available(): Int = when (val rem = remaining()) { 11 | in 0..Int.MAX_VALUE -> rem.toInt() 12 | else -> Int.MAX_VALUE 13 | } 14 | 15 | override fun skip(toSkip: Long): Long = when { 16 | toSkip <= 0 -> 0 17 | else -> when (val rem = remaining()) { 18 | in 0..toSkip -> rem.also { alreadyRead += rem } 19 | else -> toSkip.also { alreadyRead += toSkip } 20 | } 21 | } 22 | 23 | override fun read(): Int = when { 24 | remaining() == 0L -> -1 25 | alreadyRead < prefixSize -> ' '.code.also { alreadyRead++ } 26 | else -> 'J'.code.also { alreadyRead++ } 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/util/FileUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.util 8 | 9 | import java.io.File 10 | import java.io.InputStream 11 | import java.nio.file.Files 12 | import java.nio.file.StandardCopyOption 13 | 14 | object FileUtil { 15 | 16 | @JvmStatic 17 | fun streamToFile(inputStream: InputStream, path: String) { 18 | val newFile = File(path) 19 | newFile.parentFile.mkdirs() // create parent dirs if necessary 20 | newFile.createNewFile() // create file if necessary 21 | inputStream.use { input -> 22 | Files.copy(input, newFile.toPath(), StandardCopyOption.REPLACE_EXISTING) 23 | } 24 | 25 | } 26 | 27 | @JvmStatic 28 | fun readResource(path: String) = FileUtil::class.java.getResource(path).readText() 29 | 30 | @JvmStatic 31 | fun readFile(path: String) = File(path).readText() 32 | 33 | } 34 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/Handler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http; 8 | 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * Main interface for endpoint actions. A handler has a void return type, 13 | * so you have to use {@link Context#result} to return data to the client. 14 | * 15 | * @see Context 16 | * @see Handler in documentation 17 | */ 18 | @FunctionalInterface 19 | public interface Handler { 20 | /** 21 | * Handles a request. 22 | * 23 | * @param ctx the current request's context. Use this to process the request's 24 | * parameter (query, form params, body, …) and build up the response 25 | * (status code, payload, …) 26 | * @throws Exception an exception while handling the request 27 | */ 28 | void handle(@NotNull Context ctx) throws Exception; 29 | } 30 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestDependencyUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.util.CoreDependency 4 | import io.javalin.util.DependencyUtil 5 | import org.assertj.core.api.Assertions.assertThat 6 | import org.junit.jupiter.api.Test 7 | 8 | class TestDependencyUtil { 9 | 10 | @Test 11 | fun `DependencyUtil#missingDependencyMessage works`() { 12 | val message = DependencyUtil.missingDependencyMessage(CoreDependency.JACKSON) 13 | assertThat(message).contains("You're missing the 'Jackson' dependency in your project. Add the dependency:") 14 | } 15 | 16 | @Test 17 | fun `DependencyUtil#mavenAndGradleSnippets works`() { 18 | val dependency = CoreDependency.JACKSON 19 | val message = DependencyUtil.mavenAndGradleSnippets(dependency) 20 | assertThat(message).contains("pom.xml:") 21 | assertThat(message).contains("build.gradle or build.gradle.kts:") 22 | assertThat(message).contains("""implementation("${dependency.groupId}:${dependency.artifactId}:${dependency.version}")""") 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshots.yml: -------------------------------------------------------------------------------- 1 | name: Snapshot build 2 | 3 | on: 4 | push: 5 | branches: [ 'master' ] 6 | 7 | jobs: 8 | publish: 9 | if: | 10 | github.repository == 'javalin/javalin' && 11 | !contains(github.event.head_commit.message, '[maven-release-plugin] prepare release') 12 | name: Publish snapshot 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Set up JDK11 17 | uses: actions/setup-java@v4 18 | with: 19 | distribution: 'zulu' 20 | java-version: 11 21 | - name: Grant execute permission for mvnw 22 | run: chmod +x ./mvnw 23 | - uses: s4u/maven-settings-action@v3.1.0 24 | with: 25 | servers: | 26 | [{ 27 | "id": "reposilite-repository", 28 | "username": "${{ secrets.MAVEN_NAME }}", 29 | "password": "${{ secrets.MAVEN_SECRET }}" 30 | }] 31 | - name: Publish with Maven 32 | run: ./mvnw -DRunningOnCi=true clean deploy --file pom.xml --batch-mode -P publish-snapshot 33 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/JettyInternalConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | import org.eclipse.jetty.server.Connector 4 | import org.eclipse.jetty.server.HttpConfiguration 5 | import org.eclipse.jetty.server.Server 6 | import org.eclipse.jetty.servlet.ServletContextHandler 7 | import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory 8 | import java.util.function.BiFunction 9 | import java.util.function.Consumer 10 | 11 | class JettyInternalConfig { 12 | // @formatter:off 13 | @JvmField var server: Server? = null 14 | @JvmField var serverConsumers: MutableList> = mutableListOf() 15 | @JvmField var httpConfigurationConfigs: MutableList> = mutableListOf() 16 | @JvmField var servletContextHandlerConsumers: MutableList> = mutableListOf() 17 | @JvmField var wsFactoryConfigs: MutableList> = mutableListOf() 18 | @JvmField var connectors: MutableList> = mutableListOf() 19 | // @formatter:on 20 | } 21 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/FileUploadExample.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.util.FileUtil; 11 | 12 | public class FileUploadExample { 13 | 14 | public static void main(String[] args) { 15 | 16 | Javalin app = Javalin.create().start(7000); 17 | 18 | app.get("/", ctx -> 19 | ctx.html( 20 | "" 21 | + "
" 22 | + " " 23 | + " " 24 | + "
" 25 | ) 26 | ); 27 | 28 | app.post("/", ctx -> { 29 | ctx.uploadedFiles("files").forEach(file -> { 30 | FileUtil.streamToFile(file.content(), "upload/" + file.filename()); 31 | }); 32 | }); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldSse.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.sse.SseClient 11 | import java.util.concurrent.ConcurrentLinkedQueue 12 | import java.util.concurrent.TimeUnit 13 | 14 | fun main() { 15 | 16 | val clients = ConcurrentLinkedQueue() 17 | 18 | val app = Javalin.create().start(7000) 19 | app.get("/") { it.html("") } 20 | 21 | app.sse("/sse") { client -> 22 | client.keepAlive() 23 | clients.add(client) // save the sse to use outside of this context 24 | client.onClose { clients.remove(client) } 25 | } 26 | 27 | while (true) { 28 | for (client in clients) { 29 | client.sendEvent("hi", "hello world") 30 | } 31 | TimeUnit.SECONDS.sleep(1) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /javalin-utils/javalin-context-mock/src/main/java/io/javalin/mock/ContextMockConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.mock 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.config.JavalinConfig 5 | import io.javalin.mock.servlet.HttpServletRequestMock.RequestState 6 | import io.javalin.mock.servlet.HttpServletResponseMock.ResponseState 7 | import java.util.function.Consumer 8 | 9 | data class ContextMockConfig internal constructor( 10 | val req: RequestState = RequestState(), 11 | val res: ResponseState = ResponseState(), 12 | var javalinConfig: JavalinConfig = Javalin.create().unsafeConfig() 13 | ) { 14 | 15 | /** Change Javalin config used to prepare the [Context] instance. */ 16 | fun javalinConfig(config: Consumer) { 17 | this.javalinConfig = Javalin.create(config).unsafeConfig() 18 | } 19 | 20 | /** Deep copy of this [ContextMockConfig] */ 21 | fun clone(): ContextMockConfig = 22 | copy( 23 | req = req.copy(), 24 | res = res.copy(), 25 | javalinConfig = javalinConfig // TODO: we could clone this too (?) 26 | ) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/SpaRootConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | import io.javalin.http.Handler 4 | import io.javalin.http.SinglePageHandler 5 | import io.javalin.http.staticfiles.Location 6 | 7 | /** 8 | * Configures a Single Page Application root. 9 | * 10 | * Single page application (SPA) mode is similar to static file handling. 11 | * It runs after endpoint matching, and after static file handling. It’s 12 | * basically a very fancy 404 mapper, which converts any 404’s into a 13 | * specified page. You can define multiple single page handlers for your 14 | * application by specifying different root paths. 15 | * 16 | * @see [SinglePageHandler] 17 | */ 18 | class SpaRootConfig(private val cfg: JavalinConfig) { 19 | 20 | @JvmOverloads 21 | fun addFile(hostedPath: String, filePath: String, location: Location = Location.CLASSPATH) { 22 | cfg.pvt.singlePageHandler.add(hostedPath, filePath, location) 23 | } 24 | 25 | fun addHandler(hostedPath: String, customHandler: Handler) { 26 | cfg.pvt.singlePageHandler.add(hostedPath, customHandler) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/http/HttpResponseExceptionTest.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http 2 | 3 | import java.util.Locale 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.assertDoesNotThrow 6 | 7 | class HttpResponseExceptionTest { 8 | 9 | @Test 10 | fun `all HttpStatus values should be covered by an Exception class`() { 11 | for (httpStatus in HttpStatus.entries) { 12 | if (httpStatus == HttpStatus.UNKNOWN) { 13 | continue 14 | } 15 | val convertedEnumName = httpStatus.name.splitToSequence("_").map { word -> 16 | word.lowercase().replaceFirstChar { 17 | if (it.isLowerCase()) it.titlecase( 18 | Locale.getDefault() 19 | ) else it.toString() 20 | } 21 | }.joinToString("") 22 | val expectedClassName = "io.javalin.http.${convertedEnumName}Response" 23 | assertDoesNotThrow("$expectedClassName does not exist") { 24 | Class.forName(expectedClassName) 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/sse/SseHandler.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.sse 2 | 3 | import io.javalin.http.Context 4 | import io.javalin.http.Handler 5 | import io.javalin.http.Header 6 | import java.util.function.Consumer 7 | 8 | class SseHandler @JvmOverloads constructor( 9 | private val timeout: Long = 0, 10 | private val clientConsumer: Consumer 11 | ) : Handler { 12 | 13 | override fun handle(ctx: Context) { 14 | if (ctx.header(Header.ACCEPT) == "text/event-stream") { 15 | ctx.res().apply { 16 | status = 200 17 | characterEncoding = "UTF-8" 18 | contentType = "text/event-stream" 19 | addHeader(Header.CONNECTION, "close") 20 | addHeader(Header.CACHE_CONTROL, "no-cache") 21 | addHeader(Header.X_ACCEL_BUFFERING, "no") // See https://serverfault.com/a/801629 22 | flushBuffer() 23 | } 24 | ctx.async({ 25 | it.timeout = timeout 26 | }) { 27 | clientConsumer.accept(SseClient(ctx)) 28 | } 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldBasicAuth.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.examples; 9 | 10 | import io.javalin.Javalin; 11 | import io.javalin.plugin.bundled.BasicAuthPlugin; 12 | 13 | public class HelloWorldBasicAuth { 14 | 15 | public static void main(String[] args) { 16 | Javalin 17 | .create(config -> { 18 | config.registerPlugin(new BasicAuthPlugin(basicAuthCfg -> { 19 | basicAuthCfg.username = "panda"; 20 | basicAuthCfg.password = "bamboo"; 21 | })); 22 | config.registerPlugin( 23 | new BasicAuthPlugin(basicAuthCfg -> { 24 | basicAuthCfg.username = "panda"; 25 | basicAuthCfg.password = "bamboo"; 26 | }) 27 | ); 28 | }) 29 | .get("/hello", ctx -> ctx.result("Hello World 1")) 30 | .start(7070); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldCustomJsonMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.json.JsonMapper 11 | import java.lang.reflect.Type 12 | 13 | fun main() { 14 | val rawJsonMapper: JsonMapper = object : JsonMapper { 15 | // serialize obj your favourite api 16 | override fun toJsonString(obj: Any, type: Type): String = "{ \"" + type.typeName + "\": \"" + obj + "\" }" 17 | 18 | // deserialize json your favourite api 19 | @Suppress("UNCHECKED_CAST") 20 | override fun fromJsonString(json: String, targetType: Type): T = when (targetType) { 21 | String::class.java -> json as T 22 | else -> throw UnsupportedOperationException("RawJsonMapper can deserialize only strings") 23 | } 24 | } 25 | 26 | Javalin.create { it.jsonMapper(rawJsonMapper) } 27 | .get("/") { it.json(listOf("a", "b", "c")) } 28 | .start(7070) 29 | } 30 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/websocket/WsAutomaticPing.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.websocket 2 | 3 | import io.javalin.util.ConcurrencyUtil 4 | import io.javalin.util.javalinLazy 5 | import java.nio.ByteBuffer 6 | import java.util.concurrent.ConcurrentHashMap 7 | import java.util.concurrent.ScheduledExecutorService 8 | import java.util.concurrent.ScheduledFuture 9 | import java.util.concurrent.TimeUnit 10 | 11 | val executor: ScheduledExecutorService by javalinLazy { ConcurrencyUtil.newSingleThreadScheduledExecutor("JavalinWebSocketPingThread") } 12 | val pingFutures: ConcurrentHashMap?> by javalinLazy { ConcurrentHashMap() } 13 | 14 | fun enableAutomaticPings(ctx: WsContext, interval: Long, unit: TimeUnit, applicationData: ByteBuffer?) { 15 | synchronized(ctx) { 16 | disableAutomaticPings(ctx); 17 | pingFutures[ctx] = executor.scheduleAtFixedRate({ 18 | ctx.sendPing(applicationData) 19 | }, interval, interval, unit) 20 | } 21 | } 22 | 23 | fun disableAutomaticPings(ctx: WsContext) { 24 | synchronized(ctx) { 25 | pingFutures[ctx]?.cancel(false) 26 | pingFutures.remove(ctx); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /javalin-rendering/src/test/java/io/javalin/TestTemplateUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin 9 | 10 | import io.javalin.rendering.template.TemplateUtil 11 | import org.assertj.core.api.Assertions.assertThat 12 | import org.junit.jupiter.api.Test 13 | import org.junit.jupiter.api.Assertions.assertThrows 14 | 15 | 16 | class TestTemplateUtil { 17 | @Test 18 | fun `model works`() { 19 | val model = TemplateUtil.model("foo", "bar", "baz", "qux") 20 | assertThat(model).isEqualTo(mapOf("foo" to "bar", "baz" to "qux")) 21 | } 22 | 23 | @Test 24 | fun `model throws exception if number of arguments is odd`() { 25 | assertThrows(IllegalArgumentException::class.java) { 26 | TemplateUtil.model("foo", "bar", "baz") 27 | } 28 | } 29 | 30 | @Test 31 | fun `model works with null values`() { 32 | val model = TemplateUtil.model("foo", "bar", "baz", null) 33 | assertThat(model).isEqualTo(mapOf("foo" to "bar", "baz" to null)) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldCors.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.apibuilder.ApiBuilder.patch 12 | import io.javalin.apibuilder.ApiBuilder.post 13 | import io.javalin.plugin.bundled.CorsPlugin 14 | 15 | fun main() { 16 | Javalin.create { cfg -> 17 | cfg.registerPlugin(CorsPlugin { cors -> 18 | cors.addRule { 19 | it.allowHost("http://localhost:7001/", "http://localhost:7002") 20 | } 21 | }) 22 | cfg.router.apiBuilder { 23 | get { it.json("Hello Get") } 24 | post { it.json("Hello Post") } 25 | patch { it.json("Hello Patch") } 26 | } 27 | }.start(7070) 28 | 29 | Javalin.create().start(7001).get("/") { it.html("Try some CORS") } 30 | Javalin.create().start(7002).get("/") { it.html("Try some CORS") } 31 | Javalin.create().start(7003).get("/") { it.html("No CORS here") } 32 | } 33 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldApi.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.examples; 9 | 10 | import io.javalin.Javalin; 11 | 12 | import static io.javalin.apibuilder.ApiBuilder.get; 13 | import static io.javalin.apibuilder.ApiBuilder.path; 14 | import static io.javalin.http.HttpStatus.OK; 15 | 16 | public class HelloWorldApi { 17 | 18 | public static void main(String[] args) { 19 | Javalin.create(config -> { 20 | config.router.apiBuilder(() -> { 21 | get("/hello", ctx -> ctx.result("Hello World")); 22 | path("/api", () -> { 23 | get("/test", ctx -> ctx.result("Hello World")); 24 | get("/tast", ctx -> ctx.status(OK).result("Hello world")); 25 | get("/hest", ctx -> ctx.status(OK).result("Hello World")); 26 | get("/hast", ctx -> ctx.status(OK).result("Hello World").header("test", "tast")); 27 | }); 28 | }); 29 | }) 30 | .start(7070); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Javalin 2 | This readme is for Javalin developers. 3 | If you are looking for the general readme, see [.github/README.md](.github/README.md). 4 | 5 | ## Getting started 6 | ```sh 7 | gh repo clone javalin/javalin 8 | cd javalin 9 | ./mvnw package #(or `mvn package` if you have maven installed) 10 | ./mvnw test #(or `mvn test` if you have maven installed) 11 | ``` 12 | 13 | If you run `test` before `package`, you will get an error in the OSGI artifact: 14 | 15 | ``` 16 | [ERROR] Failed to execute goal org.apache.maven.plugins:maven-dependency-plugin:2.8:unpack-dependencies (unpack-everything) on project javalin-osgi: Artifact has not been packaged yet. When used on reactor artifact, unpack should be executed after packaging: see MDEP-98. -> [Help 1] 17 | ``` 18 | 19 | ## Running maven commands 20 | We have Maven wrapper included in the project, which means that 21 | you can run Maven commands without having Maven installed on your system. 22 | Simply replace any `mvn goal` command with `./mvnw goal`. 23 | 24 | ## Deploy 25 | The `sonatype-oss-release` profile is used for releasing the project artifacts to Sonatype OSSRH (OSS Repository Hosting). 26 | This is only used by tipsy to release the project. 27 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/error/ErrorMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.router.error 8 | 9 | import io.javalin.http.Context 10 | import io.javalin.http.Handler 11 | import io.javalin.http.Header 12 | 13 | class ErrorMapper { 14 | 15 | data class MapperEntry(val statusCode: Int, val contentType: String, val handler: Handler) 16 | 17 | private val errorHandlers = mutableSetOf() 18 | 19 | fun addHandler(statusCode: Int, contentType: String, handler: Handler) { 20 | errorHandlers.add(MapperEntry(statusCode, contentType, handler)) 21 | } 22 | 23 | fun handle(statusCode: Int, ctx: Context) { 24 | errorHandlers.asSequence() 25 | .filter { it.statusCode == statusCode } 26 | .filter { it.contentType == "*" || ctx.contentTypeMatches(it.contentType) } 27 | .forEach { it.handler.handle(ctx) } 28 | } 29 | 30 | private fun Context.contentTypeMatches(contentType: String): Boolean = 31 | header(Header.ACCEPT)?.contains(contentType, ignoreCase = true) == true 32 | 33 | } 34 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/UploadedFile.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.http 8 | 9 | import jakarta.servlet.http.Part 10 | import java.io.InputStream 11 | 12 | /** 13 | * Data class containing the content and meta-info of an uploaded file. 14 | * [content]: the file-content as an [InputStream] 15 | * [contentType]: the content-type passed by the client 16 | * [size]: the size of the file in bytes 17 | * [filename]: the file-name reported by the client 18 | * [extension]: the file-extension, extracted from the [filename] 19 | * @see Context.uploadedFile 20 | * @see Uploads in FAQ 21 | */ 22 | class UploadedFile(private val part: Part) { 23 | fun content(): InputStream = part.inputStream 24 | fun contentAndClose(callback: (InputStream) -> T) = content().use { callback(it) } 25 | fun contentType(): String? = part.contentType 26 | fun filename(): String = part.submittedFileName 27 | fun extension(): String = part.submittedFileName.replaceBeforeLast(".", "") 28 | fun size(): Long = part.size 29 | } 30 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/websocket/WsExceptionMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.websocket 8 | 9 | import io.javalin.util.JavalinLogger 10 | import io.javalin.util.Util 11 | import org.eclipse.jetty.websocket.api.StatusCode 12 | 13 | /** 14 | * Maps exception types to exception handlers. 15 | */ 16 | class WsExceptionMapper { 17 | 18 | val handlers = mutableMapOf, WsExceptionHandler?>() 19 | 20 | /** 21 | * Handles the specific [exception]. If no handler is associated with the exception, then the 22 | * socket is closed with a status code that indicates internal error. 23 | */ 24 | fun handle(exception: Exception, ctx: WsContext) { 25 | val handler = Util.findByClass(handlers, exception.javaClass) 26 | if (handler != null) { 27 | handler.handle(exception, ctx) 28 | } else { 29 | JavalinLogger.warn("Uncaught exception in WebSocket handler", exception) 30 | ctx.session.close(StatusCode.SERVER_ERROR, exception.message) 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestContextHandlerType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2021 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | package io.javalin; 7 | 8 | import io.javalin.http.HandlerType; 9 | import io.javalin.testing.TestUtil; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import org.assertj.core.api.Assertions; 13 | import org.junit.jupiter.api.Test; 14 | import static io.javalin.http.HttpStatus.OK; 15 | 16 | public class TestContextHandlerType { 17 | 18 | @Test 19 | public void testHandlerTypeCanBeAccessedInContext() { 20 | TestUtil.test(Javalin.create(), (app, http) -> { 21 | List handlerTypes = new ArrayList<>(); 22 | app.before(ctx -> handlerTypes.add(ctx.handlerType())); 23 | app.after(ctx -> handlerTypes.add(ctx.handlerType())); 24 | app.get("/", ctx -> handlerTypes.add(ctx.handlerType())); 25 | 26 | Assertions.assertThat(http.get("/").getStatus()).isEqualTo(OK.getCode()); 27 | Assertions.assertThat(handlerTypes).containsExactly(HandlerType.BEFORE, HandlerType.GET, HandlerType.AFTER); 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/WebDriverUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.testing 2 | 3 | import io.github.bonigarcia.wdm.WebDriverManager 4 | import org.junit.jupiter.api.Assumptions.assumeTrue 5 | import org.openqa.selenium.chrome.ChromeDriver 6 | import org.openqa.selenium.chrome.ChromeOptions 7 | 8 | object WebDriverUtil { 9 | 10 | init { 11 | WebDriverManager.chromedriver().setup() 12 | } 13 | 14 | fun getDriver(): ChromeDriver { 15 | if (TestEnvironment.isNotCiServer && Math.random() < 0.8) { 16 | assumeTrue(false, "Skipping random WebDriver tests because we're not on a CI server and the tests are sooo slow") 17 | } 18 | if (TestEnvironment.isCiServer && TestEnvironment.isWindows) { // TODO: remove this when flakiness is fixed 19 | assumeTrue(false, "Skipping WebDriver tests on Windows CI server because they are flaky") 20 | } 21 | return ChromeDriver(ChromeOptions().apply { 22 | addArguments("--no-sandbox") 23 | addArguments("--headless=new") 24 | addArguments("--disable-gpu") 25 | addArguments("--remote-allow-origins=*") 26 | addArguments("--disable-dev-shm-usage") 27 | }) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/KotlinExample.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.HttpStatus 11 | import io.javalin.testing.TypedException 12 | 13 | fun main() { 14 | 15 | Javalin.create().apply { 16 | 17 | get("/users") { ctx -> 18 | ctx.result("") 19 | } 20 | 21 | post("/users/create") { ctx -> 22 | ctx.status(HttpStatus.CREATED) 23 | } 24 | 25 | patch("/users/update/:id") { ctx -> 26 | ctx.status(HttpStatus.NO_CONTENT) 27 | } 28 | 29 | delete("/users/delete/:id") { ctx -> 30 | ctx.status(HttpStatus.NO_CONTENT) 31 | } 32 | 33 | exception(Exception::class.java) { e, ctx -> 34 | e.printStackTrace() 35 | } 36 | 37 | exception(TypedException::class.java) { e, ctx -> 38 | e.proofOfType() 39 | } 40 | 41 | error(HttpStatus.NOT_FOUND) { ctx -> 42 | ctx.result("not found") 43 | } 44 | 45 | }.start(7070) 46 | 47 | } 48 | 49 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinMustache.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import com.github.mustachejava.DefaultMustacheFactory 10 | import com.github.mustachejava.MustacheFactory 11 | import io.javalin.http.Context 12 | import io.javalin.rendering.FileRenderer 13 | import io.javalin.rendering.util.RenderingDependency.MUSTACHE 14 | import io.javalin.rendering.util.Util 15 | import java.io.StringWriter 16 | 17 | class JavalinMustache @JvmOverloads constructor( 18 | private var mustacheFactory: MustacheFactory = defaultMustacheFactory() 19 | ) : FileRenderer { 20 | 21 | init { 22 | Util.throwIfNotAvailable(MUSTACHE) 23 | } 24 | 25 | override fun render(filePath: String, model: Map, context: Context): String { 26 | val stringWriter = StringWriter() 27 | mustacheFactory.compile(filePath).execute(stringWriter, model).close() 28 | return stringWriter.toString() 29 | } 30 | 31 | companion object { 32 | fun defaultMustacheFactory(): MustacheFactory = DefaultMustacheFactory("./") 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/validation/BodyValidator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.validation 8 | 9 | import org.jetbrains.annotations.NotNull 10 | 11 | const val REQUEST_BODY = "REQUEST_BODY" 12 | 13 | open class BodyValidator internal constructor(value: String, clazz: Class, valueSupplier: () -> T?) : BaseValidator(Params("", clazz, value, valueSupplier = valueSupplier)) { 14 | fun check(check: Check, error: String) = check(fieldName = REQUEST_BODY, check, error) 15 | fun check(check: Check, error: ValidationError) = check(fieldName = REQUEST_BODY, check, error) 16 | fun check(fieldName: String, check: Check, error: String) = addRule(fieldName, { check(it!!) }, error) as BodyValidator 17 | fun check(fieldName: String, check: Check, error: ValidationError) = addRule(fieldName, { check(it!!) }, error) as BodyValidator 18 | 19 | @NotNull // there is a null-check in BaseValidator 20 | override fun get(): T = super.get()!! 21 | 22 | @NotNull 23 | override fun getOrThrow(exceptionFunction: (Map>>) -> Exception): T = super.getOrThrow(exceptionFunction)!! 24 | } 25 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/markdown/Commonmark.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.markdown 8 | 9 | import io.javalin.http.Context 10 | import io.javalin.rendering.FileRenderer 11 | import io.javalin.rendering.util.RenderingDependency.COMMONMARK 12 | import io.javalin.rendering.util.Util 13 | import org.commonmark.parser.Parser 14 | import org.commonmark.renderer.html.HtmlRenderer 15 | 16 | class JavalinCommonmark @JvmOverloads constructor( 17 | private var renderer: HtmlRenderer = defaultRenderer(), 18 | private var parser: Parser = defaultParser() 19 | ) : FileRenderer { 20 | 21 | init { 22 | Util.throwIfNotAvailable(COMMONMARK) 23 | } 24 | 25 | override fun render(filePath: String, model: Map, context: Context): String { 26 | val fileContent = JavalinCommonmark::class.java.getResource(filePath).readText() 27 | return renderer.render(parser.parse(fileContent)) 28 | } 29 | 30 | companion object { 31 | fun defaultRenderer(): HtmlRenderer = HtmlRenderer.builder().build() 32 | fun defaultParser(): Parser = Parser.builder().build() 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/malformed.p12: -------------------------------------------------------------------------------- 1 | ���� 2 | y� ��&] 3 | ު��fϰ��˥�� K���ˠ�(�Mg��� 4 | &�^��{��ބOm�Y$*q��l7����v���� Co���/��r��جk�H?7��fᾉ����!"�� )�J���?=Z�h��y��Z�b���T��.m�y)1A4�n�/O6��"<͜��ǚĸM�ϼdb�y���>1�v��<�)�)�e�G�_�ײ��R �K��g�;-�m�1E�m�JrJ�|Ƽ}2f`2-��R���lYG�I�����:Q}�����e���PFTc 7 | �N� =��C��� <��9�:�� _7�c��y���*ce����6���`M��5���7R�~4`�m�ȋ�g�����wv�33�1 8 | .Q�2f����ayؾsS����w�s �r��l��"L|�4SÖ͝c��>"�VK�>�/����[��s�n���u�WU6aa��Bl�m����=��#E�s��hӚ�d4�h=}*l�����J��hn�j�{�B�k��|-!��@�r������f�2n ��4 9 | ��baw� 10 | �0� 11 | ��e%����B= ��B���I�&�"_�oA������'�����Xwi�� �`4�N�o���� �p�c#�3�]���qo 56l�ei���d$�;_c��Κo 12 | "��F P�l���L�v����g��!:�i��@�P�Yt�|n5q=�hYV'��<(� �|�(4N��%Q��L�,���ډ���S�LO�y����� �@,��oF=�Vp ��;�l��&;M���h4�k`�2����;�]����ىs��0 13 |  *�H�� 14 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinFreemarker.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import freemarker.template.Configuration 10 | import freemarker.template.Version 11 | import io.javalin.http.Context 12 | import io.javalin.rendering.FileRenderer 13 | import io.javalin.rendering.util.RenderingDependency.FREEMARKER 14 | import io.javalin.rendering.util.Util 15 | import java.io.StringWriter 16 | 17 | class JavalinFreemarker @JvmOverloads constructor( 18 | private val configuration: Configuration = defaultFreemarkerEngine() 19 | ) : FileRenderer { 20 | 21 | init { 22 | Util.throwIfNotAvailable(FREEMARKER) 23 | } 24 | 25 | override fun render(filePath: String, model: Map, context: Context): String { 26 | val stringWriter = StringWriter() 27 | configuration.getTemplate(filePath).process(model, stringWriter) 28 | return stringWriter.toString() 29 | } 30 | 31 | companion object { 32 | fun defaultFreemarkerEngine() = Configuration(Version(2, 3, 26)).apply { 33 | setClassForTemplateLoading(JavalinFreemarker::class.java, "/") 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldSse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.http.sse.SseClient; 11 | import java.util.Queue; 12 | import java.util.concurrent.ConcurrentLinkedQueue; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | public class HelloWorldSse { 16 | 17 | public static void main(String[] args) throws InterruptedException { 18 | 19 | Queue clients = new ConcurrentLinkedQueue<>(); 20 | 21 | Javalin app = Javalin.create().start(7000); 22 | app.get("/", ctx -> ctx.html("")); 23 | app.sse("/sse", client -> { 24 | client.keepAlive(); 25 | clients.add(client); // save the sse to use outside of this context 26 | client.onClose(() -> clients.remove(client)); 27 | }); 28 | 29 | while (true) { 30 | for (SseClient client : clients) { 31 | client.sendEvent("hi", "hello world"); 32 | } 33 | TimeUnit.SECONDS.sleep(1); 34 | } 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/config/ContextResolverConfig.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.config 2 | 3 | import io.javalin.http.Context 4 | import io.javalin.http.Header 5 | 6 | /** 7 | * Configure the implementation for Context functions. 8 | * 9 | * @see [JavalinConfig.contextResolver] 10 | */ 11 | class ContextResolverConfig { 12 | companion object { 13 | internal val ContextResolverKey = Key("javalin-context-resolver") 14 | } 15 | // @formatter:off 16 | /** The IP address resolver (default: reads the `remoteAddr` part of the request) */ 17 | @JvmField var ip: (Context) -> String = { it.req().remoteAddr } 18 | /** The host resolver (default: reads the `Host` header). */ 19 | @JvmField var host: (Context) -> String? = { it.header(Header.HOST) } 20 | /** The scheme resolver (default: reads the `scheme` part of the request). */ 21 | @JvmField var scheme: (Context) -> String = { it.req().scheme } 22 | /** The URL resolver (default: reads the `requestUrl` part of the request). */ 23 | @JvmField var url: (Context) -> String = { it.req().requestURL.toString() } 24 | /** The full URL resolver (default: reads the url with its query string). */ 25 | @JvmField var fullUrl: (Context) -> String = { it.url() + if (it.queryString() != null) "?" + it.queryString() else "" } 26 | // @formatter:on 27 | } 28 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/json/PipedStreamUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.json 2 | 3 | import io.javalin.util.ConcurrencyUtil 4 | import io.javalin.util.javalinLazy 5 | import java.io.InputStream 6 | import java.io.PipedInputStream 7 | import java.io.PipedOutputStream 8 | 9 | class PipedStreamExecutor(useVirtualThreads: Boolean) { 10 | 11 | private val executorService by javalinLazy { ConcurrencyUtil.executorService("JavalinPipedStreamingThreadPool", useVirtualThreads) } 12 | 13 | fun getInputStream(userCallback: (PipedOutputStream) -> Unit): InputStream { 14 | val pipedOutputStream = PipedOutputStream() 15 | val pipedInputStream = object : PipedInputStream(pipedOutputStream) { 16 | var exception: Exception? = null // possible exception from child thread 17 | override fun close() = exception?.let { throw it } ?: super.close() 18 | } 19 | executorService.execute { // start child thread, necessary to prevent deadlock 20 | try { 21 | userCallback(pipedOutputStream) 22 | } catch (userException: Exception) { 23 | pipedInputStream.exception = userException // pass exception to parent thead 24 | } finally { 25 | pipedOutputStream.close() 26 | } 27 | } 28 | return pipedInputStream 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/client-nochain.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDgjCCAmqgAwIBAgIEY8ADejANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJO 3 | TzEQMA4GA1UECgwHSmF2YWxpbjEhMB8GA1UECwwYSW50ZXJtZWRpYXRlIChJc3N1 4 | ZXIpIENBMRIwEAYDVQQDDAlpc3N1ZXItY2EwIBcNMjMwMTEyMTI1NjI2WhgPMjEy 5 | MzAxMTIxMjU2MjZaMEUxCzAJBgNVBAYTAk5PMRAwDgYDVQQKDAdKYXZhbGluMRMw 6 | EQYDVQQLDApUTFMgQ2xpZW50MQ8wDQYDVQQDDAZjbGllbnQwggEiMA0GCSqGSIb3 7 | DQEBAQUAA4IBDwAwggEKAoIBAQDYMknUfY1UkypmoR7HTXcQwqK7KA3EqhQ5hwq5 8 | 9bRBDrxhDtZ8Sb07pmB7UvqwY690rA03pKls7qaRUd8M/n5dk6KQfb37IlU/RidU 9 | /gRN6R34biLoE/H2vQJW7ae0XocVkbgPJNWz1+1BVFBmFbVqkIElnkurlJ5PmC+E 10 | 3oKCZMl3RHBFOLg25ro7/RhyTh9uJjE41weU7fI0fAiCuxJd1CABteSbI0ZKX660 11 | VNshmbLRlgvziqlAgNmric7/KoJmcTFA9f9+q/9yGptafPwlchqk6LaiLgxK+/5P 12 | oaKve0aqwDSBvD0lMQRpr1wE8AymKI5OQHURlCIhoGCtA2FVAgMBAAGjZzBlMA4G 13 | A1UdDwEB/wQEAwIFoDAfBgNVHSMEGDAWgBR2vQOS8elWjlZVncVxaS6OIq8kCDAd 14 | BgNVHQ4EFgQUAy/Wki7+pkBlZ5tDOIJpSaYP5lUwEwYDVR0lBAwwCgYIKwYBBQUH 15 | AwIwDQYJKoZIhvcNAQELBQADggEBALh1rZWFP/shgih6D5sKn5H1xRpIs3moTfly 16 | Dn/poo5to+5wn59Drr5aAAUDcmprJrfG+hzOX/2/im/JolE6yIYJRjhSBFl0b0/a 17 | YaRIRlV8/NWGzTayu8zlIF5xQFmozD9I95Xs9OBslJ3RTz+uMXvmCqu+jWYYjIlZ 18 | Zg7+JwXH9SBItjuAYpvD075nkzPxT9AuTfhU1yelmXxR8W6LMQflB1kIYx/u+75A 19 | 1XWdl8tYNyuyhuEbuMnLfY/ehOPJVCaILL8dBPpakyfl6zT599TMQpGqJxy43Mk3 20 | BpYHCXFcNqm2ZEbioMrJopv8ehz5ASZGStg+/2liZlP+VsvfzuA= 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/root-ca.cer: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDhzCCAm+gAwIBAgIEY7/+SjANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJO 3 | TzEQMA4GA1UECgwHSmF2YWxpbjEfMB0GA1UECwwWQ2VydGlmaWNhdGUgQXV0aG9y 4 | aXR5IDEQMA4GA1UEAwwHcm9vdC1jYTAgFw0yMzAxMTIxMjM0MThaGA8yMTIzMDEx 5 | MjEyMzQxOFowUjELMAkGA1UEBhMCTk8xEDAOBgNVBAoMB0phdmFsaW4xHzAdBgNV 6 | BAsMFkNlcnRpZmljYXRlIEF1dGhvcml0eSAxEDAOBgNVBAMMB3Jvb3QtY2EwggEi 7 | MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDgiP9ZQzoK6doYFJgheTWJOxNP 8 | N06vhVgJ9iUBVZAp+3qD7lCz0uqJrQHhj9/3j6SjWJMW8X2znwvIvy8/RLeZbEhc 9 | kiQTPhV6rwXhh1ejAWLudGTaPe25DUITpyQeolRag+ZnBQjgkunyzaVWiHdhAH9r 10 | OLzI5ARlpWjCcPPDZuUMgHxwxpqZ/BfccfcPIk+RJf9ZwvWq1e7O8AYTMmZIVEus 11 | y/q/UqXDtpTOB5lcBY+qsnunzcMGgzk0dF5RRUJbT0JCXDvTw9FTkveozrcI4xlL 12 | CM4w/9x90wgz+fqqTQKJqtuMWR7E5sWigHvI+iHYZjS0jKuUEk7Vm8yVZHGHAgMB 13 | AAGjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB8GA1UdIwQY 14 | MBaAFEfUujjST8uJf6w2fzOLrSCkPG7hMB0GA1UdDgQWBBRH1Lo40k/LiX+sNn8z 15 | i60gpDxu4TANBgkqhkiG9w0BAQsFAAOCAQEAuhUHRMXiHVreEl52BXIDGboq4fbl 16 | RfSpXFM+Hp70WBiOJto5q4DVoBe5Q3mnwdNhHPUDXaPiGxZwJtISl2P05DA48LOE 17 | p700pRU9W1VqjWRdTh+Tqaec/x3r5dmBCe/pfSUM07PWeP5OiVJoopE5BDra2c2X 18 | luyosLhuluqXhZn62lnyDz3XsOP7sx+q+7X6EGgzUOJTh5aHIIY2JkRXtxytQvgS 19 | TlIXtm11BOZ+rwvKhuOMtHP5nrlUjp7K2usWKZ29NB7lPwGw1HRJjF1/gwgBl1zI 20 | IZFjI2C3o2f6F1TLSZP8ImAoRmUkHhfEMKQi3jJqV5ud7F4MWQ0kKupKAA== 21 | -----END CERTIFICATE----- 22 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/sse/Emitter.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.sse 2 | 3 | import jakarta.servlet.http.HttpServletResponse 4 | import java.io.IOException 5 | import java.io.InputStream 6 | 7 | const val COMMENT_PREFIX = ":" 8 | const val NEW_LINE = "\n" 9 | 10 | class Emitter(private var response: HttpServletResponse) { 11 | 12 | var closed = false 13 | private set 14 | 15 | fun emit(event: String, data: InputStream, id: String?) = synchronized(this) { 16 | try { 17 | if (id != null) { 18 | write("id: $id$NEW_LINE") 19 | } 20 | write("event: $event$NEW_LINE") 21 | 22 | data.buffered().reader().useLines { 23 | it.forEach { line -> write("data: $line$NEW_LINE") } 24 | } 25 | 26 | write(NEW_LINE) 27 | response.flushBuffer() 28 | } catch (ignored: IOException) { 29 | closed = true 30 | } 31 | } 32 | 33 | fun emit(comment: String) = 34 | try { 35 | comment.split(NEW_LINE).forEach { 36 | write("$COMMENT_PREFIX $it$NEW_LINE") 37 | } 38 | response.flushBuffer() 39 | } catch (ignored: IOException) { 40 | closed = true 41 | } 42 | 43 | private fun write(value: String) = 44 | response.outputStream.print(value) 45 | 46 | } 47 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldCors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | 11 | import static io.javalin.apibuilder.ApiBuilder.get; 12 | import static io.javalin.apibuilder.ApiBuilder.patch; 13 | import static io.javalin.apibuilder.ApiBuilder.post; 14 | 15 | public class HelloWorldCors { 16 | 17 | public static void main(String[] args) { 18 | Javalin.create(config -> { 19 | config.bundledPlugins.enableCors(cors -> { 20 | cors.addRule(corsConfig -> { 21 | corsConfig.allowHost("http://localhost:7001/", "http://localhost:7002"); 22 | }); 23 | }); 24 | config.router.apiBuilder(() -> { 25 | get(ctx -> ctx.json("Hello Get")); 26 | post(ctx -> ctx.json("Hello Post")); 27 | patch(ctx -> ctx.json("Hello Patch")); 28 | }); 29 | }).start(7070); 30 | 31 | Javalin.create().start(7001).get("/", ctx -> ctx.html("Try some CORS")); 32 | Javalin.create().start(7002).get("/", ctx -> ctx.html("Try some CORS")); 33 | Javalin.create().start(7003).get("/", ctx -> ctx.html("No CORS here")); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /javalin-utils/coverage/README.md: -------------------------------------------------------------------------------- 1 | # Javalin Utils - Coverage 2 | 3 | This module is used for Jacoco code coverage report aggregation. 4 | 5 | ## Jacoco Report Path 6 | 7 | The Jacoco report can be found at the following path, relative to the root of the project: 8 | 9 | `javalin-utils/coverage/target/site/jacoco-aggregate/jacoco.xml` 10 | 11 | ## Generating the Jacoco Report 12 | 13 | To generate the Jacoco report, run the following command from the root of the project: 14 | 15 | `mvn clean verify` 16 | 17 | ## Viewing the Jacoco Report 18 | 19 | The Jacoco report can be viewed by opening the following file in a web browser: 20 | 21 | `javalin-utils/coverage/target/site/jacoco-aggregate/index.html` 22 | 23 | Or by using Codecov to view the report online here: [![codecov](https://codecov.io/gh/javalin/javalin/graph/badge.svg?token=3L3CvpyMPI)](https://codecov.io/gh/javalin/javalin) 24 | 25 | ## Coverage graphs 26 | 27 | ### Sunburst 28 | 29 | [![codecov](https://codecov.io/gh/javalin/javalin/graphs/sunburst.svg?token=3L3CvpyMPI 30 | )](https://codecov.io/gh/javalin/javalin) 31 | 32 | ### Icicle 33 | 34 | [![codecov](https://codecov.io/gh/javalin/javalin/graphs/icicle.svg?token=3L3CvpyMPI 35 | )](https://codecov.io/gh/javalin/javalin) 36 | 37 | ### Treemap / Grid 38 | 39 | [![codecov](https://codecov.io/gh/javalin/javalin/graphs/treemap.svg?token=3L3CvpyMPI 40 | )](https://codecov.io/gh/javalin/javalin) 41 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinPebble.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import com.mitchellbosecke.pebble.PebbleEngine 10 | import com.mitchellbosecke.pebble.loader.ClasspathLoader 11 | import io.javalin.http.Context 12 | import io.javalin.rendering.FileRenderer 13 | import io.javalin.rendering.util.RenderingDependency.PEBBLE 14 | import io.javalin.rendering.util.Util 15 | import java.io.StringWriter 16 | 17 | class JavalinPebble @JvmOverloads constructor( 18 | private var pebbleEngine: PebbleEngine = defaultPebbleEngine() 19 | ) : FileRenderer { 20 | 21 | init { 22 | Util.throwIfNotAvailable(PEBBLE) 23 | } 24 | 25 | override fun render(filePath: String, model: Map, context: Context): String { 26 | val compiledTemplate = pebbleEngine.getTemplate(filePath) 27 | val stringWriter = StringWriter() 28 | compiledTemplate.evaluate(stringWriter, model) 29 | return stringWriter.toString() 30 | } 31 | 32 | companion object { 33 | private fun defaultPebbleEngine() = PebbleEngine.Builder() 34 | .loader(ClasspathLoader()) 35 | .strictVariables(false) 36 | .build() 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDmDCCAoCgAwIBAgIUdWY83fnUuYRDmDnHi34wkeG+yA4wDQYJKoZIhvcNAQEL 3 | BQAwUjEPMA0GA1UEAwwGY2xpZW50MQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFk 4 | cmlkMQ8wDQYDVQQHDAZNYWRyaWQxEDAOBgNVBAoMB0phdmFsaW4wIBcNMjMwMTEw 5 | MTEwNTE0WhgPMjEyMjEyMTcxMTA1MTRaMFIxDzANBgNVBAMMBmNsaWVudDELMAkG 6 | A1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMRAwDgYD 7 | VQQKDAdKYXZhbGluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsCUt 8 | UOTWxQ3zbVXK8FPFh4DsDCphoUt/k6v8n4miZWxfw45VqsSk5JktmFhqSsmerh0N 9 | cQZ7rji69LK/dr4wfZJjWLGlyNDJnT1W/PP3HaGErJxDl/NqLjl+xULXsp7+/SP7 10 | Jz6QcEKmDYOyQND79MaYXlhkCLtt/RslfIP1YQ4AFCGcw4z/cGERuMtcLY8FFT+N 11 | U4OD26AZX4fAQ+fQRAdALzp63wCnWiYyQ+0Nqeq4wDM+HYlAsUbwSwiJSseIVn2u 12 | nn1kQq45TUcL8HUuVGr9CF8PyvkOLxbdzC0q43MfPDck7CgqR2YG9XHrca9cT6c+ 13 | zE+BGhjzOjlAxUCYqwIDAQABo2QwYjAdBgNVHQ4EFgQU3MAhBUHI6S9obysrX38v 14 | HiGLmIcwHwYDVR0jBBgwFoAU3MAhBUHI6S9obysrX38vHiGLmIcwCwYDVR0PBAQD 15 | AgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBVjsXw 16 | P4ZbUt5XqN8Iy30YqBi90OtfcmWxVnuc7O/HU2ue+PLM3rRyKYVgwY6G/HoRAibq 17 | HsLnGa/2jT+qzri68CKfjE/wmBIAgzaKF4XmhOxIxQjTUuxbVd4BmrDmGqsMgWoQ 18 | 5XnYvyBQJ9LTnvvxPk4UvhUjm1/6RbPFqqxVenTUpYS4U3JTv1c9ddu9VSA8ebT4 19 | BGBVq2iwgTm38xN9C6eS/xGCdLXGIaEQvmfgAPi1Nmw32KrLJfL3oz9bWdYhp9Hg 20 | fZg2Pug5bLDqy8ktyTDdM+q4d+wd3XpKzuLvCIr2q03vrT9j+dMIEOTaqxWQAYiH 21 | CYGXrU6Ry61UJSer 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/cert.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDpjCCAo6gAwIBAgIUKK29nJVFCs8SjBqcvxrg7boyem8wDQYJKoZIhvcNAQEL 3 | BQAwQjESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJFUzEQMA4GA1UECAwH 4 | R2FsaWNpYTENMAsGA1UEBwwEVmlnbzAgFw0yMjA3MDYxMTQyMDdaGA80MDA1MDMx 5 | MjExNDIwN1owQjESMBAGA1UEAwwJbG9jYWxob3N0MQswCQYDVQQGEwJFUzEQMA4G 6 | A1UECAwHR2FsaWNpYTENMAsGA1UEBwwEVmlnbzCCASIwDQYJKoZIhvcNAQEBBQAD 7 | ggEPADCCAQoCggEBALtW247iPVAuCcQByuqgj8tSzJcwVqCmheT6ld0Xe7DYoLOL 8 | EsjilB/jgG9aBEBfYJ2h74K7SIdqiSDz4rgUuJUzhZnJo5d3n3wT9Wb2AZcsqFce 9 | JK0UNBKe2/1b01dFWtQFW4zHC/JM/Gp0dMTy1Vt1Zf/3SmQjSD/KzgJf4m2O/GOP 10 | 3iRFsCSPC4CU3TZCDmI5/qRr4icJCY5s3gJ+RT+edfsvtdkfAO4hK/p+37RrwHax 11 | nyFLoAzYdJMcnDX/+V7Ez2y7jkTkcUk2gKG+3dpio2XqAE9pXcXa4kYk0NL9Vw6L 12 | C2QMefFKHLDqLWx/bfQXpbULFawldETDbuLVe7UCAwEAAaOBkTCBjjAdBgNVHQ4E 13 | FgQUiiPTBoFstcGbb0zYWsM/ZwupRRYwHwYDVR0jBBgwFoAUiiPTBoFstcGbb0zY 14 | WsM/ZwupRRYwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr 15 | BgEFBQcDAjAMBgNVHRMBAf8EAjAAMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcN 16 | AQELBQADggEBAGvqUrtYWZpKBJNYL4UVLnm2+dQl33l8BH7PhU6YvMufThDCVjOw 17 | IJ7ezOReDlCAmytQD7ChKpsJrAOBzKRdrifL0f88psbE83+6Ys/s/1rHMq282p/S 18 | WPRiZDVO8Mw2ra9v9b6cprW5phHJkp7TiIBP82A+v19lt3R+vE4HZ91ZyioNqMzf 19 | Aqvd5gfxHexpilgil0osF0o/8ajSnLiBfWI82Lz/1JB+xUMYW91ahRgt13/54h13 20 | eL70steoAmx55he3pQaaeRZKzI1nLxsrTkjs055jDn0G/yj1L6kY3OeVFg3AhETJ 21 | sg+yATMTef2Qskr4dgzb1LJkC9meaU2TFwk= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/util/JavalinLogger.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.util 2 | 3 | import io.javalin.Javalin 4 | import org.slf4j.LoggerFactory 5 | 6 | // @formatter:off 7 | object JavalinLogger { 8 | 9 | private val log = LoggerFactory.getLogger(Javalin::class.java)!! 10 | 11 | @JvmField var enabled = true 12 | @JvmField var startupInfo = true 13 | 14 | @JvmStatic fun startup(message: String) { 15 | if (!enabled) return 16 | if (startupInfo) log.info(message) 17 | } 18 | 19 | @JvmOverloads @JvmStatic fun info(message: String, throwable: Throwable? = null) { 20 | if (!enabled) return 21 | if (throwable != null) log.info(message, throwable) else log.info(message) 22 | } 23 | 24 | @JvmOverloads @JvmStatic fun warn(message: String, throwable: Throwable? = null) { 25 | if (!enabled) return 26 | if (throwable != null) log.warn(message, throwable) else log.warn(message) 27 | } 28 | 29 | @JvmOverloads @JvmStatic fun error(message: String, throwable: Throwable? = null) { 30 | if (!enabled) return 31 | if (throwable != null) log.error(message, throwable) else log.error(message) 32 | } 33 | 34 | @JvmOverloads @JvmStatic fun debug(message: String, throwable: Throwable? = null) { 35 | if (!enabled) return 36 | if (throwable != null) log.debug(message, throwable) else log.debug(message) 37 | } 38 | 39 | } 40 | // @formatter:on 41 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestConcurrencyUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.util.ReentrantLazy 4 | import org.assertj.core.api.Assertions.assertThat 5 | import org.junit.jupiter.api.Test 6 | import java.util.concurrent.CountDownLatch 7 | import java.util.concurrent.Executors 8 | import java.util.concurrent.atomic.AtomicInteger 9 | 10 | internal class TestConcurrencyUtil { 11 | 12 | @Test 13 | fun `reentrant lazy should work like synchronized lazy`() { 14 | val threads = Runtime.getRuntime().availableProcessors() * 2 15 | val await = CountDownLatch(threads) 16 | val counter = AtomicInteger(0) 17 | 18 | val lazy = ReentrantLazy { 19 | Thread.sleep(1000) 20 | counter.incrementAndGet() 21 | } 22 | 23 | val fixedExecutor = Executors.newFixedThreadPool(threads) 24 | 25 | for (thread in 0..threads) { 26 | fixedExecutor.execute { 27 | lazy.value 28 | await.countDown() 29 | } 30 | } 31 | 32 | await.await() 33 | assertThat(lazy.isInitialized()) 34 | assertThat(lazy.value).isEqualTo(1) 35 | assertThat(counter.get()).isEqualTo(1) 36 | } 37 | 38 | @Test 39 | fun `reentrant lazy should work like other kotlin lazy`() { 40 | val lazy: Lazy = ReentrantLazy { 1 } 41 | val lazyBy by lazy 42 | assertThat(lazyBy).isEqualTo(1) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldServlet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.testing.TestServlet; 11 | import org.eclipse.jetty.server.Handler; 12 | import org.eclipse.jetty.server.Server; 13 | import org.eclipse.jetty.server.handler.ContextHandlerCollection; 14 | import org.eclipse.jetty.servlet.ServletContextHandler; 15 | 16 | public class HelloWorldServlet { 17 | 18 | public static void main(String[] args) { 19 | Javalin app = Javalin.create(config -> { 20 | config.router.contextPath = "/api"; 21 | 22 | Server server = new Server(); 23 | ServletContextHandler context = new ServletContextHandler(); 24 | context.setContextPath("/test-servlet"); 25 | //Servlet will respond to all requests beginning with /test-servlet 26 | context.addServlet(TestServlet.class, "/"); 27 | ContextHandlerCollection handlers = new ContextHandlerCollection(); 28 | handlers.setHandlers(new Handler[]{context}); 29 | server.setHandler(handlers); 30 | 31 | config.pvt.jetty.server = server; 32 | }); 33 | app.get("/", ctx -> ctx.result("Hello Javalin World!")); 34 | app.start(8000); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/cert.p7b: -------------------------------------------------------------------------------- 1 | -----BEGIN PKCS7----- 2 | MIIDyQYJKoZIhvcNAQcCoIIDujCCA7YCAQExADALBgkqhkiG9w0BBwGgggOcMIID 3 | mDCCAoCgAwIBAgIUdWY83fnUuYRDmDnHi34wkeG+yA4wDQYJKoZIhvcNAQELBQAw 4 | UjEPMA0GA1UEAwwGY2xpZW50MQswCQYDVQQGEwJFUzEPMA0GA1UECAwGTWFkcmlk 5 | MQ8wDQYDVQQHDAZNYWRyaWQxEDAOBgNVBAoMB0phdmFsaW4wIBcNMjMwMTEwMTEw 6 | NTE0WhgPMjEyMjEyMTcxMTA1MTRaMFIxDzANBgNVBAMMBmNsaWVudDELMAkGA1UE 7 | BhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMRAwDgYDVQQK 8 | DAdKYXZhbGluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsCUtUOTW 9 | xQ3zbVXK8FPFh4DsDCphoUt/k6v8n4miZWxfw45VqsSk5JktmFhqSsmerh0NcQZ7 10 | rji69LK/dr4wfZJjWLGlyNDJnT1W/PP3HaGErJxDl/NqLjl+xULXsp7+/SP7Jz6Q 11 | cEKmDYOyQND79MaYXlhkCLtt/RslfIP1YQ4AFCGcw4z/cGERuMtcLY8FFT+NU4OD 12 | 26AZX4fAQ+fQRAdALzp63wCnWiYyQ+0Nqeq4wDM+HYlAsUbwSwiJSseIVn2unn1k 13 | Qq45TUcL8HUuVGr9CF8PyvkOLxbdzC0q43MfPDck7CgqR2YG9XHrca9cT6c+zE+B 14 | GhjzOjlAxUCYqwIDAQABo2QwYjAdBgNVHQ4EFgQU3MAhBUHI6S9obysrX38vHiGL 15 | mIcwHwYDVR0jBBgwFoAU3MAhBUHI6S9obysrX38vHiGLmIcwCwYDVR0PBAQDAgeA 16 | MBMGA1UdJQQMMAoGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBVjsXwP4Zb 17 | Ut5XqN8Iy30YqBi90OtfcmWxVnuc7O/HU2ue+PLM3rRyKYVgwY6G/HoRAibqHsLn 18 | Ga/2jT+qzri68CKfjE/wmBIAgzaKF4XmhOxIxQjTUuxbVd4BmrDmGqsMgWoQ5XnY 19 | vyBQJ9LTnvvxPk4UvhUjm1/6RbPFqqxVenTUpYS4U3JTv1c9ddu9VSA8ebT4BGBV 20 | q2iwgTm38xN9C6eS/xGCdLXGIaEQvmfgAPi1Nmw32KrLJfL3oz9bWdYhp9HgfZg2 21 | Pug5bLDqy8ktyTDdM+q4d+wd3XpKzuLvCIr2q03vrT9j+dMIEOTaqxWQAYiHCYGX 22 | rU6Ry61UJSeroQAxAA== 23 | -----END PKCS7----- 24 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.examples 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.http.Context 5 | import io.javalin.http.TooManyRequestsResponse 6 | import io.javalin.plugin.ContextPlugin 7 | import java.util.function.Consumer 8 | 9 | fun main() { 10 | val app = Javalin.create { 11 | it.registerPlugin(KRate { it.limit = 3 }) 12 | } 13 | app.get("/") { ctx -> 14 | ctx.with(KRate::class).tryConsume(cost = 5) // will throw because limit is 3 15 | ctx.result("Hello World") 16 | } 17 | } 18 | 19 | // this class demonstrates the most advanced use case of a plugin, 20 | // where the plugin has a config and a plugin extension 21 | // we recommend using inner classes for plugins, as it keeps the whole plugin in one place 22 | class KRate(userConfig: Consumer) : ContextPlugin(userConfig, Config()) { 23 | val ipToCounter = mutableMapOf() 24 | override fun createExtension(context: Context) = Extension(context) 25 | class Config(var limit: Int = 0) 26 | inner class Extension(var context: Context) { 27 | fun tryConsume(cost: Int = 1) { 28 | val ip = context.ip() 29 | val counter = ipToCounter.compute(ip) { _, v -> v?.plus(cost) ?: cost }!! 30 | if (counter > pluginConfig.limit) { 31 | throw TooManyRequestsResponse() 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/vue/VueStateRenderer.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.vue 2 | 3 | import io.javalin.http.Context 4 | import io.javalin.json.toJsonString 5 | import io.javalin.vue.JavalinVueConfig.Companion.VueConfigKey 6 | import org.eclipse.jetty.util.URIUtil 7 | 8 | internal object VueStateRenderer { 9 | fun getState(ctx: Context, state: Any?): String { 10 | val cfg = ctx.appData(VueConfigKey) 11 | fun urlEncodedState(state: Any?): String = ctx.jsonMapper() 12 | .toJsonString( 13 | mapOf( 14 | "pathParams" to ctx.pathParamMap(), 15 | "state" to (state ?: cfg.stateFunction(ctx)) 16 | ) 17 | ) 18 | .urlEncodeForJavascript() 19 | 20 | val prototypeOrGlobalConfig = if (cfg.vueInstanceNameInJs != null) "${cfg.vueInstanceNameInJs}.config.globalProperties" else "Vue.prototype" 21 | return """ 22 | 26 | """.trimIndent() 27 | } 28 | 29 | // Unfortunately, Java's URLEncoder#encode is slightly different from JavaScript's encodeURIComponent. 30 | // Luckily, Jetty has a util which works exactly the same. 31 | private fun String.urlEncodeForJavascript() = URIUtil.encodePath(this) 32 | } 33 | -------------------------------------------------------------------------------- /javalin-utils/javalin-context-mock/src/main/java/io/javalin/mock/servlet/InMemoryPart.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.mock.servlet 2 | 3 | import jakarta.servlet.http.Part 4 | import java.io.InputStream 5 | import java.nio.file.Files 6 | import java.nio.file.Paths 7 | 8 | // @formatter:off 9 | class InMemoryPart(private val state: PartState) : Part { 10 | 11 | class PartState @JvmOverloads constructor( 12 | @JvmField val name: String, 13 | @JvmField val fileName: String, 14 | @JvmField val content: ByteArray, 15 | @JvmField val size: Long = content.size.toLong(), 16 | @JvmField val contentType: String = "application/octet-stream", 17 | @JvmField val headers: Map> = emptyMap() 18 | ) 19 | 20 | override fun getInputStream(): InputStream = state.content.inputStream() 21 | override fun getContentType(): String = state.contentType 22 | override fun getName(): String = state.name 23 | override fun getSubmittedFileName(): String = state.fileName 24 | override fun getSize(): Long = state.size 25 | override fun write(fileName: String) { Files.write(Paths.get(fileName), state.content) } 26 | override fun delete() {} 27 | override fun getHeader(name: String): String? = state.headers[name]?.firstOrNull() 28 | override fun getHeaders(name: String): Collection = state.headers[name] ?: emptyList() 29 | override fun getHeaderNames(): Collection = state.headers.keys 30 | 31 | } 32 | // @formatter:on 33 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/util/ReflectionUtil.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.util 2 | 3 | internal val Any.kotlinFieldName // this is most likely a very stupid solution 4 | get() = this.javaClass.toString().removePrefix(this.parentClass.toString() + "$").takeWhile { it != '$' } 5 | 6 | internal val Any.javaFieldName: String? 7 | get() = try { 8 | parentClass.declaredFields.find { it.isAccessible = true; it.get(it) == this }?.name 9 | } catch (ignored: Exception) { // Nothing really matters. 10 | null 11 | } 12 | 13 | internal val Any.parentClass: Class<*> get() = Class.forName(this.javaClass.name.takeWhile { it != '$' }, false, this.javaClass.classLoader) 14 | 15 | internal val Any.implementingClassName: String? get() = this.javaClass.name 16 | 17 | internal val Any.isClass: Boolean get() = this is Class<*> 18 | 19 | internal val Any.isKotlinAnonymousLambda: Boolean get() = this.javaClass.enclosingMethod != null 20 | 21 | internal val Any.isKotlinMethodReference: Boolean get() = this.javaClass.declaredFields.count { it.name == "function" || it.name == "\$tmp0" } == 1 22 | 23 | internal val Any.isKotlinField: Boolean get() = this.javaClass.fields.any { it.name == "INSTANCE" } 24 | 25 | internal val Any.isJavaAnonymousLambda: Boolean get() = this.javaClass.isSynthetic 26 | 27 | internal val Any.isJavaField: Boolean get() = this.javaFieldName != null 28 | 29 | internal fun Any.runMethod(name: String): Any = this.javaClass.getMethod(name).apply { isAccessible = true }.invoke(this) 30 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldAsync.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.plugin.bundled.DevLoggingPlugin 12 | import java.util.concurrent.CompletableFuture 13 | import java.util.concurrent.Executors 14 | import java.util.concurrent.TimeUnit 15 | 16 | fun main() { 17 | Javalin.create { 18 | val scheduledExecutor = Executors.newSingleThreadScheduledExecutor() 19 | 20 | it.router.apiBuilder { 21 | get("/result") { ctx -> 22 | ctx.future { 23 | val future = CompletableFuture() 24 | scheduledExecutor.schedule({ future.complete("Hello World!") }, 10, TimeUnit.MILLISECONDS) 25 | future.thenApply { ctx.result(it) } 26 | } 27 | } 28 | get("/json") { ctx -> 29 | ctx.future { 30 | val future = CompletableFuture>() 31 | scheduledExecutor.schedule({ future.complete(listOf("a", "b", "c")) }, 10, TimeUnit.MILLISECONDS) 32 | future.thenApply { ctx.json(it) } 33 | } 34 | } 35 | } 36 | 37 | it.registerPlugin(DevLoggingPlugin()) 38 | }.start(7070) 39 | } 40 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldStaticFiles_linked.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.http.staticfiles.Location; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.nio.file.Path; 14 | import java.nio.file.Paths; 15 | import org.eclipse.jetty.server.handler.ContextHandler; 16 | 17 | public class HelloWorldStaticFiles_linked { 18 | 19 | public static void main(String[] args) { 20 | createSymLink("src/test/external/html.html", "src/test/external/linked_html.html"); 21 | 22 | Javalin.create(javalin -> { 23 | javalin.staticFiles.add(staticFiles -> { 24 | staticFiles.directory = "src/test/external/"; 25 | staticFiles.location = Location.EXTERNAL; 26 | staticFiles.aliasCheck = new ContextHandler.ApproveAliases(); 27 | }); 28 | }).start(7070); 29 | } 30 | 31 | private static void createSymLink(String targetPath, String linkPath) { 32 | Path target = Paths.get(targetPath).toAbsolutePath(); 33 | Path link = Paths.get(linkPath).toAbsolutePath(); 34 | try { 35 | Files.createSymbolicLink(link, target); 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldSecure.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import org.eclipse.jetty.server.ServerConnector 11 | import org.eclipse.jetty.util.ssl.SslContextFactory 12 | 13 | // This is a very basic example, a better one can be found at: 14 | // https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java#L139-L163 15 | fun main() { 16 | val app = Javalin.create { 17 | it.jetty.addConnector { server, _ -> 18 | val sslConnector = ServerConnector(server, sslContextFactory()) 19 | sslConnector.port = 443 20 | sslConnector 21 | } 22 | it.jetty.addConnector { server, _ -> 23 | val httpConnector = ServerConnector(server) 24 | httpConnector.port = 80 25 | httpConnector 26 | } 27 | }.start() 28 | 29 | app.get("/") { it.result("Hello World") } // valid endpoint for both connectors 30 | } 31 | 32 | private fun sslContextFactory(): SslContextFactory.Server { 33 | val sslContextFactory = SslContextFactory.Server() 34 | sslContextFactory.keyStorePath = HelloWorldSecure::class.java.getResource("/keystore.jks")!!.toExternalForm() 35 | sslContextFactory.keyStorePassword = "localhost" 36 | return sslContextFactory 37 | } 38 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/plugin/bundled/BasicAuthPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.plugin.bundled 8 | 9 | import io.javalin.config.JavalinConfig 10 | import io.javalin.http.Header.WWW_AUTHENTICATE 11 | import io.javalin.http.UnauthorizedResponse 12 | import io.javalin.plugin.Plugin 13 | import java.util.function.Consumer 14 | 15 | /** 16 | * Adds a filter that runs before every http request. 17 | * Note: It does not apply to websocket upgrade requests 18 | */ 19 | class BasicAuthPlugin(userConfig: Consumer) : Plugin(userConfig, Config()) { 20 | 21 | class Config { 22 | @JvmField var username: String? = null 23 | @JvmField var password: String? = null 24 | } 25 | 26 | override fun onStart(config: JavalinConfig) { 27 | config.router.mount { 28 | it.before { ctx -> 29 | val matched = runCatching { ctx.basicAuthCredentials() } 30 | .fold( 31 | onSuccess = { auth -> auth?.username == pluginConfig.username && auth?.password == pluginConfig.password }, 32 | onFailure = { false } 33 | ) 34 | 35 | if (!matched) { 36 | ctx.header(WWW_AUTHENTICATE, "Basic") 37 | throw UnauthorizedResponse() 38 | } 39 | } 40 | } 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/validation/Validator.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.validation 8 | 9 | import org.jetbrains.annotations.NotNull 10 | 11 | /** 12 | * The non-nullable [Validator] uses [Rule] rules, but checks if value is null before calling them. 13 | * The [check] method wraps its non-nullable predicate in a nullable predicate 14 | */ 15 | open class Validator internal constructor(params: Params) : BaseValidator(params) { 16 | 17 | fun allowNullable(): NullableValidator { 18 | if (this.rules.isEmpty()) return NullableValidator(params) 19 | throw IllegalStateException("Validator#allowNullable must be called before adding rules") 20 | } 21 | 22 | fun check(check: Check, error: String) = addRule(params.fieldName, { check(it!!) }, error) as Validator 23 | fun check(check: Check, error: ValidationError) = addRule(params.fieldName, { check(it!!) }, error) as Validator 24 | 25 | fun hasValue() = !params.stringValue.isNullOrEmpty() || typedValue != null 26 | 27 | @NotNull // there is a null-check in BaseValidator 28 | override fun get(): T = super.get()!! 29 | 30 | fun getOrDefault(default: T) = if (hasValue()) super.get()!! else default 31 | 32 | @NotNull 33 | override fun getOrThrow(exceptionFunction: (Map>>) -> Exception) = super.getOrThrow(exceptionFunction)!! 34 | 35 | } 36 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/http/util/ETagGenerator.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http.util 2 | 3 | import io.javalin.http.Context 4 | import io.javalin.http.HandlerType.GET 5 | import io.javalin.http.Header 6 | import io.javalin.http.HttpStatus.NOT_MODIFIED 7 | import io.javalin.util.Util 8 | import java.io.ByteArrayInputStream 9 | import java.io.InputStream 10 | 11 | object ETagGenerator { 12 | 13 | /** Generates & writes etag if possible, returns true if [resultStream] has been processed, otherwise false */ 14 | fun tryWriteEtagAndClose(generatorEnabled: Boolean, ctx: Context, resultStream: InputStream): Boolean { 15 | val serverEtag = ctx.res().getHeader(Header.ETAG) 16 | val clientEtag = ctx.req().getHeader(Header.IF_NONE_MATCH) 17 | 18 | if (serverEtag != null && serverEtag == clientEtag) { 19 | ctx.closeWith304(resultStream) // client etag matches, nothing to write) 20 | return true 21 | } 22 | 23 | if (serverEtag == null && generatorEnabled && ctx.method() == GET && resultStream is ByteArrayInputStream) { 24 | val generatedEtag = Util.getChecksumAndReset(resultStream) 25 | ctx.header(Header.ETAG, generatedEtag) 26 | 27 | if (generatedEtag == clientEtag) { 28 | ctx.closeWith304(resultStream) 29 | return true 30 | } 31 | } 32 | 33 | return false 34 | } 35 | 36 | private fun Context.closeWith304(inputStream: InputStream) { 37 | inputStream.use { status(NOT_MODIFIED) } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/vue/VueFileInliner.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.vue 2 | 3 | import io.javalin.util.javalinLazy 4 | import java.nio.file.Path 5 | import java.util.regex.Matcher 6 | import kotlin.LazyThreadSafetyMode.NONE 7 | 8 | internal object VueFileInliner { 9 | private val newlineRegex = Regex("\\r?\\n") 10 | private val unconditionalRegex = Regex("""@inlineFile\(".*"\)""") 11 | private val devRegex = Regex("""@inlineFileDev\(".*"\)""") 12 | private val notDevRegex = Regex("""@inlineFileNotDev\(".*"\)""") 13 | 14 | fun String.inlineFiles(isDev: Boolean, nonVueFiles: List): String { 15 | val pathMap = nonVueFiles.associateBy { """"/vue/${it.toString().replace("\\", "/").substringAfter("/vue/")}"""" } // normalize keys 16 | return this.split(newlineRegex).joinToString("\n") { line -> 17 | if (!line.contains("@inlineFile")) return@joinToString line // nothing to inline 18 | val matchingKey = pathMap.keys.find { line.contains(it) } ?: throw IllegalStateException("Invalid path found: $line") 19 | val matchingFileContent by javalinLazy { Matcher.quoteReplacement(pathMap[matchingKey]!!.readText()) } 20 | when { 21 | devRegex.containsMatchIn(line) -> if (isDev) line.replace(devRegex, matchingFileContent) else "" 22 | notDevRegex.containsMatchIn(line) -> if (!isDev) line.replace(notDevRegex, matchingFileContent) else "" 23 | else -> line.replace(unconditionalRegex, matchingFileContent) 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinVelocity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import io.javalin.http.Context 10 | import io.javalin.rendering.FileRenderer 11 | import io.javalin.rendering.util.RenderingDependency.VELOCITY 12 | import io.javalin.rendering.util.Util 13 | import org.apache.velocity.VelocityContext 14 | import org.apache.velocity.app.VelocityEngine 15 | import java.io.StringWriter 16 | import java.nio.charset.StandardCharsets 17 | 18 | class JavalinVelocity @JvmOverloads constructor( 19 | private var velocityEngine: VelocityEngine = defaultVelocityEngine() 20 | ) : FileRenderer { 21 | 22 | init { 23 | Util.throwIfNotAvailable(VELOCITY) 24 | } 25 | 26 | override fun render(filePath: String, model: Map, context: Context): String { 27 | val stringWriter = StringWriter() 28 | velocityEngine.getTemplate(filePath, StandardCharsets.UTF_8.name()).merge( 29 | VelocityContext(model.toMutableMap()), stringWriter 30 | ) 31 | return stringWriter.toString() 32 | } 33 | 34 | companion object { 35 | fun defaultVelocityEngine() = VelocityEngine().apply { 36 | setProperty("resource.loaders", "class") 37 | setProperty("resource.loader.class.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader") 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldImages.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.ContentType 11 | import io.javalin.util.JavalinLogger 12 | import java.awt.image.BufferedImage 13 | import java.io.ByteArrayOutputStream 14 | import javax.imageio.ImageIO 15 | 16 | fun main() { 17 | val app = Javalin.create().start(7070) 18 | 19 | app.get("/image/:color/width/:width/height/:height") { ctx -> 20 | // rrggbb, in hex, then setting the alpha channel to 0xff 21 | val colorStr = ctx.pathParam("color") 22 | val colorBits = colorStr.toInt(16) 23 | val color = (colorBits and 0xffffff) or 0xff000000.toInt() 24 | val width = ctx.pathParam("width").toInt() 25 | val height = ctx.pathParam("height").toInt() 26 | 27 | val image = java.awt.image.BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB) 28 | for (y in 0 until height) 29 | for (x in 0 until width) 30 | image.setRGB(x, y, color) 31 | 32 | val os = ByteArrayOutputStream() 33 | val success = ImageIO.write(image, "png", os) 34 | val bytes = os.toByteArray() 35 | 36 | ctx.contentType(ContentType.IMAGE_PNG).result(bytes) 37 | } 38 | 39 | JavalinLogger.info("Red square: http://localhost:7070/image/ff0000/width/400/height/400") 40 | JavalinLogger.info("Brown rectangle: http://localhost:7070/image/804020/width/400/height/100") 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldCustomJsonMapper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | import io.javalin.json.JsonMapper; 11 | import java.lang.reflect.Type; 12 | import java.util.Arrays; 13 | import org.jetbrains.annotations.NotNull; 14 | 15 | public class HelloWorldCustomJsonMapper { 16 | 17 | public static void main(String[] args) { 18 | JsonMapper rawJsonMapper = new JsonMapper() { 19 | @NotNull 20 | @Override 21 | public String toJsonString(@NotNull Object obj, @NotNull Type type) { 22 | // serialize obj your favourite api 23 | return "{ \"" + type.getTypeName() + "\": \"" + obj + "\" }"; 24 | } 25 | 26 | @NotNull 27 | @Override 28 | @SuppressWarnings("unchecked") 29 | public T fromJsonString(@NotNull String json, @NotNull Type targetType) { 30 | // deserialize json your favourite api 31 | if (targetType.equals(String.class)) { 32 | return (T) json; 33 | } else { 34 | throw new UnsupportedOperationException("RawJsonMapper can deserialize only strings"); 35 | } 36 | } 37 | }; 38 | 39 | Javalin.create(config -> config.jsonMapper(rawJsonMapper)) 40 | .get("/", ctx -> ctx.json(Arrays.asList("a", "b", "c"))) 41 | .start(7070); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/malformed.jks: -------------------------------------------------------------------------------- 1 | sfdsf�o5b�0��0 2 | +*���=���H8��(�m�ϴ��2�� 3 | y� ��&] 4 | ު��fϰ��˥�� K���ˠ�(�Mg��� 5 | &�^��{��ބOm�Y$*q��l7����v���� Co���/��r��جk�H?7��fᾉ����!"�� )�J���?=Z�h��y��Z�b���T��.m�y)1A4�n�/O6��"<͜��ǚĸM�ϼdb�y���>1�v��<�)�)�e�G�_�ײ��R �K��g�;-�m�1E�m�JrJ�|Ƽ}2f`2-��R���lYG�I�����:Q}�����e���PFTc 8 | �N� =��C��� <��9�:�� _7�c��y���*ce����6���`M��5���7R�~4`�m�ȋ�g�����wv�33�1 9 | .Q�2f����ayؾsS����w�s �r��l��"L|�4SÖ͝c��>"�VK�>�/����[��s�n���u�WU6aa��Bl�m����=��#E�s��hӚ�d4�h=}*l�����J��hn�j�{�B�k��|-!��@�r������f�2n ��4 10 | ��baw� 11 | = 12 |  *�H�� 13 | �0� 14 | ��e%����B= ��B���I�&�"_�oA������'�����Xwi�� �`4�N�o���� �p�c#�3�]���qo 56l�ei���d$�;_c��Κo 15 | "��F P�l���L�v����g��!:�i��@�P�Yt�|n5q=�hYV'��<(� �|�(4N��%Q��L�,���ډ���S�LO�y����� �@,��oF=�Vp ��;�l��&;M���h4�k`�2����;�]����ىs��0 16 |  *�H�� 17 |  �#b�*� 18 | ZP�Ճ�?[��e�#m��O�A��u+�b�F30���������N�^9���Xӄ~�7�#�†u����s\Yu89�',/鑜s�̭z��c&=�9����JxY�>�7L=�D�ANy�-���w�g,�۾�G�@���$�m<�A[cWX(�]����Jlփ�l$�/�v�������eU�lsכBp � 19 | �I��N�֛|]8�?Kfv� 20 | ~�gt��a� t|�2�t�ڎ-V�뻥t0�(�M�g���2�ݾ�>���*����j� 21 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldWebSockets.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.plugin.bundled.DevLoggingPlugin 11 | 12 | // WebSockets also work with ssl, 13 | // see HelloWorldSecure for how to set that up 14 | fun main() { 15 | Javalin.create { it.registerPlugin(DevLoggingPlugin()) }.apply { 16 | ws("/websocket") { ws -> 17 | ws.onConnect { ctx -> 18 | println("Connection established") 19 | ctx.send("[MESSAGE FROM SERVER] Connection established") 20 | } 21 | ws.onMessage { ctx -> 22 | val message = ctx.message() 23 | println("Received: " + message) 24 | ctx.send("[MESSAGE FROM SERVER] Echo: " + message) 25 | } 26 | ws.onClose { ctx -> println("Closed") } 27 | ws.onError { ctx -> println("Errored") } 28 | } 29 | get("/") { ctx -> 30 | ctx.html( 31 | """

WebSocket example

32 | """ 38 | ) 39 | } 40 | }.start(7070) 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestMethodNotAllowed.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.http.HttpStatus.METHOD_NOT_ALLOWED 4 | import io.javalin.testing.TestUtil 5 | import io.javalin.testing.httpCode 6 | import org.assertj.core.api.Assertions.assertThat 7 | import org.junit.jupiter.api.Test 8 | 9 | class TestMethodNotAllowed { 10 | 11 | private val preferring405Javalin = Javalin.create { it.http.prefer405over404 = true }.apply { 12 | post("/test") { it.result("Hello world") } 13 | put("/test") { it.result("Hello world") } 14 | delete("/test") { it.result("Hello world") } 15 | } 16 | 17 | @Test 18 | fun `405 response for HTML works`() = TestUtil.test(preferring405Javalin) { _, http -> 19 | val expectedHtml = """ 20 | |Method Not Allowed 21 | | 22 | |Available methods: 23 | |POST, PUT, DELETE 24 | |""".trimMargin() 25 | assertThat(http.htmlGet("/test").httpCode()).isEqualTo(METHOD_NOT_ALLOWED) 26 | assertThat(http.htmlGet("/test").body).isEqualTo(expectedHtml) 27 | } 28 | 29 | @Test 30 | fun `405 response for JSON works`() = TestUtil.test(preferring405Javalin) { _, http -> 31 | val expectedJson = """{ 32 | | "title": "Method Not Allowed", 33 | | "status": 405, 34 | | "type": "https://javalin.io/documentation#methodnotallowedresponse", 35 | | "details": {"availableMethods":"POST, PUT, DELETE"} 36 | |}""".trimMargin() 37 | assertThat(http.jsonGet("/test").httpCode()).isEqualTo(METHOD_NOT_ALLOWED) 38 | assertThat(http.jsonGet("/test").body).isEqualTo(expectedJson) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/plugin/PluginManager.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.plugin 2 | 3 | import io.javalin.config.JavalinConfig 4 | 5 | class PluginManager internal constructor(private val cfg: JavalinConfig) { 6 | 7 | private val plugins = mutableListOf>() 8 | private val initializedPlugins = mutableListOf>() 9 | private val enabledPlugins = mutableListOf>() 10 | 11 | fun register(plugin: Plugin<*>) { 12 | if (!plugin.repeatable() && plugins.any { it.javaClass == plugin.javaClass }) { 13 | throw PluginAlreadyRegisteredException(plugin) 14 | } 15 | plugins.add(plugin) 16 | initializePlugins() 17 | } 18 | 19 | private fun initializePlugins() { 20 | while (plugins.size != initializedPlugins.size) { 21 | val pluginsToInitialize = plugins 22 | .asSequence() 23 | .filter { it !in initializedPlugins } 24 | .sortedBy { it.priority() } 25 | 26 | for (plugin in pluginsToInitialize) { 27 | initializedPlugins.add(plugin) 28 | plugin.onInitialize(cfg) 29 | } 30 | } 31 | } 32 | 33 | fun startPlugins() { 34 | initializedPlugins 35 | .asSequence() 36 | .filter { it !in enabledPlugins } 37 | .sortedBy { it.priority() } 38 | .forEach { 39 | it.onStart(cfg) 40 | enabledPlugins.add(it) 41 | } 42 | } 43 | 44 | fun getContextPlugin(clazz: Class>): ContextPlugin<*, T> { 45 | return (plugins.find { it.javaClass == clazz } ?: throw PluginNotRegisteredException(clazz)) as ContextPlugin<*, T> 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /javalin/module-info.java: -------------------------------------------------------------------------------- 1 | module io.javalin { 2 | 3 | exports io.javalin; 4 | exports io.javalin.apibuilder; 5 | exports io.javalin.compression; 6 | exports io.javalin.config; 7 | exports io.javalin.event; 8 | exports io.javalin.http; 9 | exports io.javalin.http.servlet; 10 | exports io.javalin.http.sse; 11 | exports io.javalin.http.staticfiles; 12 | exports io.javalin.http.util; 13 | exports io.javalin.jetty; 14 | exports io.javalin.json; 15 | exports io.javalin.plugin; 16 | exports io.javalin.plugin.bundled; 17 | exports io.javalin.rendering; 18 | exports io.javalin.router; 19 | exports io.javalin.security; 20 | exports io.javalin.util; 21 | exports io.javalin.util.function; 22 | exports io.javalin.validation; 23 | exports io.javalin.vue; 24 | exports io.javalin.websocket; 25 | 26 | requires transitive kotlin.stdlib; 27 | requires transitive org.slf4j; 28 | requires transitive org.eclipse.jetty.server; 29 | requires transitive org.eclipse.jetty.servlet; 30 | requires transitive org.eclipse.jetty.util; 31 | requires transitive org.eclipse.jetty.websocket.core.common; 32 | requires transitive org.eclipse.jetty.websocket.jetty.api; 33 | requires transitive org.eclipse.jetty.websocket.jetty.server; 34 | 35 | //optional dependencies 36 | requires static org.jetbrains.annotations; 37 | requires static com.aayushatharva.brotli4j; 38 | requires static com.aayushatharva.brotli4j.service; 39 | requires static com.fasterxml.jackson.databind; 40 | requires static com.fasterxml.jackson.kotlin; 41 | requires static com.google.gson; 42 | 43 | //Required to use the Service Loader on this type 44 | uses org.slf4j.spi.SLF4JServiceProvider; 45 | } 46 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/routeoverview/TestRouteOverviewInJava.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.routeoverview; 8 | 9 | import io.javalin.http.Context; 10 | import io.javalin.http.Handler; 11 | import io.javalin.plugin.bundled.RouteOverviewUtil; 12 | import io.javalin.routeoverview.VisualTest.HandlerImplementation; 13 | import org.jetbrains.annotations.NotNull; 14 | import org.junit.jupiter.api.Test; 15 | import static org.assertj.core.api.AssertionsForClassTypes.assertThat; 16 | 17 | public class TestRouteOverviewInJava { 18 | 19 | private static final Handler lambdaField = ctx -> {}; 20 | 21 | @Test 22 | public void field_works() { 23 | assertThat(RouteOverviewUtil.getMetaInfo(lambdaField)).isEqualTo("io.javalin.routeoverview.TestRouteOverviewInJava.lambdaField"); 24 | } 25 | 26 | @Test 27 | public void class_works() { 28 | assertThat(RouteOverviewUtil.getMetaInfo(new InnerHandlerImplementation())).isEqualTo("io.javalin.routeoverview.TestRouteOverviewInJava$InnerHandlerImplementation.class"); 29 | assertThat(RouteOverviewUtil.getMetaInfo(new HandlerImplementation())).isEqualTo("io.javalin.routeoverview.VisualTest$HandlerImplementation.class"); 30 | } 31 | 32 | @Test 33 | public void lambda_works() { 34 | assertThat(RouteOverviewUtil.getMetaInfo((Handler) (ctx -> ctx.result("")))).isEqualTo("io.javalin.routeoverview.TestRouteOverviewInJava::??? (anonymous lambda)"); 35 | } 36 | 37 | private static class InnerHandlerImplementation implements Handler { 38 | @Override 39 | public void handle(@NotNull Context context) {} 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestCustomRequestLifecycle.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.http.servlet.DefaultTasks.HTTP 4 | import io.javalin.http.servlet.SubmitOrder.LAST 5 | import io.javalin.http.servlet.Task 6 | import io.javalin.http.servlet.TaskInitializer 7 | import io.javalin.testing.TestUtil 8 | import org.assertj.core.api.Assertions.assertThat 9 | import org.junit.jupiter.api.Test 10 | 11 | class TestCustomRequestLifecycle { 12 | 13 | @Test 14 | fun `can remove lifecycle stage`() = TestUtil.test(Javalin.create { 15 | it.pvt.servletRequestLifecycle = mutableListOf(HTTP) 16 | }) { app, http -> 17 | app.before { it.result("Overridden") } 18 | app.get("/") { it.result("Hello!") } 19 | app.after { it.result("Overridden") } 20 | assertThat(http.getBody("/")).isEqualTo("Hello!") 21 | } 22 | 23 | @Test 24 | fun `can add custom lifecycle stage`() = TestUtil.test(Javalin.create { 25 | it.pvt.servletRequestLifecycle = mutableListOf( 26 | HTTP, 27 | TaskInitializer { submitTask, _, ctx, _ -> 28 | submitTask(LAST, Task { 29 | ctx.result("Static after!") 30 | }) 31 | } 32 | ) 33 | }) { app, http -> 34 | app.get("/") { it.result("Hello!") } 35 | assertThat(http.getBody("/")).isEqualTo("Static after!") 36 | } 37 | 38 | @Test 39 | fun `can terminate request handling`() = TestUtil.test { app, http -> 40 | app.before { 41 | it.result("Before") 42 | it.skipRemainingHandlers() 43 | } 44 | app.get("/") { it.result("Http") } 45 | app.after { it.result("After") } 46 | assertThat(http.getBody("/")).isEqualTo("Before") 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/matcher/RoutingRegexes.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.router.matcher 2 | 3 | import io.javalin.config.RouterConfig 4 | import io.javalin.router.matcher.PathSegment.Wildcard 5 | 6 | 7 | internal fun constructRegexList( 8 | options: RouterConfig, 9 | matchEverySubPath: Boolean, 10 | segments: List, 11 | regexSuffix: String, 12 | regexOptions: Set = emptySet(), 13 | mapper: (PathSegment) -> String 14 | ): List { 15 | fun addRegexForExtraWildcard(): List { 16 | return if (matchEverySubPath) { 17 | listOf(constructRegex(options, segments + Wildcard, regexSuffix, regexOptions, mapper)) 18 | } else { 19 | emptyList() 20 | } 21 | } 22 | 23 | return listOf(constructRegex(options, segments, regexSuffix, regexOptions, mapper)) + addRegexForExtraWildcard() 24 | } 25 | 26 | internal fun constructRegex( 27 | options: RouterConfig, 28 | segments: List, 29 | regexSuffix: String, 30 | regexOptions: Set = emptySet(), 31 | mapper: (PathSegment) -> String 32 | ): Regex { 33 | val slashRegex = if (options.treatMultipleSlashesAsSingleSlash) { 34 | "/+" 35 | } else { 36 | "/" 37 | } 38 | return buildString { 39 | append("^/") 40 | if (options.treatMultipleSlashesAsSingleSlash) { 41 | append("+") 42 | } 43 | append(segments.joinToString(separator = slashRegex, transform = mapper)) 44 | append(regexSuffix) 45 | append("$") 46 | }.toRegex(regexOptions) 47 | } 48 | 49 | // Match and group values, then drop first element (the input string) 50 | internal fun values(regex: Regex, url: String) = regex.matchEntire(url)?.groupValues?.drop(1) ?: emptyList() 51 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/HelloWorldAuth.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.apibuilder.ApiBuilder.get 11 | import io.javalin.apibuilder.ApiBuilder.path 12 | import io.javalin.examples.KRole.ROLE_ONE 13 | import io.javalin.examples.KRole.ROLE_THREE 14 | import io.javalin.examples.KRole.ROLE_TWO 15 | import io.javalin.http.HttpStatus 16 | import io.javalin.http.UnauthorizedResponse 17 | import io.javalin.security.RouteRole 18 | 19 | enum class KRole : RouteRole { 20 | ROLE_ONE, ROLE_TWO, ROLE_THREE 21 | } 22 | 23 | fun main() { 24 | Javalin.create { cfg -> 25 | cfg.router.mount { 26 | it.beforeMatched { ctx -> 27 | val userRole = ctx.queryParam("role")?.let { KRole.valueOf(it) } ?: throw UnauthorizedResponse() 28 | val routeRoles = ctx.routeRoles() 29 | if (userRole !in routeRoles) { 30 | throw UnauthorizedResponse() 31 | } 32 | } 33 | } 34 | cfg.router.apiBuilder { 35 | get("/hello", { it.result("Hello World 1") }, ROLE_ONE) 36 | path("/api") { 37 | get("/test", { it.result("Hello World 2") }, ROLE_TWO) 38 | get("/tast", { it.status(HttpStatus.OK).result("Hello world 3") }, ROLE_THREE) 39 | get("/hest", { it.status(HttpStatus.OK).result("Hello World 4") }, ROLE_ONE, ROLE_TWO) 40 | get("/hast", { it.status(HttpStatus.OK).result("Hello World 5").header("test", "tast") }, ROLE_ONE, ROLE_THREE) 41 | } 42 | } 43 | }.start(7070) 44 | } 45 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/plugin/bundled/RouteOverviewPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.plugin.bundled 2 | 3 | import io.javalin.config.JavalinConfig 4 | import io.javalin.http.ContentType 5 | import io.javalin.http.Context 6 | import io.javalin.http.Header 7 | import io.javalin.plugin.Plugin 8 | import io.javalin.plugin.PluginPriority 9 | import io.javalin.router.InternalRouter 10 | import io.javalin.security.RouteRole 11 | import java.util.* 12 | import java.util.function.Consumer 13 | 14 | /** The route overview plugin provides you with a HTML and/or JSON overview of all the routes registered on your Javalin application. */ 15 | class RouteOverviewPlugin(userConfig: Consumer? = null) : Plugin(userConfig, Config()) { 16 | 17 | class Config { 18 | @JvmField var path: String = "/routes" 19 | @JvmField var roles: Array = emptyArray() 20 | } 21 | 22 | override fun onStart(config: JavalinConfig) { 23 | config.router.mount { 24 | it.get(pluginConfig.path, { ctx -> handle(ctx, config.pvt.internalRouter) }, *pluginConfig.roles) 25 | } 26 | } 27 | 28 | private fun handle(ctx: Context, internalRouter: InternalRouter) { 29 | when(ctx.header(Header.ACCEPT)?.lowercase(Locale.ROOT)?.contains(ContentType.JSON)) { 30 | true -> { 31 | ctx.header(Header.CONTENT_TYPE, ContentType.JSON) 32 | ctx.result(RouteOverviewUtil.createJsonOverview(internalRouter.allHttpHandlers(), internalRouter.allWsHandlers())) 33 | } 34 | else -> ctx.html(RouteOverviewUtil.createHtmlOverview(internalRouter.allHttpHandlers(), internalRouter.allWsHandlers())) 35 | } 36 | } 37 | 38 | override fun priority(): PluginPriority = PluginPriority.LATE 39 | 40 | } 41 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestClose.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | package io.javalin 8 | 9 | import io.javalin.testing.TestUtil 10 | import org.assertj.core.api.Assertions.assertThat 11 | import org.junit.jupiter.api.Test 12 | 13 | class AutoClosableJavalin(val javalin: Javalin) : AutoCloseable { 14 | override fun close() { 15 | javalin.stop() 16 | } 17 | 18 | fun start(port: Int): AutoClosableJavalin = also { javalin.start(port) } 19 | fun jettyServer() = javalin.jettyServer() 20 | } 21 | 22 | class TestClose { 23 | 24 | @Test 25 | fun useStopsServer() = TestUtil.runLogLess { 26 | val app = AutoClosableJavalin(Javalin.create()) 27 | app.start(0).use { } 28 | assertThat(app.jettyServer().server().isStopped).isTrue 29 | } 30 | 31 | @Test 32 | fun useCallsLifecycleEvents() = TestUtil.runLogLess { 33 | var log = "" 34 | val app = AutoClosableJavalin(Javalin.create().events { 35 | it.serverStopping { log += "Stopping" } 36 | it.serverStopped { log += "Stopped" } 37 | }) 38 | app.start(0).use { } 39 | assertThat(log).isEqualTo("StoppingStopped") 40 | } 41 | 42 | @Test 43 | fun closingInsideUseIsIdempotent() = TestUtil.runLogLess { 44 | var log = "" 45 | val app = AutoClosableJavalin(Javalin.create().events { 46 | it.serverStopping { log += "Stopping" } 47 | it.serverStopped { log += "Stopped" } 48 | }) 49 | app.start(0).use { it.close() } 50 | assertThat(app.jettyServer().server().isStopped).isTrue 51 | assertThat(log).isEqualTo("StoppingStopped") 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestSslRedirectPlugin.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.http.Header 4 | import io.javalin.plugin.bundled.SslRedirectPlugin 5 | import io.javalin.testing.TestUtil 6 | import kong.unirest.Unirest 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | 10 | class TestSslRedirectPlugin { 11 | 12 | private val httpWithoutAutoRedirects = Unirest.spawnInstance() 13 | 14 | init { 15 | httpWithoutAutoRedirects.config().followRedirects(false) 16 | } 17 | 18 | @Test 19 | fun `ssl redirect plugin should redirect all http requests`() = TestUtil.test( 20 | Javalin.create { cfg -> 21 | cfg.registerPlugin(SslRedirectPlugin { 22 | it.redirectOnLocalhost = true 23 | }) 24 | } 25 | ) { app, http -> 26 | app.get("/") { ctx -> ctx.result("Hello") } 27 | 28 | val response = httpWithoutAutoRedirects.get(http.origin).asEmpty() 29 | assertThat(response.status).isEqualTo(301) 30 | assertThat(response.headers.getFirst(Header.LOCATION)).isEqualTo("https://localhost:${app.port()}/") 31 | } 32 | 33 | @Test 34 | fun `ssl redirect plugin should support custom ssl port`() = TestUtil.test( 35 | Javalin.create { cfg -> 36 | cfg.registerPlugin(SslRedirectPlugin { 37 | it.redirectOnLocalhost = true 38 | it.sslPort = 8443 39 | }) 40 | } 41 | ) { app, http -> 42 | app.get("/") { ctx -> ctx.result("Hello") } 43 | 44 | val response = httpWithoutAutoRedirects.get(http.origin).asEmpty() 45 | assertThat(response.status).isEqualTo(301) 46 | assertThat(response.headers.getFirst(Header.LOCATION)).isEqualTo("https://localhost:8443/") 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/client.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDYMknUfY1Ukypm 3 | oR7HTXcQwqK7KA3EqhQ5hwq59bRBDrxhDtZ8Sb07pmB7UvqwY690rA03pKls7qaR 4 | Ud8M/n5dk6KQfb37IlU/RidU/gRN6R34biLoE/H2vQJW7ae0XocVkbgPJNWz1+1B 5 | VFBmFbVqkIElnkurlJ5PmC+E3oKCZMl3RHBFOLg25ro7/RhyTh9uJjE41weU7fI0 6 | fAiCuxJd1CABteSbI0ZKX660VNshmbLRlgvziqlAgNmric7/KoJmcTFA9f9+q/9y 7 | GptafPwlchqk6LaiLgxK+/5PoaKve0aqwDSBvD0lMQRpr1wE8AymKI5OQHURlCIh 8 | oGCtA2FVAgMBAAECggEAMMS7Sc8ek+70mB0NQ9kSHDpDmiNRY8UX9dgkD8nApRJN 9 | ZTmDIZtCsBN6XSddPG/oMtHi02P2Aggdk9dbkONRnwtAFwb+VYmroC/TjIUWdHq7 10 | E4DIqABjywxc3D4U4AeYwkFSOzxMZpq1PtDlp/mKqGS7dUBtgmFuirbiNT86Qm5O 11 | rSSyKX8lRTojZvAZWzjJbMZBXuLnQEbhbX+Kl+9zydzpHfsglIrTmKApz3vf6gZ4 12 | lLZEnrdVz4NCgJm6uak7eduKTqv4aKaFxTRmM9G+a+Dpuc8goAfZENzwPMW99XYt 13 | enUrvvZ5K3LPfrpeid4wSNwCMpS1K/kTQDAhiDorGwKBgQDtsVJhgL4mE8QLZXZv 14 | ziin9iGd9CvRwq8ecIESr3Mse3rwHtU3nlUB3q5ygrZ3kN17BfaN0OBKKE5l9yQv 15 | AKl8zrjlokzIEmY4MM8DkxVgG2mhv0wi9lBWgQ7DViKDqAu43C/HLUM24tjNRkcq 16 | OvU9s3DlENu5zlydZJ8eEaFIcwKBgQDo2R4CtjjOccw4C3Z9FZotk+ujCFU/jNIl 17 | CNKwXDPS/H411hxvLEfjVgHn15PsPQdci/YNe4bEn9hwpF3PPpFHnorYWR8SaBeA 18 | R25bihH7wS0looVfrO52VkuEfRO+4AVThoh+FxKsIKoFHl7HPpX2ZzW1KSbqXoeP 19 | haDjtK3lFwKBgF4bFW2rBh2XN8f3TNPpQGl3ZkneMTckKCcaDdAzDJtw6/SDkJEr 20 | q6ZI3rJRlCJ0hWu+qaXuK87QdHGkTnwZfAEA621nO5T+y9+1W1Cj/BOH5uN2FJPH 21 | B3moMQsfYBzV/IYdDwGBpxnk6EclaumKXCwojiry6LuAu2RJQUIrYrqHAoGBAMJa 22 | JzCARJ4cYicNelMhqtUMOIyWMgGfWvBKkwFdlGwxEG+DTayFu2NHsMOBLxjqP7DB 23 | qhjK2ExaV2B98UdApCaxcJbwnHzSoXsTJEwHzMbkEPju1IBtaYUgtlvZwpd5iTiU 24 | fG0yeQVvJjqrhNStqqJGt4+Ez9SkHmZvVUF62AL1AoGANAnLQpUjyaedFDxMTHyG 25 | k8/UAdUMMEo/Bm3oe7N64yu0x92nBwozYCPB9Mon19O5tzE6oA/GnbExzPyKdNjk 26 | ndfcLNB85cOcQ41Xl+zjm/6DdZ4bVia/0LB/iIUbmqKdDQ9fV/w1LpAo/+H6mF/o 27 | W5aSOgMTY6Cq2tnTeVor1W4= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/ca/server.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCIZ/c31LyjHtdO 3 | NKWHVH6Qg+Ivnq695GoBnPFUZ4k+vmTrMqZknZ7gNQ2uowVw0O9KryYHcTAA+Ah0 4 | 4fg0OALg0NgRgoc823FRMjfUTS6xS0nAX53xwscYF3nw/wAja4lx0jXsqlYLvWjB 5 | fFHtPOo0Lus0Rj9D+/wmwBiriPAvFzdbmTf39UsByXmTem3DR3JXxEmhliy/JQAl 6 | bz5599Zfw9QbFC5E4wZTq5gy1x55wPbizXPeQlkee3tptqn6N/mfvsPFeNnak3pa 7 | +5xC4iOMvy4Q3wM86MM+LZTLd5LQNHpobm28jJ/cJda0uOJAvjilUtO1i54XYSCk 8 | 8q2GhS6fAgMBAAECggEAPYVWdLHYGmHwjW8lMPIk2x0ASKXy/eYlAXE9Tuobdlb8 9 | wS9o6HxGiayj7R3NeGJjanWwWy7rjaRQfBbmkPaqJadLrCoIwKPgadSSZYgiBQiF 10 | zOPTIpTMossDznPUJiV8Kf+7HnB8unLE6m8drDgLEVoP7tW39vuRcBC3j7YFTdcs 11 | KUm1GoJTfcUbv6JP2DkixbLc7PJXvHWuhV/pVO9BK/MRbdy329x+ZsQ0KqlZtzWs 12 | wirsmLXe1vesi+sQZCnsYG0XDrc8pNWJZBlAuiKBOE0c99L+taMiicWs7/eLPdrL 13 | sPJ8u8VmYc7yTiFfZ7O2r9PTwujaaAhgGG8xWi+yUQKBgQC8NSQI6Kb0DBzqt4L1 14 | F2vmn2X4+XzxbSJz0otwursmVKaxcE0npdQvRnqpZdSNCemd2ozCPgKcFLqfMWGz 15 | AYp7AR5XDpek8awcJLXC8q1rBV0jZnmtZAL+AjOEsSWbXaYJP2jk8noKqQoXGabi 16 | NjDhijPFL3xLu8PPS30JLjT+LQKBgQC5iiVypdr52o9kZZsUvJ3fDGrVWAafQrhD 17 | cQazfz62ofNcRYehVdXZSt59a/FCooNR0+Q0q0vZrmSx2rF1+2luqcTtI/P4yPF/ 18 | 1kCKjlieCWXpKDAIA7xjEDhLVFO0pGiQIkyXaCIwQS34V8cR2yrGgcQsI0AkA6FE 19 | G8hSNcOrewKBgGtE+oHdYHLOiJi6+XgJ3mT2yt8KC81qv202PmWE+HATZWLSuBb1 20 | 7Cb5y6XLNUchtDZVP07imwgsxGVBdwbYxP3wmENUPNg1KjxsK/Ct8muCBiDMImPS 21 | EeaGiKiDcwTHbn/s1TNQhICY8fhSlYxvCJMz+ufTdcuQfD0lMGHQJUINAoGBAKNi 22 | Om3GDZl91E2Vh8Xv/pl/BsjOFi7Cgx6odj00bRZIcNQtSi1RN9p8KoaJuk9yam9x 23 | RwD2YvnSr0tLCICbnxFbIFWvFlVbJgbrtbQNe43XOs3hnMJB/+/ARcJYBMoLCST7 24 | fzTI92pEy2Vobp2TwQxDE2vGsuHD7IvcZz+1ppPnAoGAQdJeZuZwE7lv9j5Ts+bn 25 | bziTivb3I+GPtJ7GEo9RvAlHZ8Y19mMZ+B5Y6vDEB26JnOAANfBKvsGrH3wslmA8 26 | 7PNxNS6uZ3RVq4H657S/x3/NMQUgrFUmtaRzWdqN1jqXJHlqa1fTY8DhRzQUFRxZ 27 | W/7SxZrSboB4GB/B4Fk41NI= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/client/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwJS1Q5NbFDfNt 3 | VcrwU8WHgOwMKmGhS3+Tq/yfiaJlbF/DjlWqxKTkmS2YWGpKyZ6uHQ1xBnuuOLr0 4 | sr92vjB9kmNYsaXI0MmdPVb88/cdoYSsnEOX82ouOX7FQteynv79I/snPpBwQqYN 5 | g7JA0Pv0xpheWGQIu239GyV8g/VhDgAUIZzDjP9wYRG4y1wtjwUVP41Tg4PboBlf 6 | h8BD59BEB0AvOnrfAKdaJjJD7Q2p6rjAMz4diUCxRvBLCIlKx4hWfa6efWRCrjlN 7 | RwvwdS5Uav0IXw/K+Q4vFt3MLSrjcx88NyTsKCpHZgb1cetxr1xPpz7MT4EaGPM6 8 | OUDFQJirAgMBAAECggEADdQNV7Fvbu7mcmnu0akx865aWaYmHfyIWnaBEaFDf4Tf 9 | i8Gr1gk0DMI9wx0F0zM64t5jBMGGiinn+3fg8hiCRAlvBTKFGlvRyCddoeQhPVFF 10 | 0is+Xzp71n8rBZ92wY4b5JGjkPQncLi6worZPp9peFDy+00jJVBZlSpBaiIN7H2E 11 | iZQYUMI07u6xJW/EUE6X9g3AhgV9QMxfJawn8AWHXR8+9iNsOb9hlVUWBPwR7xb5 12 | 4KqB/89UFp/40tEDeKz9/MMsH5FjNCNPCaLADJS2Xy1Q1icV6V42HsaZm9vZUL+J 13 | dru6OwEo6iJhWKjkBaWvVl4HuOPrrUP9sLSN6g6PCQKBgQDXih7xgHF35yDPvnNx 14 | fqqxfRO+PMHq1se2tOhAdeDmdStUyl/u1NwJ9BE9Fb/lbdulYFfZJtef2TmeX31x 15 | DaQWXrg4Pai2pnCcSfItogWJSFrg6dphbABwVslTvWw2ikB6hN2jmUaReM0atW2S 16 | YUVWD0JFMsf4IimgAcGPgebprQKBgQDRNfB2k6NhqgKBXKrshT/3No/kMnhkNl9H 17 | i/UmiCUYvw5E/L4q2wrsehnERAPpod3EoHjkYCmY/BK4oRCtkz6t1nnWX1zGabY3 18 | Nn4Ie+BMCYp2NLa7yGZ0sk1rrtlgZBoaR1ZF0+HADPpffELPD6HzvUQuPyN+wlA0 19 | SWwq8DuGtwKBgBxyxIbHlzJmNTR2RLJ0L39hrNttFYMzegSpeAYaCOciC+gTFfpl 20 | 6ez+Y9AWMM/NYjI/txiYQdl9SFeY7uufC0tQkSwLJ1uEOFTIhch0HBr0i9onw4Uc 21 | RiqNqeD9nWzNbpk9NCvFrUTCFwAxdhbd89LaDLspaq9bgvb1hGC2mo25AoGBAKVP 22 | ks+Pf3Unik0/tQupis7DvVVakAjXcdgt/itRPsbcCOF4OKfSZ0JOhNexysmsjnjV 23 | OFF0rsnkvMJI+s284LUqGSHMPpnFZCciltoLUEOk8lTO+GlPQ64ISebBxaBF2N5U 24 | 6hXJA8PmPVx/6qaEurrHHf3RBDIgRpHaRm9zXgXnAoGASlEFHkUKwf8G3AePstzk 25 | sHoxJiKMTq2qFb/NTVE4z4+pc03uhxno79+R4aV4JD0dK1gyRaX5/TCwdvI5smS3 26 | Vfl5JN+HiO0zClecR8N83arOLka2prJ3ZjjCy2JgZKRXZQ/vcsTKnvh3DIFyR/NZ 27 | OKM5x3IGChzxEZLumfedQX4= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/passwordless.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7VtuO4j1QLgnE 3 | AcrqoI/LUsyXMFagpoXk+pXdF3uw2KCzixLI4pQf44BvWgRAX2Cdoe+Cu0iHaokg 4 | 8+K4FLiVM4WZyaOXd598E/Vm9gGXLKhXHiStFDQSntv9W9NXRVrUBVuMxwvyTPxq 5 | dHTE8tVbdWX/90pkI0g/ys4CX+Jtjvxjj94kRbAkjwuAlN02Qg5iOf6ka+InCQmO 6 | bN4CfkU/nnX7L7XZHwDuISv6ft+0a8B2sZ8hS6AM2HSTHJw1//lexM9su45E5HFJ 7 | NoChvt3aYqNl6gBPaV3F2uJGJNDS/VcOiwtkDHnxShyw6i1sf230F6W1CxWsJXRE 8 | w27i1Xu1AgMBAAECggEAfPI7UZr3BckO3lnLup0ICrXYmmW1AUTPPJ8c4O7Oom55 9 | EAaLqsvjuzkC6kGBYGW8jKX6lpjOkPKvLvk6l0fKrEhGrQFdSKKSDjFJlTgya19v 10 | j1sdXwqAiILHer2JwUUShSJlowkGoL5UA7RURR8oye0M8KFATnVxtIpQyCinXiW/ 11 | LkDuqUr8MIbu6V/KcoSOLfJyTWyuwSRPHuFKhv154UAqaTkSPbf2mCTa9hH5Tb4f 12 | Lfzy9o3Ux4ieZceG28De+SmC7uMzbBs1stowOuDmFg3znI/1Br/sQEAXPFngDe3s 13 | soDD2PbLo7/4SPBNgl5vygf7jhvxHPY3DTUXOxLSgQKBgQD4EzKVTx/GpF7Yswma 14 | oixidzSi/KnHJiMjIERF4QPVfDNnggRORNMbPnRhNWSRhS7r+INYbN4yB/vBZO5I 15 | IIqowdJbLjGbmq91equP0zzrP2wCjqtFK6gRElX7acAWY5xTesIT5Fa1Ug++dFLS 16 | MxCZKL6JMZaHJzZVzXugaltMsQKBgQDBUvPSaDnIBrZGdNtAyNMxZyVbp/ObIKW1 17 | TvCDX2hqf+yiTVclbZr5QkwCE3MHErfsKlWU01K9CtzsQh4u9L5tPaeFlvm6iZq6 18 | ktbflNvI+z+qEW3JbROR4WwwbmWFvKRLBA0OQom7tGuNnNyRtkDFxlkFJPcD6Eff 19 | ZEq+ewrQRQKBgQCV7URM6J0TuJN58/qB8jFQ8Spmtr0FFw91UzLv6KYgiAepLvLb 20 | Os07UeuUNGiragqJoo//CQzgv+JvZ0h7Xu9uPnWblbd1i28vWQwGyGuw4Yutn/vy 21 | ugfBCYvdfnQRE/KOoUpaK04cF5RcToEfeK03Y2CEGewXkqNMB/wHXz/+gQKBgE8Y 22 | 34WQ+0Mp69375dEl2bL23sQXfYZU3zfFaoZ1vMUGPg1R03wO0j91rp+S0ZdtQy8v 23 | SwCvTcTm8uj/TFYt8NPFTAtOcDKwJkx708p6n0ol8jBlHSQyqrUfJCLUqFkFi7rd 24 | l3HkK3JPKUoxidVcWjgRJU8DhsVkfjOaVzKEKTJ5AoGARBwn7gt2H35urQ6/U3nJ 25 | hFjOVn01F5uV0NvRtRDCsAIUMeA2T4pwALUUIqlA9HmpwYgLeG4bZ+SkhNpy70N/ 26 | qcufT1DeM+q3H5zFPANyjcqVaqa6KUnttvi/lhxMdRb6GsA9TzzHzY1P9ovpIOCK 27 | IS639NPzxpI0Ka+v6t+nFEM= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinJte.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import gg.jte.ContentType 10 | import gg.jte.TemplateEngine 11 | import gg.jte.output.StringOutput 12 | import gg.jte.resolve.DirectoryCodeResolver 13 | import io.javalin.http.Context 14 | import io.javalin.http.servlet.isLocalhost 15 | import io.javalin.rendering.FileRenderer 16 | import io.javalin.rendering.util.RenderingDependency.JTE 17 | import io.javalin.rendering.util.RenderingDependency.JTE_KOTLIN 18 | import io.javalin.rendering.util.Util 19 | import java.io.File 20 | 21 | class JavalinJte @JvmOverloads constructor( 22 | private val templateEngine: TemplateEngine = defaultJteEngine(), 23 | private val isDevFunction: (Context) -> Boolean = { ctx -> ctx.isLocalhost() } 24 | ) : FileRenderer { 25 | 26 | init { 27 | Util.throwIfNotAvailable(JTE) 28 | } 29 | 30 | private var isDev: Boolean? = null // cached and easily accessible, is set on first request (can't be configured directly by end user) 31 | 32 | override fun render(filePath: String, model: Map, context: Context): String { 33 | if (isDev == true && filePath.endsWith(".kte")) { 34 | Util.throwIfNotAvailable(JTE_KOTLIN) 35 | } 36 | isDev = isDev ?: isDevFunction(context) 37 | val stringOutput = StringOutput() 38 | templateEngine.render(filePath, model, stringOutput) 39 | return stringOutput.toString() 40 | } 41 | 42 | companion object { 43 | private fun defaultJteEngine() = TemplateEngine.create(DirectoryCodeResolver(File("src/main/jte").toPath()), ContentType.Html) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldSecure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | * 6 | */ 7 | 8 | package io.javalin.examples; 9 | 10 | import io.javalin.Javalin; 11 | import org.eclipse.jetty.server.ServerConnector; 12 | import org.eclipse.jetty.util.ssl.SslContextFactory; 13 | 14 | public class HelloWorldSecure { 15 | 16 | // This is a very basic example, a better one can be found at: 17 | // https://github.com/eclipse/jetty.project/blob/jetty-9.4.x/examples/embedded/src/main/java/org/eclipse/jetty/embedded/LikeJettyXml.java#L139-L163 18 | public static void main(String[] args) { 19 | Javalin.create(config -> { 20 | config.jetty.addConnector((server, httpConfiguration) -> { 21 | ServerConnector sslConnector = new ServerConnector(server, getSslContextFactory()); 22 | sslConnector.setPort(443); 23 | return sslConnector; 24 | }); 25 | config.jetty.addConnector((server, httpConfiguration) -> { 26 | ServerConnector connector = new ServerConnector(server); 27 | connector.setPort(80); 28 | return connector; 29 | }); 30 | 31 | }).start().get("/", ctx -> ctx.result("Hello World")); // valid endpoint for both connectors 32 | } 33 | 34 | private static SslContextFactory.Server getSslContextFactory() { 35 | SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); 36 | sslContextFactory.setKeyStorePath(HelloWorldSecure.class.getResource("/keystore.jks").toExternalForm()); 37 | sslContextFactory.setKeyStorePassword("password"); 38 | return sslContextFactory; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldWebSockets.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples; 8 | 9 | import io.javalin.Javalin; 10 | 11 | // WebSockets also work with ssl, 12 | // see HelloWorldSecure for how to set that up 13 | public class HelloWorldWebSockets { 14 | public static void main(String[] args) { 15 | Javalin app = Javalin.create(cfg -> cfg.bundledPlugins.enableDevLogging()); 16 | app.ws("/websocket", ws -> { 17 | ws.onConnect(ctx -> { 18 | System.out.println("Connected"); 19 | ctx.send("[MESSAGE FROM SERVER] Connection established"); 20 | }); 21 | ws.onMessage(ctx -> { 22 | System.out.println("Received: " + ctx.message()); 23 | ctx.send("[MESSAGE FROM SERVER] Echo: " + ctx.message()); 24 | }); 25 | ws.onClose(ctx -> { 26 | System.out.println("Closed"); 27 | }); 28 | ws.onError(ctx -> { 29 | System.out.println("Errored"); 30 | }); 31 | }); 32 | app.get("/", ctx -> { 33 | ctx.html("

WebSocket example

\n" + 34 | ""); 40 | }); 41 | app.start(7070); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /javalin/src/test/kotlin/io/javalin/examples/AdvancedServerSentEvent.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.examples 8 | 9 | import io.javalin.Javalin 10 | import io.javalin.http.sse.SseClient 11 | import io.javalin.http.staticfiles.Location 12 | import org.eclipse.jetty.server.Server 13 | import org.eclipse.jetty.util.thread.QueuedThreadPool 14 | import java.util.concurrent.ConcurrentLinkedQueue 15 | import java.util.concurrent.TimeUnit 16 | 17 | fun main() { 18 | 19 | val tp = QueuedThreadPool(8, 2, 60_000) 20 | val counterClients = ConcurrentLinkedQueue() 21 | val statsClients = ConcurrentLinkedQueue() 22 | 23 | Javalin.create { 24 | it.staticFiles.add("/public", Location.CLASSPATH) 25 | it.pvt.jetty.server = Server(tp) 26 | }.apply { 27 | get("/") { it.redirect("/sse/sse-example.html") } 28 | sse("/sse-counter") { client -> 29 | counterClients.add(client) 30 | client.onClose { counterClients.remove(client) } 31 | } 32 | sse("/sse-stats") { eventSource -> 33 | statsClients.add(eventSource) 34 | eventSource.onClose { statsClients.remove(eventSource) } 35 | } 36 | }.start(7000) 37 | 38 | for (counter in 1..999) { 39 | counterClients.forEach { 40 | it.sendEvent("Counter: $counter") // send as "message" 41 | it.sendEvent("counter", "Counter: $counter", 1.toString()) 42 | } 43 | statsClients.forEach { 44 | it.sendEvent("stats", "Clients: ${counterClients.size + statsClients.size}, Threads: ${tp.busyThreads}/${tp.threads}", 999.toString()) 45 | } 46 | TimeUnit.SECONDS.sleep(1) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/router/matcher/PathMatcher.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.router.matcher 8 | 9 | import io.javalin.http.HandlerType 10 | import io.javalin.router.ParsedEndpoint 11 | import java.util.* 12 | import java.util.stream.Stream 13 | 14 | class PathMatcher { 15 | 16 | private val handlerEntries: Map> = 17 | HandlerType.entries.associateWithTo(EnumMap(HandlerType::class.java)) { arrayListOf() } 18 | 19 | fun add(entry: ParsedEndpoint) { 20 | val type = entry.endpoint.method 21 | val path = entry.endpoint.path 22 | 23 | if (type.isHttpMethod && handlerEntries[type]!!.find { it.endpoint.method == type && it.endpoint.path == path } != null) { 24 | throw IllegalArgumentException("Handler with type='${type}' and path='${path}' already exists.") 25 | } 26 | 27 | handlerEntries[type]!!.add(entry) 28 | } 29 | 30 | fun findEntries(handlerType: HandlerType, requestUri: String?): Stream = 31 | when (requestUri) { 32 | null -> handlerEntries[handlerType]!!.stream() 33 | else -> handlerEntries[handlerType]!!.stream().filter { he -> match(he, requestUri) } 34 | } 35 | 36 | fun allEntries() = handlerEntries.values.flatten() 37 | 38 | internal fun hasEntries(handlerType: HandlerType, requestUri: String): Boolean = 39 | handlerEntries[handlerType]!!.any { entry -> match(entry, requestUri) } 40 | 41 | private fun match(entry: ParsedEndpoint, requestPath: String): Boolean = when (entry.endpoint.path) { 42 | "*" -> true 43 | requestPath -> true 44 | else -> entry.matches(requestPath) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /javalin/src/main/java/io/javalin/plugin/bundled/HttpAllowedMethodsPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2021 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.plugin.bundled 8 | 9 | import io.javalin.config.JavalinConfig 10 | import io.javalin.http.HandlerType.OPTIONS 11 | import io.javalin.http.Header.ACCESS_CONTROL_ALLOW_METHODS 12 | import io.javalin.plugin.Plugin 13 | import io.javalin.router.Endpoint 14 | 15 | /** 16 | * Plugin adding automatically the Access-Control-Allow-Methods when sending an OPTIONS request to a valid path. 17 | * 18 | * The Access-Control-Allow-Methods response header specifies one or more methods allowed when accessing 19 | * a resource in response to a preflight request. 20 | * 21 | * e.g.: `Access-Control-Allow-Methods: POST, DELETE` 22 | */ 23 | open class HttpAllowedMethodsPlugin : Plugin() { 24 | 25 | override fun onStart(config: JavalinConfig) { 26 | config.events.serverStarted { 27 | config.pvt.internalRouter.allHttpHandlers() 28 | .asSequence() 29 | .map { it.endpoint } 30 | .filter { it.method.isHttpMethod } 31 | .groupBy({ it.path }, { it.method }) 32 | .mapValues { (_, handlers) -> (handlers + OPTIONS).toSet() } 33 | .forEach { (path, handlers) -> 34 | val allowedMethods = handlers.joinToString(",") 35 | 36 | config.pvt.internalRouter.addHttpEndpoint( 37 | Endpoint( 38 | method = OPTIONS, 39 | path = path, 40 | handler = { it.header(ACCESS_CONTROL_ALLOW_METHODS, allowedMethods) } 41 | ) 42 | ) 43 | } 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/util/RenderingDependency.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.util 8 | 9 | import io.javalin.util.DependencyUtil 10 | import io.javalin.util.OptionalDependency; 11 | import io.javalin.util.Util 12 | 13 | enum class RenderingDependency( 14 | override val displayName: String, 15 | override val testClass: String, 16 | override val groupId: String, 17 | override val artifactId: String, 18 | override val version: String 19 | ) : OptionalDependency { 20 | JTE("jte", "gg.jte.TemplateEngine", "gg.jte", "jte", "2.2.1"), 21 | JTE_KOTLIN("jte-kotlin", "gg.jte.compiler.kotlin.KotlinClassCompiler", "gg.jte", "jte-kotlin", "2.2.1"), 22 | VELOCITY("Velocity", "org.apache.velocity.app.VelocityEngine", "org.apache.velocity", "velocity-engine-core", "2.3"), 23 | FREEMARKER("Freemarker", "freemarker.template.Configuration", "org.freemarker", "freemarker", "2.3.30"), 24 | THYMELEAF("Thymeleaf", "org.thymeleaf.TemplateEngine", "org.thymeleaf", "thymeleaf", "3.0.12.RELEASE"), 25 | MUSTACHE("Mustache", "com.github.mustachejava.MustacheFactory", "com.github.spullara.mustache.java", "compiler", "0.9.7"), 26 | PEBBLE("Pebble", "com.mitchellbosecke.pebble.PebbleEngine", "io.pebbletemplates", "pebble", "3.1.5"), 27 | COMMONMARK("Commonmark", "org.commonmark.renderer.html.HtmlRenderer", "org.commonmark", "commonmark", "0.17.1"), 28 | ; 29 | 30 | internal fun exists() = Util.classExists(this.testClass) 31 | } 32 | 33 | object Util { 34 | fun throwIfNotAvailable(dependency: RenderingDependency) { 35 | if (!Util.classExists(dependency.testClass)) { 36 | throw IllegalStateException(DependencyUtil.missingDependencyMessage(dependency)) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/http/ContentTypeTest.kt: -------------------------------------------------------------------------------- 1 | package io.javalin.http 2 | 3 | import io.javalin.Javalin 4 | import io.javalin.http.HttpStatus.OK 5 | import io.javalin.testing.TestUtil 6 | import io.javalin.testing.httpCode 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | 10 | class ContentTypeTest { 11 | 12 | @Test 13 | fun `fetching content type by mime type should get the right type`() { 14 | ContentType.entries.forEach { 15 | assertThat(ContentType.getContentType(it.mimeType)).isEqualTo(it) 16 | } 17 | } 18 | 19 | @Test 20 | fun `fetching content type by its extension should get the right type`() { 21 | ContentType.entries.forEach { type -> 22 | type.extensions.forEach { extension -> 23 | assertThat(ContentType.getContentTypeByExtension(extension)).isEqualTo(type) 24 | assertThat(ContentType.getMimeTypeByExtension(extension)).isEqualTo(type.mimeType) 25 | } 26 | } 27 | } 28 | 29 | @Test 30 | fun `string representation should return mime type`() { 31 | assertThat(ContentType.TEXT_PLAIN.toString()).isEqualTo(ContentType.TEXT_PLAIN.mimeType) 32 | } 33 | 34 | @Test 35 | fun `file should be served even if content type isn't set`() { 36 | val precompressingApp = Javalin.create { config -> 37 | config.staticFiles.add { 38 | it.precompress = true 39 | it.directory = "/markdown" 40 | } 41 | } 42 | TestUtil.test(precompressingApp) { _, http -> 43 | val response = http.get("/test.md") 44 | assertThat(response.httpCode()).isEqualTo(OK) 45 | assertThat(response.body).contains("# Hello Markdown!") 46 | assertThat(response.headers.getFirst(Header.CONTENT_TYPE)).isEqualTo("") 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /javalin-rendering/src/main/java/io/javalin/rendering/template/JavalinThymeleaf.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.rendering.template 8 | 9 | import io.javalin.http.Context 10 | import io.javalin.rendering.FileRenderer 11 | import io.javalin.rendering.util.RenderingDependency.THYMELEAF 12 | import io.javalin.rendering.util.Util 13 | import org.thymeleaf.TemplateEngine 14 | import org.thymeleaf.context.WebContext 15 | import org.thymeleaf.templatemode.TemplateMode 16 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver 17 | import org.thymeleaf.web.servlet.JakartaServletWebApplication 18 | 19 | class JavalinThymeleaf @JvmOverloads constructor( 20 | private var templateEngine: TemplateEngine = defaultThymeLeafEngine() 21 | ) : FileRenderer { 22 | 23 | init { 24 | Util.throwIfNotAvailable(THYMELEAF) 25 | } 26 | 27 | override fun render(filePath: String, model: Map, context: Context): String { 28 | // ctx.req.servletContext that is passed to buildApplication has to match ctx.req.servletContext passed into 29 | // buildExchange. (application.servletContext === ctx.req.servletContext) 30 | val application = JakartaServletWebApplication.buildApplication(context.req().servletContext) 31 | val webExchange = application.buildExchange(context.req(), context.res()) 32 | val webContext = WebContext(webExchange, webExchange.locale, model) 33 | return templateEngine.process(filePath, webContext) 34 | } 35 | 36 | companion object { 37 | private fun defaultThymeLeafEngine() = TemplateEngine().apply { 38 | setTemplateResolver(ClassLoaderTemplateResolver().apply { 39 | templateMode = TemplateMode.HTML 40 | }) 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestMaxRequestSize.kt: -------------------------------------------------------------------------------- 1 | package io.javalin 2 | 3 | import io.javalin.http.HttpStatus.CONTENT_TOO_LARGE 4 | import io.javalin.http.HttpStatus.OK 5 | import io.javalin.testing.TestUtil 6 | import io.javalin.testing.httpCode 7 | import org.assertj.core.api.Assertions.assertThat 8 | import org.junit.jupiter.api.Test 9 | 10 | class TestMaxRequestSize { 11 | 12 | @Test 13 | fun `max request size is set by default`() = TestUtil.test { app, http -> 14 | app.post("/") { it.result(it.body()) } 15 | assertThat(http.post("/").body(ByteArray(1_000_000)).asString().httpCode()).isEqualTo(OK) 16 | assertThat(http.post("/").body(ByteArray(1_000_001)).asString().httpCode()).isEqualTo(CONTENT_TOO_LARGE) 17 | } 18 | 19 | @Test 20 | fun `user can configure max request size`() = TestUtil.test(Javalin.create { it.http.maxRequestSize = 4L }) { app, http -> 21 | app.post("/") { it.result(it.body()) } 22 | assertThat(http.post("/").body(ByteArray(4)).asString().httpCode()).isEqualTo(OK) 23 | assertThat(http.post("").body(ByteArray(5)).asString().httpCode()).isEqualTo(CONTENT_TOO_LARGE) 24 | } 25 | 26 | @Test 27 | fun `body can be read multiple times`() = TestUtil.test(Javalin.create()) { app, http -> 28 | app.post("/") { it.result(it.body() + it.body() + it.body()) } 29 | assertThat(http.post("/").body("Hi").asString().body).isEqualTo("HiHiHi") 30 | } 31 | 32 | @Test 33 | fun `can read payloads larger than max size by using inputstream`() = TestUtil.test(Javalin.create { it.http.maxRequestSize = 4L }) { app, http -> 34 | app.post("/body") { it.result(it.body()) } 35 | assertThat(http.post("/body").body("123456").asString().body).isEqualTo("Content Too Large") 36 | app.post("/stream") { it.result(it.req().inputStream.readBytes()) } 37 | assertThat(http.post("/stream").body("123456").asString().body).isEqualTo("123456") 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestJsonMapper.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin 8 | 9 | import io.javalin.json.JsonMapper 10 | import io.javalin.json.toJsonString 11 | import org.assertj.core.api.Assertions.assertThat 12 | import java.io.ByteArrayOutputStream 13 | import java.io.OutputStream 14 | import kotlin.streams.asStream 15 | 16 | class TestJsonMapper { 17 | 18 | companion object { 19 | 20 | data class Foo(val value: Long) 21 | 22 | fun convertSmallStreamToJson(jsonMapper: JsonMapper) { 23 | val source = listOf(Foo(1_000_000), Foo(1_000_001)) 24 | val baos = ByteArrayOutputStream() 25 | jsonMapper.writeToOutputStream(source.stream(), baos) 26 | assertThat("""[{"value":1000000},{"value":1000001}]""").isEqualTo(baos.toString()) 27 | } 28 | 29 | fun convertLargeStreamToJson(jsonMapper: JsonMapper) { 30 | val countingOutputStream = CountingOutputStream() 31 | var valueLength = 1_000_000L // we will increment this up 1_050_000L 32 | val oneElementLength = jsonMapper.toJsonString(Foo(valueLength)).length 33 | val numElements = 50_000 34 | val seq = generateSequence { Foo(valueLength++) } 35 | jsonMapper.writeToOutputStream(seq.take(numElements).asStream(), countingOutputStream) 36 | val expectedCharacterCount = 2 + // bookend brackets 37 | (numElements - 1) + // commas 38 | oneElementLength * numElements // elements 39 | assertThat(expectedCharacterCount).isEqualTo(countingOutputStream.count) 40 | } 41 | 42 | } 43 | 44 | private class CountingOutputStream : OutputStream() { 45 | var count: Long = 0 46 | override fun write(b: Int) { 47 | count++ 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /javalin-ssl/src/test/resources/server/encrypted.key: -------------------------------------------------------------------------------- 1 | -----BEGIN ENCRYPTED PRIVATE KEY----- 2 | MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIMDP+/JKdUc4CAggA 3 | MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBRfpz0ZTscSvALfUISuNYIBIIE 4 | 0I8rVF2h/qQANJ3WvXFcmm7dFqEiQwUm8cDxaDpPd8RRweclTesEj70yg+3xcGLh 5 | rhSFrNSB2wmy/jB6lFcN02KcSU3p6H7aSuLRffbYYAQ3LGU6Ie79NW68x189zB/b 6 | sDi6gWkxHCrGzBydKR4a6ZvF9TMnP743hCw3t3NrO/4xdoZ9+YaxmBaBjt4E1Bns 7 | J2yCHHV5kXXsWOZJvTTWxf+fIEQNe1cjidBxcpvQxreZpOsday2KM8tctom+p9lw 8 | DEF0mhUi/FHZZnmfgr1Cz4+PmspjOTykX+0RWD1wi0kMJwqo6aRHwlEbEE+f83Df 9 | kazqIXOfD0VrzSXTwr1kIzjQI+DK8sKyfg5lfTby1AFy5cvtJxL7cK6As9Cq05XI 10 | i2fX5PWUj1sHplMOI2+qh31R7w6qb0DygXC22ZFNLlFYwP0QKPp9XzZZLIvPI662 11 | 9xlF4VgtcS9JV7hztrg6Bbc23l1cSsBXPqreWd39NM/Kggf6J3GV/P0AacYYp0OY 12 | A3Pt9i+RV+HHv8OwfZ+v4RH8hVhtDkPWyBJX581zwF5OQLqjksKa1FNC8qB/VlE4 13 | Ponm33vn1gWtKY962sYoJxDVHbgWwpWP7bSqtO66jiwlTEyQwltd3LbZGcJ4yJNd 14 | eEJUE8vFHyXmEx1KoDHUh2/v9l6pdo59PgBlY8mxl95AuTNds+dtuqZQE6ZNZvDZ 15 | lw7dOp+BATb/3YWCF5O6jQjm11ji9kZxgnPBTSiaegFFIa4OxdQwP2pxbyVk4rGH 16 | /gs6olWtc3hqqQspMJDqT2cJeaE61us7hUya1w1LjivOvofR3Zt2v2Wtxmm+ey3e 17 | /mPBZH1LIRdP9vEuHxKkjjppXVRWL5TdHeN9Ai6jG3/WCp/NgBnjOi9/5GVX7j+T 18 | dUzGBaxwUGt10QZ8Yvo9qT8Cg2tiUD770EzaD09aiRfoAs7YwLsVi35gul+JyNrD 19 | CzqZ8I2NZ6Uo/r9I9Xda9qkoxbS2hNg+53whm5L2fT4SrJ69MOY/tM3mR8q1Ta18 20 | W+dXuFSD+3nAU7Aqug4LlKcOS9/RW18kRtHRVatXZscxITO5dlgmw7zEVzrkwa+q 21 | r1y4YG488XZZ3KCXPJthnmP4nIYERW6hn62P8EKzM8wfxxT39O96QNzMgszor/WA 22 | TG8o0JDRvG5WW/OfVA4Ls8QK6lx3E2cPhyqnvM+HirtP2xL4Gd0SibGIK7QvewSf 23 | 9a5TnbQsuoWvTqtzX7PEb9snLxQjaxTLZEbTyimwEyaaZ1Ev72did2EdmA3UEtzq 24 | e+X86mvYOZrAJIWNGIfoMI3QPtxlC2MbDjUcLB5crk90T2dCcIdwpr6cKGdnEqP+ 25 | DmkkwTl84MSV2tVQ2qCJPtiwsR8V2xkwqesD1p0G5whR2SxsUQDoG48l1zRkLrA5 26 | PbwUii9Xapp5+R08t+dIt19cRJyewjAKxpWkKHNjtXmBMJpvJ65A4wAAV5vqcTdY 27 | FIrJEMySqFDrodCwkAs9s8FKIWvEnWKkaX2NvjoTWdQEGmKpiEazUsknd4wNX8js 28 | MjjY/VHqWNYR6cF84H+WuFS86S37Vt3nBEpos0vp9n8epNNC+ETcewKMgovLJMnt 29 | na5mQXa7ctzrJ+bqW9B+QLBX6KZk3tRnigYO6Fum0t7I 30 | -----END ENCRYPTED PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/TestHttpAllowedMethodsPlugin.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin 8 | 9 | import io.javalin.http.Header.ACCESS_CONTROL_ALLOW_METHODS 10 | import io.javalin.http.Header.ACCESS_CONTROL_REQUEST_HEADERS 11 | import io.javalin.http.Header.ACCESS_CONTROL_REQUEST_METHOD 12 | import io.javalin.http.HttpStatus.CREATED 13 | import io.javalin.http.HttpStatus.NOT_MODIFIED 14 | import io.javalin.http.HttpStatus.OK 15 | import io.javalin.plugin.bundled.HttpAllowedMethodsPlugin 16 | import io.javalin.testing.TestUtil 17 | import kong.unirest.Unirest 18 | import org.assertj.core.api.Assertions.assertThat 19 | import org.junit.jupiter.api.Test 20 | 21 | class TestHttpAllowedMethodsPlugin { 22 | 23 | @Test 24 | fun `enableHttpOptionsForRoutes allows possible methods on routes`() { 25 | val javalin = Javalin.create { it.registerPlugin(HttpAllowedMethodsPlugin()) } 26 | javalin.get("/") { it.result("Hello") } 27 | javalin.delete("/") { it.status(OK) } 28 | javalin.get("/users") { it.result("Users") } 29 | javalin.post("/users") { it.status(CREATED) } 30 | javalin.patch("/users") { it.status(NOT_MODIFIED) } 31 | 32 | TestUtil.test(javalin) { app, http -> 33 | val response = Unirest.options(http.origin) 34 | .header(ACCESS_CONTROL_REQUEST_HEADERS, "123") 35 | .header(ACCESS_CONTROL_REQUEST_METHOD, "TEST") 36 | .asString() 37 | 38 | assertThat(response.headers[ACCESS_CONTROL_ALLOW_METHODS].first()) 39 | .isEqualTo("GET,DELETE,OPTIONS") 40 | 41 | val usersResponse = Unirest.options(http.origin + "/users") 42 | .asString() 43 | 44 | assertThat(usersResponse.headers[ACCESS_CONTROL_ALLOW_METHODS].first()) 45 | .isEqualTo("GET,POST,PATCH,OPTIONS") 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/examples/HelloWorldPlugin.java: -------------------------------------------------------------------------------- 1 | package io.javalin.examples; 2 | 3 | import io.javalin.Javalin; 4 | import io.javalin.http.Context; 5 | import io.javalin.http.TooManyRequestsResponse; 6 | import io.javalin.plugin.ContextPlugin; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.function.Consumer; 12 | 13 | public class HelloWorldPlugin { 14 | public static void main(String[] args) { 15 | var app = Javalin.create(config -> { 16 | config.registerPlugin(new JRate(it -> it.limit = 1)); // register plugin with config 17 | }); 18 | app.get("/", ctx -> { 19 | ctx.with(JRate.class).tryConsume(2); 20 | ctx.result("Hello World"); 21 | }); 22 | app.start(7070); 23 | } 24 | } 25 | 26 | // this class demonstrates the most advanced use case of a plugin, 27 | // where the plugin has a config and a plugin extension 28 | // we recommend using inner classes for plugins, as it keeps the whole plugin in one place 29 | class JRate extends ContextPlugin { 30 | public JRate(Consumer userConfig) { 31 | super(userConfig, new Config()); 32 | } 33 | Map ipToCounter = new HashMap<>(); 34 | @Override 35 | public Extension createExtension(@NotNull Context context) { 36 | return new Extension(context); 37 | } 38 | public static class Config { 39 | public int limit = 1; 40 | } 41 | public class Extension { 42 | private final Context context; 43 | public Extension(Context context) { 44 | this.context = context; 45 | } 46 | public void tryConsume(int cost) { 47 | String ip = context.ip(); 48 | int counter = ipToCounter.compute(ip, (k, v) -> v == null ? cost : v + cost); 49 | if (counter > pluginConfig.limit) { 50 | throw new TooManyRequestsResponse(); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /javalin/src/test/java/io/javalin/testing/HttpUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Javalin - https://javalin.io 3 | * Copyright 2017 David Åse 4 | * Licensed under Apache 2.0: https://github.com/tipsy/javalin/blob/master/LICENSE 5 | */ 6 | 7 | package io.javalin.testing 8 | 9 | import io.javalin.http.ContentType 10 | import io.javalin.http.Header 11 | import io.javalin.http.HttpStatus 12 | import kong.unirest.HttpMethod 13 | import kong.unirest.HttpResponse 14 | import kong.unirest.Unirest 15 | 16 | class HttpUtil(port: Int) { 17 | 18 | @JvmField 19 | val origin: String = "http://localhost:$port" 20 | 21 | fun enableUnirestRedirects() = Unirest.config().reset().followRedirects(true) 22 | fun disableUnirestRedirects() = Unirest.config().reset().followRedirects(false) 23 | 24 | // Unirest 25 | fun get(path: String) = Unirest.get(origin + path).asString() 26 | fun get(path: String, headers: Map) = Unirest.get(origin + path).headers(headers).asString() 27 | 28 | fun getStatus(path: String) = HttpStatus.forStatus(get(path).status) 29 | fun getBody(path: String) = Unirest.get(origin + path).asString().body 30 | fun getBody(path: String, headers: Map) = Unirest.get(origin + path).headers(headers).asString().body 31 | fun post(path: String) = Unirest.post(origin + path) 32 | fun call(method: HttpMethod, pathname: String) = Unirest.request(method.name(), origin + pathname).asString() 33 | fun htmlGet(path: String) = Unirest.get(origin + path).header("Accept", ContentType.HTML).asString() 34 | fun jsonGet(path: String) = Unirest.get(origin + path).header("Accept", ContentType.JSON).asString() 35 | fun sse(path: String) = Unirest.get(origin + path).header("Accept", "text/event-stream").header("Connection", "keep-alive").header("Cache-Control", "no-cache").asStringAsync() 36 | fun wsUpgradeRequest(path: String) =Unirest.get(origin + path).header(Header.SEC_WEBSOCKET_KEY, "not-null").asString() 37 | } 38 | 39 | fun HttpResponse<*>.httpCode(): HttpStatus = 40 | HttpStatus.forStatus(this.status) 41 | --------------------------------------------------------------------------------