http/2 is functional
This commit is contained in:
@@ -46,6 +46,21 @@
|
||||
<artifactId>jetty-jndi</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-server</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-alpn-java-server</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.http2</groupId>
|
||||
<artifactId>http2-server</artifactId>
|
||||
<version>${jetty.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
@@ -127,11 +142,17 @@
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
|
@@ -1,16 +1,37 @@
|
||||
package org.jeudego.pairgoth.application
|
||||
|
||||
import org.apache.commons.io.FileUtils
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory
|
||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory
|
||||
import org.eclipse.jetty.server.HttpConfiguration
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory
|
||||
import org.eclipse.jetty.server.SecureRequestCustomizer
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.server.ServerConnector
|
||||
import org.eclipse.jetty.server.SslConnectionFactory
|
||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection
|
||||
import org.eclipse.jetty.util.resource.Resource
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory
|
||||
import org.eclipse.jetty.webapp.WebAppContext
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.InputStreamReader
|
||||
import java.net.JarURLConnection
|
||||
import java.net.URL
|
||||
import java.net.URLDecoder
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.security.KeyFactory
|
||||
import java.security.KeyStore
|
||||
import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import java.security.spec.PKCS8EncodedKeySpec
|
||||
import java.util.*
|
||||
import java.util.jar.JarFile
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
fun main(vararg args: String) {
|
||||
try {
|
||||
@@ -32,9 +53,9 @@ private fun extractWarFiles() {
|
||||
Files.createDirectories(targetPath)
|
||||
|
||||
// extract wars
|
||||
val webappsFolderURL = object{}::class.java.enclosingClass.getResource("/META-INF/webapps") ?: throw Error("webapps not found")
|
||||
val webappsFolderURL = getResource("/META-INF/webapps") ?: throw Error("webapps not found")
|
||||
val jarConnection = webappsFolderURL.openConnection() as JarURLConnection
|
||||
val jarFile: JarFile = jarConnection.getJarFile()
|
||||
val jarFile: JarFile = jarConnection.jarFile
|
||||
jarFile.entries().toList().filter { entry ->
|
||||
entry.name.startsWith(jarConnection.entryName)
|
||||
}.forEach { entry ->
|
||||
@@ -46,36 +67,89 @@ private fun extractWarFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
private val mainClass = object{}::class.java.enclosingClass
|
||||
private val jarPath = mainClass.protectionDomain.codeSource.location.path.let { URLDecoder.decode(it, "UTF-8") }
|
||||
private val serverProps = Properties()
|
||||
private fun getResource(resource: String) = mainClass.getResource(resource)
|
||||
private fun getResourceProperty(key: String) = serverProps.getProperty(key)?.let { property ->
|
||||
val url = property.replace("\$jar", jarPath)
|
||||
if (!Resource.newResource(url).exists()) throw Error("resource not found: $url")
|
||||
URL(url)
|
||||
} ?: throw Error("missing property: $key")
|
||||
|
||||
private fun launchServer() {
|
||||
|
||||
// create server
|
||||
val server = Server(8080) // CB TODO port is to be calculated from webapp.url
|
||||
|
||||
// create webapps contexts
|
||||
val apiContext = createContext("api", "/api");
|
||||
val viewContext = createContext("view", "/");
|
||||
val apiContext = createContext("api", "/api")
|
||||
val viewContext = createContext("view", "/")
|
||||
|
||||
// handle properties
|
||||
val properties = File("./pairgoth.properties");
|
||||
val defaultProps = getResource("/server.default.properties") ?: throw Error("missing default server properties")
|
||||
defaultProps.openStream().use {
|
||||
serverProps.load(InputStreamReader(it, StandardCharsets.UTF_8))
|
||||
}
|
||||
val properties = File("./pairgoth.properties")
|
||||
if (properties.exists()) {
|
||||
val props = Properties()
|
||||
props.load(FileReader(properties));
|
||||
props.entries.forEach { entry ->
|
||||
serverProps.load(FileReader(properties))
|
||||
serverProps.entries.forEach { entry ->
|
||||
val property = entry.key as String
|
||||
val value = entry.value as String
|
||||
if (property.startsWith("logger.")) {
|
||||
// special handling for logger properties
|
||||
val webappLoggerPropKey = "webapp-slf4j-logger.${property.substring(7)}"
|
||||
apiContext.setInitParameter(webappLoggerPropKey, value);
|
||||
viewContext.setInitParameter(webappLoggerPropKey, value);
|
||||
apiContext.setInitParameter(webappLoggerPropKey, value)
|
||||
viewContext.setInitParameter(webappLoggerPropKey, value)
|
||||
} else if (property.startsWith("webapp.ssl.")) {
|
||||
// do not propagate ssl properties further
|
||||
} else {
|
||||
System.setProperty("pairgoth.$property", value);
|
||||
System.setProperty("pairgoth.$property", value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create server
|
||||
val server = Server(8080) // CB TODO port is to be calculated from webapp.url
|
||||
|
||||
// register webapps
|
||||
server.handler = ContextHandlerCollection(apiContext, viewContext);
|
||||
server.handler = ContextHandlerCollection(apiContext, viewContext)
|
||||
|
||||
// set up http/2
|
||||
val httpConfig = HttpConfiguration().apply {
|
||||
addCustomizer(SecureRequestCustomizer())
|
||||
}
|
||||
val http11 = HttpConnectionFactory(httpConfig)
|
||||
val h2 = HTTP2ServerConnectionFactory(httpConfig)
|
||||
val alpn = ALPNServerConnectionFactory().apply {
|
||||
defaultProtocol = http11.protocol
|
||||
}
|
||||
val cert = getResourceProperty("webapp.ssl.cert").readBytes()
|
||||
val key = getResourceProperty("webapp.ssl.key").readText().let {
|
||||
val encodedKey = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*").matcher(it).replaceFirst("$1")
|
||||
Base64.getDecoder().decode(encodedKey.replace("\n", ""))
|
||||
}
|
||||
val pass = serverProps.getProperty("webapp.ssl.pass") ?: "foobar"
|
||||
|
||||
val keyFactory = KeyFactory.getInstance("RSA")
|
||||
val keySpec = PKCS8EncodedKeySpec(key)
|
||||
val privKey = keyFactory.generatePrivate(keySpec)
|
||||
|
||||
val certificateFactory = CertificateFactory.getInstance("X.509")
|
||||
val store = KeyStore.getInstance("JKS").apply {
|
||||
load(null)
|
||||
setCertificateEntry("certificate", certificateFactory.generateCertificate(ByteArrayInputStream(cert)) as X509Certificate)
|
||||
setKeyEntry("key", privKey, pass.toCharArray(), arrayOf(certificateFactory.generateCertificate(ByteArrayInputStream(cert))))
|
||||
}
|
||||
val sslContextFactory = SslContextFactory.Server().apply {
|
||||
keyStoreType = "JKS"
|
||||
keyStore = store
|
||||
keyStorePassword = pass
|
||||
// if (pass.isNotEmpty()) keyManagerPassword = pass
|
||||
}
|
||||
|
||||
val tls = SslConnectionFactory(sslContextFactory, alpn.protocol)
|
||||
val connector = ServerConnector(server, tls, alpn, h2, http11)
|
||||
connector.port = 8443
|
||||
server.addConnector(connector)
|
||||
|
||||
// launch server
|
||||
server.start()
|
||||
@@ -84,5 +158,5 @@ private fun launchServer() {
|
||||
|
||||
private fun createContext(webapp: String, contextPath: String) = WebAppContext().also { context ->
|
||||
context.war = "$tmp/pairgoth/webapps/$webapp-webapp-$version.war"
|
||||
context.contextPath = contextPath;
|
||||
context.contextPath = contextPath
|
||||
}
|
||||
|
3
webserver/src/main/resources/server.default.properties
Normal file
3
webserver/src/main/resources/server.default.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
webapp.ssl.key = jar:file:$jar!/ssl/localhost.key
|
||||
# webapp.ssl.pass = foobar (not supported for now)
|
||||
webapp.ssl.cert = jar:file:$jar!/ssl/localhost.crt
|
25
webserver/src/main/resources/ssl/localhost.crt
Normal file
25
webserver/src/main/resources/ssl/localhost.crt
Normal file
@@ -0,0 +1,25 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIELTCCAxWgAwIBAgIUOOB8QYucWNOJYAg1ypawCJpdE9kwDQYJKoZIhvcNAQEL
|
||||
BQAwgaUxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDANJZEYxDjAMBgNVBAcMBVBhcmlz
|
||||
MSwwKgYDVQQKDCNGw4PCqWTDg8KpcmF0aW9uIEZyYW7Dg8KnYWlzZSBkZSBHbzER
|
||||
MA8GA1UECwwIcGFpcmdvdGgxEjAQBgNVBAMMCWxvY2FsaG9zdDEjMCEGCSqGSIb3
|
||||
DQEJARYUcGFpcmdvdGhAamV1ZGVnby5vcmcwHhcNMjMwNjA5MTMwMDMyWhcNMzMw
|
||||
NjA2MTMwMDMyWjCBpTELMAkGA1UEBhMCRlIxDDAKBgNVBAgMA0lkRjEOMAwGA1UE
|
||||
BwwFUGFyaXMxLDAqBgNVBAoMI0bDg8KpZMODwqlyYXRpb24gRnJhbsODwqdhaXNl
|
||||
IGRlIEdvMREwDwYDVQQLDAhwYWlyZ290aDESMBAGA1UEAwwJbG9jYWxob3N0MSMw
|
||||
IQYJKoZIhvcNAQkBFhRwYWlyZ290aEBqZXVkZWdvLm9yZzCCASIwDQYJKoZIhvcN
|
||||
AQEBBQADggEPADCCAQoCggEBAJXPkr0CZf4xneSXFIUUWn1HNlgEVWEbhUbnrzqA
|
||||
DqGN/MkpUI6/viXurNg1yLRPLeRvmlSUhwPc6Sq5HGZqimKFXcU/JNp+H4yiQluo
|
||||
ykOvHTGhkYUtXJK91oxrghqU2kEOfJeoOyCpcBvPzBP0a3iSDBUTgazNiZWKvZ7L
|
||||
MwYDj0sfC0d8zXlKxpsg62G9AIxOJEol5l7BrxtlUdO9xS44s6vrhlXYkdOdt5gV
|
||||
77iQOWWiFJyBuhFhKrtUXP5yVPGVVko7HC6vtaiVRbzdh4g0j2tI4sq88tcTWpXA
|
||||
iIrbmSCCDNkW0XfMap/zgL9Tkqb5kEbdPPuDitZftt9zsMsCAwEAAaNTMFEwHQYD
|
||||
VR0OBBYEFDR7lP45ae2rgeyPiaIUk9AOd6geMB8GA1UdIwQYMBaAFDR7lP45ae2r
|
||||
geyPiaIUk9AOd6geMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEB
|
||||
ACp3YHugHxuoktyEB/pSEhkXFAYhaUYty6ESb0hQzF4CK9xY0fdabC40RFjGhGlg
|
||||
STBez30thKbmcYoCUy0VG9lf8qndF0xgjihXzBHmRd1b4PO8AkAeMIbAKGhFeptf
|
||||
MKIdfBjmrYAUA0L6GMU/h5hWPOzf6KACiJ8nHNjgYFsTrHO52QGqSqgy4j1l6TtQ
|
||||
hlM3rxK0gSWzafZW/syxDXeM/Rocq0R48E90WpO9A4E9dg2PQtYT2zk7MMuMMIRg
|
||||
D317F3FSFWI6XIg6EyLVx69rfuKiMqZ4ghTyXKyKcSC+LJQJhKFtOiLAdXaJ3naF
|
||||
I7MPu1wZ/ZGRd4AvjsF/5jc=
|
||||
-----END CERTIFICATE-----
|
28
webserver/src/main/resources/ssl/localhost.key
Normal file
28
webserver/src/main/resources/ssl/localhost.key
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVz5K9AmX+MZ3k
|
||||
lxSFFFp9RzZYBFVhG4VG5686gA6hjfzJKVCOv74l7qzYNci0Ty3kb5pUlIcD3Okq
|
||||
uRxmaopihV3FPyTafh+MokJbqMpDrx0xoZGFLVySvdaMa4IalNpBDnyXqDsgqXAb
|
||||
z8wT9Gt4kgwVE4GszYmVir2eyzMGA49LHwtHfM15SsabIOthvQCMTiRKJeZewa8b
|
||||
ZVHTvcUuOLOr64ZV2JHTnbeYFe+4kDllohScgboRYSq7VFz+clTxlVZKOxwur7Wo
|
||||
lUW83YeINI9rSOLKvPLXE1qVwIiK25kgggzZFtF3zGqf84C/U5Km+ZBG3Tz7g4rW
|
||||
X7bfc7DLAgMBAAECggEAAtwIgUOdY5HJA55h1AVCXoNluq9LjrUWcAgJayS1AKdc
|
||||
XKqa0OdBGzrqPwQfQ4to9iydTT92ZEh7/QJgU925ijhpd+1NbGFulP/RvYDQ4fDJ
|
||||
NqssbcWJOCcG8zjOYRL6BWE1aKx1HQAWau03ZiKkeoKNAp9PoQHU8WaWaqaWzrwm
|
||||
XZA2K1yb8jKdEGEqvyvt8bOIwAgerdWwNQPe6oqQKeCH2471YRKtIsAdVRqQwxzB
|
||||
GmKyNcc8AaG4hJAZGu59HLqAwBdLht/v2ZU5QFBeRq4ltzxe9wPlW08dHu1D3l4M
|
||||
rdIRrWn1FFVFjs71fgb+Abs9Fpi/+F3JNEyoxnz8YQKBgQDR5C4VFR4YczCFOXaB
|
||||
JAq7aF2SCLggotRO33O/Nbdl7+qJxVjl80DcbLhlgYQIZC4osUoF7JPfXPJVrYX7
|
||||
plEy/071YHikuLpBcd0FBQpnmDi0BQWbbBMKVwQCH53Lrz4FuI+3jbEozKi7BIvN
|
||||
WMzAt6fIXyPyPMI7eGWiUcUcZQKBgQC2uJmlTGDQOhPbl2ZS0Iya3hyZ0iQXI25L
|
||||
iyrHYvKN7AgQnYYn4eYrJoRh/gh9cWDSEUSB9OLY8ekEVmzmUWQVjgycaYE4seFO
|
||||
B4NWc2L+aALszES9C3SRupMa3jVgDPBI4u/DCWJrg9ttSPMEgwYFk33mFxlAQc53
|
||||
0LBn14pNbwKBgBVYeFtKh4IDDPcvjd66VKEUjxeP7XHcPW08CmByzRD/4kFaoZzZ
|
||||
LUp9gA9KqavUzGD1DsslcTBxGnAeMpcSJgXisxv/UKWn58FKHCkrhxBcCcA9FoHk
|
||||
7tbJXK3+mySg0NTyHSOUtGSq06oZX0Jl+oTK6LRXAKfdB//WUbe9SyeFAoGAE8dL
|
||||
ql7oI+IFgEGVK+WzMphUVDow+eg16it4R/jn9IDWJqZGfU6wkX8r2UecN6fsKREB
|
||||
b2fInl8hL/0C8LNiuAqWRuAMwsxObRnXF6aJ0qwDlQpPbn8s8RFXFxNyh6Ee6WTX
|
||||
Oy9q3eR5/gxlcdmU70mV2TAq5Y+5/7IxRixIpjUCgYEAlMK/pFAnmaUqgl5UUyA5
|
||||
vDGgCoPb3ZBTF8yNndaOq07cDmGPIURwQKiI24iAm2HNyoEP5gVEvZ8jY2DAgqPt
|
||||
zsv6TaG1w112HA+l1tJ8cfQTMm7COwXFjdPJ0lOb8VaNoAzeW+/B7+IsVCa2yjP4
|
||||
LZWxz7cjuLlYfZk3KM06HmA=
|
||||
-----END PRIVATE KEY-----
|
Reference in New Issue
Block a user