forked from woggioni/rbcs
temporary commit
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
package net.woggioni.gbcs
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
import java.net.URL
|
||||
import java.net.URLStreamHandlerFactory
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.nio.file.StandardOpenOption
|
||||
import java.security.KeyStore
|
||||
import java.security.MessageDigest
|
||||
import java.security.PrivateKey
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.AbstractMap.SimpleEntry
|
||||
import java.util.Base64
|
||||
import java.util.ServiceLoader
|
||||
import io.netty.bootstrap.ServerBootstrap
|
||||
import io.netty.buffer.ByteBuf
|
||||
import io.netty.buffer.Unpooled
|
||||
@@ -30,25 +45,18 @@ import io.netty.handler.codec.http.HttpResponseStatus
|
||||
import io.netty.handler.codec.http.HttpServerCodec
|
||||
import io.netty.handler.codec.http.HttpUtil
|
||||
import io.netty.handler.codec.http.LastHttpContent
|
||||
import io.netty.handler.ssl.SslContextBuilder
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate
|
||||
import io.netty.handler.stream.ChunkedNioFile
|
||||
import io.netty.handler.stream.ChunkedWriteHandler
|
||||
import io.netty.util.concurrent.DefaultEventExecutorGroup
|
||||
import io.netty.util.concurrent.EventExecutorGroup
|
||||
import net.woggioni.jwo.Application
|
||||
import net.woggioni.jwo.JWO
|
||||
import java.nio.channels.FileChannel
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.nio.file.StandardOpenOption
|
||||
import java.security.MessageDigest
|
||||
import java.util.AbstractMap.SimpleEntry
|
||||
import java.util.Base64
|
||||
import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.SSLEngine
|
||||
import net.woggioni.jwo.Tuple2
|
||||
|
||||
|
||||
class GradleBuildCacheServer {
|
||||
class GradleBuildCacheServer(private val cfg : Configuration) {
|
||||
|
||||
internal class HttpChunkContentCompressor(threshold : Int, vararg compressionOptions: CompressionOptions = emptyArray())
|
||||
: HttpContentCompressor(threshold, *compressionOptions) {
|
||||
@@ -115,25 +123,62 @@ class GradleBuildCacheServer {
|
||||
}
|
||||
}
|
||||
|
||||
private class ServerInitializer(private val cacheDir: Path) : ChannelInitializer<Channel>() {
|
||||
private class ServerInitializer(private val cfg : Configuration) : ChannelInitializer<Channel>() {
|
||||
|
||||
companion object {
|
||||
val group: EventExecutorGroup = DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors())
|
||||
fun loadKeystore(file : Path, password : String?) : KeyStore {
|
||||
val ext = JWO.splitExtension(file)
|
||||
.map(Tuple2<String, String>::get_2)
|
||||
.orElseThrow {
|
||||
IllegalArgumentException(
|
||||
"Keystore file '${file}' must have .jks or p12 extension")
|
||||
}
|
||||
val keystore = when(ext.lowercase()) {
|
||||
"jks" -> KeyStore.getInstance("JKS")
|
||||
"p12", "pfx" -> KeyStore.getInstance("PKCS12")
|
||||
else -> throw IllegalArgumentException(
|
||||
"Keystore file '${file}' must have .jks or p12 extension")
|
||||
}
|
||||
Files.newInputStream(file).use {
|
||||
keystore.load(it, password?.let(String::toCharArray))
|
||||
}
|
||||
return keystore
|
||||
}
|
||||
}
|
||||
|
||||
override fun initChannel(ch: Channel) {
|
||||
val sslEngine: SSLEngine = SSLContext.getDefault().createSSLEngine()
|
||||
sslEngine.useClientMode = false
|
||||
val pipeline = ch.pipeline()
|
||||
// pipeline.addLast(SslHandler(sslEngine))
|
||||
val tlsConfiguration = cfg.tlsConfiguration
|
||||
if(tlsConfiguration != null) {
|
||||
val ssc = SelfSignedCertificate()
|
||||
val keyStore = tlsConfiguration.keyStore
|
||||
val sslCtx = if(keyStore == null) {
|
||||
SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build()
|
||||
} else {
|
||||
val javaKeyStore = loadKeystore(keyStore.file, keyStore.password)
|
||||
val serverKey = javaKeyStore.getKey(
|
||||
keyStore.keyAlias, keyStore.keyPassword?.let(String::toCharArray)) as PrivateKey
|
||||
val serverCert = javaKeyStore.getCertificateChain(keyStore.keyAlias) as Array<X509Certificate>
|
||||
SslContextBuilder.forServer(serverKey, *serverCert).build()
|
||||
}
|
||||
val sslHandler = sslCtx.newHandler(ch.alloc())
|
||||
pipeline.addLast(sslHandler)
|
||||
if(tlsConfiguration.verifyClients) {
|
||||
val trustStore = tlsConfiguration.trustStore?.let {
|
||||
loadKeystore(it.file, it.password)
|
||||
}
|
||||
pipeline.addLast(ClientCertificateValidator.of(sslHandler, trustStore))
|
||||
}
|
||||
}
|
||||
pipeline.addLast(HttpServerCodec())
|
||||
pipeline.addLast(HttpChunkContentCompressor(1024))
|
||||
pipeline.addLast(ChunkedWriteHandler())
|
||||
pipeline.addLast(HttpObjectAggregator(Int.MAX_VALUE))
|
||||
pipeline.addLast(NettyHttpBasicAuthenticator(mapOf("user" to "password")) { user, _ -> user == "user" })
|
||||
pipeline.addLast(group, ServerHandler(cacheDir, "/cache"))
|
||||
// pipeline.addLast(NettyHttpBasicAuthenticator(mapOf("user" to "password")) { user, _ -> user == "user" })
|
||||
pipeline.addLast(group, ServerHandler(cfg.cacheFolder, cfg.serverPath))
|
||||
pipeline.addLast(ExceptionHandler())
|
||||
Files.createDirectories(cacheDir)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val group: EventExecutorGroup = DefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors())
|
||||
Files.createDirectories(cfg.cacheFolder)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,12 +299,13 @@ class GradleBuildCacheServer {
|
||||
// Configure the server
|
||||
httpBootstrap.group(bossGroup, workerGroup)
|
||||
.channel(NioServerSocketChannel::class.java)
|
||||
.childHandler(ServerInitializer(Paths.get("/tmp/gbcs"))) // <-- Our handler created here
|
||||
.childHandler(ServerInitializer(cfg))
|
||||
.option(ChannelOption.SO_BACKLOG, 128)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
|
||||
// Bind and start to accept incoming connections.
|
||||
val httpChannel = httpBootstrap.bind(HTTP_PORT).sync()
|
||||
val bindAddress = InetSocketAddress(cfg.host, cfg.port)
|
||||
val httpChannel = httpBootstrap.bind(bindAddress).sync()
|
||||
|
||||
// Wait until server socket is closed
|
||||
httpChannel.channel().closeFuture().sync()
|
||||
@@ -270,11 +316,69 @@ class GradleBuildCacheServer {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val HTTP_PORT = 8080
|
||||
|
||||
private const val PROTOCOL_HANDLER = "java.protocol.handler.pkgs"
|
||||
private const val HANDLERS_PACKAGE = "net.woggioni.gbcs.url"
|
||||
|
||||
/**
|
||||
* Reset any cached handlers just in case a jar protocol has already been used. We
|
||||
* reset the handler by trying to set a null [URLStreamHandlerFactory] which
|
||||
* should have no effect other than clearing the handlers cache.
|
||||
*/
|
||||
private fun resetCachedUrlHandlers() {
|
||||
try {
|
||||
URL.setURLStreamHandlerFactory(null)
|
||||
} catch (ex: Error) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
fun registerUrlProtocolHandler() {
|
||||
val handlers = System.getProperty(PROTOCOL_HANDLER, "")
|
||||
System.setProperty(
|
||||
PROTOCOL_HANDLER,
|
||||
if (handlers == null || handlers.isEmpty()) HANDLERS_PACKAGE else "$handlers|$HANDLERS_PACKAGE"
|
||||
)
|
||||
resetCachedUrlHandlers()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
SelfSignedCertificate()
|
||||
ServiceLoader.load(javaClass.module.layer, URLStreamHandlerFactory::class.java).stream().forEach {
|
||||
println(it.type())
|
||||
}
|
||||
// registerUrlProtocolHandler()
|
||||
Thread.currentThread().contextClassLoader = GradleBuildCacheServer::class.java.classLoader
|
||||
GradleBuildCacheServer().run()
|
||||
val app = Application.builder("gbcs")
|
||||
.configurationDirectoryEnvVar("GBCS_CONFIGURATION_DIR")
|
||||
.configurationDirectoryPropertyKey("net.woggioni.gbcs.conf.dir")
|
||||
.build()
|
||||
val confDir = app.computeConfigurationDirectory()
|
||||
val configurationFile = confDir.resolve("gbcs.xml")
|
||||
|
||||
if(!Files.exists(configurationFile)) {
|
||||
Files.createDirectories(confDir)
|
||||
val defaultConfigurationFileResourcePath = "net/woggioni/gbcs/gbcs-default.xml"
|
||||
val defaultConfigurationFileResource = GradleBuildCacheServer.javaClass.classLoader
|
||||
.getResource(defaultConfigurationFileResourcePath)
|
||||
?: throw IllegalStateException(
|
||||
"Missing default configuration file 'classpath:$defaultConfigurationFileResourcePath'")
|
||||
Files.newOutputStream(configurationFile).use { outputStream ->
|
||||
defaultConfigurationFileResource.openStream().use { inputStream ->
|
||||
JWO.copy(inputStream, outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
val schemaResource = "net/woggioni/gbcs/gbcs.xsd"
|
||||
val schemaUrl = URL("classpath:net/woggioni/gbcs/gbcs.xsd")
|
||||
// val schemaUrl = GradleBuildCacheServer::class.java.classLoader.getResource(schemaResource)
|
||||
// ?: throw IllegalStateException("Missing configuration schema '$schemaResource'")
|
||||
val schemaUrl2 = URL(schemaUrl.toString())
|
||||
val dbf = Xml.newDocumentBuilderFactory()
|
||||
dbf.schema = Xml.getSchema(schemaUrl)
|
||||
val doc = Files.newInputStream(configurationFile)
|
||||
.use(dbf.newDocumentBuilder()::parse)
|
||||
GradleBuildCacheServer(Configuration.parse(doc.documentElement)).run()
|
||||
}
|
||||
|
||||
fun digest(data : ByteArray,
|
||||
|
||||
Reference in New Issue
Block a user