diff --git a/examples/src/netty/netty.kt b/examples/src/netty/netty.kt deleted file mode 100644 index 643a9801e1a..00000000000 --- a/examples/src/netty/netty.kt +++ /dev/null @@ -1,141 +0,0 @@ -import java.util.concurrent.Executors -import java.net.InetSocketAddress -import java.net.URLDecoder -import java.io.* - -import org.jboss.netty.bootstrap.ServerBootstrap -import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory -import org.jboss.netty.channel.* -import org.jboss.netty.handler.codec.http.* -import org.jboss.netty.handler.stream.ChunkedWriteHandler -import org.jboss.netty.handler.codec.frame.TooLongFrameException -import org.jboss.netty.buffer.ChannelBuffers -import org.jboss.netty.util.CharsetUtil -import org.jboss.netty.handler.codec.http.HttpResponseStatus.* - -import netty.* -import jlstring.* - -package jlstring { - fun String.replace(c: Char, by: Char) : String = (this as java.lang.String).replace(c, by) as String - - fun String.contains(s: String) : Boolean = (this as java.lang.String).contains(s as java.lang.CharSequence) - - fun java.lang.String.plus(s: Any?) : String = (this as String) + s.toString() -} - -package netty { - fun ChannelPipeline.with(op: fun ChannelPipeline.() ) : ChannelPipeline { - this.op() - return this - } - - fun HttpResponse.set(header: String, value: Any?) : Unit { - setHeader(header, value) - } - - fun HttpResponse.setContent(content: Any?) : Unit { - setContent(ChannelBuffers.copiedBuffer(content.toString() as java.lang.String, CharsetUtil.UTF_8)) - } - - fun ChannelHandlerContext.sendError(status: HttpResponseStatus?) { - val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, status) - response["Content-Type"] = "text/plain; charset=UTF-8" - response.setContent("Failure: " + status.toString() + "\r\n") - - // Close the connection as soon as the error message is sent. - this.getChannel()?.write(response)?.addListener(ChannelFutureListener.CLOSE) - } - - fun String.decodeURI(encoding : String) : String? { - try { - return URLDecoder.decode(this, encoding) - } - catch (e : UnsupportedEncodingException) { - return null - } - } - - fun String.sanitizeUri() : String? { - val path = decodeURI("UTF-8") ?: decodeURI("ISO-8859-1") - if (path == null) - throw Error() - - val localizedPath : String = path.replace('/', File.separatorChar) - return if (localizedPath.contains(File.separator + ".") || - localizedPath.contains("." + File.separator) || - localizedPath.startsWith(".") || endsWith(".")) - null - else - localizedPath - } - - class StandardPipelineFactory(val config: fun ChannelPipeline.():Unit) : ChannelPipelineFactory { - override fun getPipeline() : ChannelPipeline { - val pipeline = DefaultChannelPipeline().with { - addLast("decoder", HttpRequestDecoder()) - addLast("aggregator", HttpChunkAggregator(65536)) - addLast("encoder", HttpResponseEncoder()) - addLast("chunkedWriter", ChunkedWriteHandler()) - } - pipeline.config () // can not move it inside with{} because of codegen bug - return pipeline - } - } - - fun ServerBootstrap.configPipeline(config: fun ChannelPipeline.(): Unit) = - setPipelineFactory( - StandardPipelineFactory(config) - ) -} - -fun main(args : Array) { - val bootstrap = ServerBootstrap( - NioServerSocketChannelFactory( - Executors.newCachedThreadPool(), - Executors.newCachedThreadPool() - ) - ) - bootstrap.configPipeline { - addLast("handler", HttpStaticFileServerHandler(args[0])) - } - bootstrap.bind(InetSocketAddress(8080)) -} - -class HttpStaticFileServerHandler(val rootDir: String) : SimpleChannelUpstreamHandler() { - - fun messageReceived(ctx : ChannelHandlerContext, e : MessageEvent) { - val request = e.getMessage() as HttpRequest - if (request.getMethod() != HttpMethod.GET) { - ctx.sendError(METHOD_NOT_ALLOWED); - return; - } - - val path = request.getUri()?.sanitizeUri() - if (path == null) { - ctx.sendError(FORBIDDEN); - return; - } - - val file = File(rootDir + File.separator + path) - if (file.isHidden() || !file.exists()) { - ctx.sendError(NOT_FOUND) - return - } - if (!file.isFile()) { - ctx.sendError(FORBIDDEN) - return - } - - var raf = RandomAccessFile(file, "r") - - val fileLength = raf.length() - - val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, OK) - response["Content-Length"] = fileLength - - val ch = e.getChannel() - ch?.write(response) - ch?.write(DefaultFileRegion(raf.getChannel(), 0, fileLength)) - } -} diff --git a/examples/src/nettyserver/lib/netty-3.2.5.Final-sources.jar b/examples/src/nettyserver/lib/netty-3.2.5.Final-sources.jar new file mode 100644 index 00000000000..cb6cb9bf537 Binary files /dev/null and b/examples/src/nettyserver/lib/netty-3.2.5.Final-sources.jar differ diff --git a/examples/src/nettyserver/lib/netty-3.2.5.Final.jar b/examples/src/nettyserver/lib/netty-3.2.5.Final.jar new file mode 100644 index 00000000000..f3b79fcbaa7 Binary files /dev/null and b/examples/src/nettyserver/lib/netty-3.2.5.Final.jar differ diff --git a/examples/src/nettyserver/nettyserver.iml b/examples/src/nettyserver/nettyserver.iml new file mode 100644 index 00000000000..6d0961b99e4 --- /dev/null +++ b/examples/src/nettyserver/nettyserver.iml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/src/nettyserver/nettyserver.ipr b/examples/src/nettyserver/nettyserver.ipr new file mode 100644 index 00000000000..d7cc3bde365 --- /dev/null +++ b/examples/src/nettyserver/nettyserver.ipr @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + http://www.w3.org/1999/xhtml + + + + + + + + + + + + + + + + + + diff --git a/examples/src/nettyserver/nettyserver.iws b/examples/src/nettyserver/nettyserver.iws new file mode 100644 index 00000000000..2873cd9a185 --- /dev/null +++ b/examples/src/nettyserver/nettyserver.iws @@ -0,0 +1,750 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + localhost + 5050 + + + + + + + 1328521187492 + 1328521187492 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + No facets are configured + + + + + + + + + + + + + + + 1.6 + + + + + + + + nettyserver + + + + + + + + 1.6 + + + + + + + + KotlinRuntime + + + + + + + + + diff --git a/examples/src/nettyserver/src/HttpHandler.kt b/examples/src/nettyserver/src/HttpHandler.kt new file mode 100644 index 00000000000..b9e10594fb2 --- /dev/null +++ b/examples/src/nettyserver/src/HttpHandler.kt @@ -0,0 +1,41 @@ +package org.jetbrains.kotlin.examples.netty + +import org.jboss.netty.channel.SimpleChannelUpstreamHandler +import java.util.LinkedList +import org.jboss.netty.channel.ChannelHandlerContext +import org.jboss.netty.channel.MessageEvent +import org.jboss.netty.handler.codec.http.HttpResponseStatus.* +import org.jboss.netty.handler.codec.http.HttpMethod +import org.jboss.netty.handler.codec.http.HttpResponseStatus + +class HttpHandler(config: HttpHandler.() -> Unit) : SimpleChannelUpstreamHandler() { + private var processors = LinkedList(); + + var onNotFound : (RequestResponse)->Any? = { request -> request.setError(NOT_FOUND) }; + + { + this.bind(config)() + } + + fun messageReceived(ctx : ChannelHandlerContext, e : MessageEvent) { + val request = RequestResponse(e) + for(processor in processors) { + if(processor.tryToProcess(request)) { + request.write() + return + } + } + + (onNotFound)(request) + } + + fun static(prefix: String = "/", directory: String) { + processors.add(StaticFileProcessor(prefix, directory)) + } + + fun rest(prefix: String, config: RestBuilder.()->Any?) { + val builder = RestBuilder() + (builder.bind(config))() + processors.add(RestProcessor(prefix, builder)) + } +} diff --git a/examples/src/nettyserver/src/Main.kt b/examples/src/nettyserver/src/Main.kt new file mode 100644 index 00000000000..b0ca453fcd5 --- /dev/null +++ b/examples/src/nettyserver/src/Main.kt @@ -0,0 +1,40 @@ +package org.jetbrains.kotlin.examples.netty + +import org.jboss.netty.handler.codec.http.HttpResponseStatus +import org.jboss.netty.handler.codec.http.DefaultHttpResponse +import org.jboss.netty.handler.codec.http.HttpVersion +import org.jboss.netty.buffer.ChannelBuffers +import java.nio.charset.Charset + +fun main(args : Array) { + if(args.size == 0) { + println("Please provide command line argument ") + System.exit(1) + } + else { + println("Please open http://localhost:8080/index.html page in your browser") + } + + httpServer(8080) { + static(directory = args[0]) + + rest("/sayhello") { + GET { + response.content = "Hello, World!" + } + POST { + response.content = "You said: ${request.getContent().sure().toString(Charset.defaultCharset())}" + } + } + + onNotFound = { requestResponse -> + when(requestResponse.path) { + "/prototype.js" -> { + requestResponse.redirect("https://ajax.googleapis.com/ajax/libs/prototype/1.7.0.0/prototype.js") + } + else -> + requestResponse.setError(HttpResponseStatus.FORBIDDEN) + } + } + } +} diff --git a/examples/src/nettyserver/src/Netty.kt b/examples/src/nettyserver/src/Netty.kt new file mode 100644 index 00000000000..04d5ac3bb0f --- /dev/null +++ b/examples/src/nettyserver/src/Netty.kt @@ -0,0 +1,66 @@ +package org.jetbrains.kotlin.examples.netty + +import java.util.concurrent.Executors +import java.net.* +import java.io.* + +import org.jboss.netty.bootstrap.ServerBootstrap +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory +import org.jboss.netty.channel.* +import org.jboss.netty.handler.codec.http.* +import org.jboss.netty.handler.stream.ChunkedWriteHandler +import org.jboss.netty.handler.codec.frame.TooLongFrameException +import org.jboss.netty.buffer.ChannelBuffers +import org.jboss.netty.util.CharsetUtil +import org.jboss.netty.handler.codec.http.HttpResponseStatus.* + +import org.jboss.netty.bootstrap.ServerBootstrap +import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory +import org.jboss.netty.channel.* +import org.jboss.netty.handler.codec.http.* +import org.jboss.netty.handler.stream.ChunkedWriteHandler +import org.jboss.netty.handler.codec.frame.TooLongFrameException +import org.jboss.netty.buffer.ChannelBuffers +import org.jboss.netty.util.CharsetUtil +import org.jboss.netty.handler.codec.http.HttpResponseStatus.* +import java.util.LinkedList + +// This is workaround for compiler bug +fun T.bind(op: T.()->Any?) = { op() } + +fun HttpResponse.set(header: String, value: Any?) : Unit { + setHeader(header, value) +} + +var HttpResponse.content : Any? + get() = throw UnsupportedOperationException() + set(c: Any?) { + val buffer = ChannelBuffers.copiedBuffer(c.toString(), CharsetUtil.UTF_8).sure() + setContent(buffer) + setHeader("Content-Length", buffer.readableBytes()) + } + +fun ServerBootstrap.configPipeline(config: ChannelPipeline.() -> Unit) = setPipelineFactory(object: ChannelPipelineFactory { + override fun getPipeline() : ChannelPipeline { + val pipeline = DefaultChannelPipeline() + pipeline.bind(config) () + return pipeline + } +}) + +fun httpServer(port: Int, handler: HttpHandler.() -> Unit) { + val bootstrap = ServerBootstrap( + NioServerSocketChannelFactory( + Executors.newCachedThreadPool(), + Executors.newCachedThreadPool() + ) + ) + bootstrap.configPipeline { + addLast("decoder", HttpRequestDecoder()) + addLast("aggregator", HttpChunkAggregator(65536)) + addLast("encoder", HttpResponseEncoder()) + addLast("chunkedWriter", ChunkedWriteHandler()) + addLast("handler", HttpHandler(handler)) + } + bootstrap.bind(InetSocketAddress(port)) +} diff --git a/examples/src/nettyserver/src/Request.kt b/examples/src/nettyserver/src/Request.kt new file mode 100644 index 00000000000..865be4455ef --- /dev/null +++ b/examples/src/nettyserver/src/Request.kt @@ -0,0 +1,80 @@ +package org.jetbrains.kotlin.examples.netty + +import org.jboss.netty.channel.SimpleChannelUpstreamHandler +import java.util.LinkedList +import org.jboss.netty.channel.ChannelHandlerContext +import org.jboss.netty.channel.MessageEvent +import org.jboss.netty.handler.codec.http.HttpResponseStatus.* +import org.jboss.netty.handler.codec.http.HttpRequest +import org.jboss.netty.channel.Channel +import org.jboss.netty.handler.codec.http.HttpResponseStatus +import org.jboss.netty.handler.codec.http.DefaultHttpResponse +import org.jboss.netty.channel.ChannelFutureListener +import org.jboss.netty.handler.codec.http.HttpVersion +import java.io.File +import java.io.UnsupportedEncodingException +import java.net.URLDecoder +import org.jboss.netty.handler.codec.http.HttpHeaders.* +import org.jboss.netty.handler.codec.http.HttpMessage +import org.jboss.netty.channel.ChannelFuture + +class RequestResponse(e: MessageEvent) { + val request = e.getMessage() as HttpRequest + val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN) + val channel = e.getChannel().sure() + val path = request.getUri().sure().sanitizeUri() + + fun setError(status: HttpResponseStatus?) { + response.setStatus(status) + response["Content-Type"] = "text/plain; charset=UTF-8" + response.content = "Failure: " + status.toString() + "\r\n" + } + + fun redirect(path: String) { + response.setStatus(HttpResponseStatus.MOVED_PERMANENTLY) + response["Location"] = path + response["Content-Length"] = 0 + channel.write(response) + } + + fun ok() : RequestResponse { + response.setStatus(HttpResponseStatus.OK) + return this + } + + fun write() = + if(response.getStatus().sure().getCode() >= 400) { + channel.write(response).sure().addListener(ChannelFutureListener.CLOSE) + } + else { + channel.write(response) + } +} + + +fun String.sanitizeUri() : String { + val path = decodeURI("UTF-8") ?: decodeURI("ISO-8859-1") ?: throw Error() + + val localizedPath = path.replace('/', File.separatorChar) + + return if (localizedPath.contains(File.separator + ".") || + localizedPath.contains("." + File.separator) || + localizedPath.startsWith(".") || localizedPath.endsWith(".")) + throw Error() + else + localizedPath +} + +fun String.decodeURI(encoding : String) : String? { + try { + return URLDecoder.decode(this, encoding) + } + catch (e : UnsupportedEncodingException) { + return null + } +} + +trait Processor { + fun tryToProcess(request: RequestResponse) : Boolean +} + diff --git a/examples/src/nettyserver/src/RestProcessor.kt b/examples/src/nettyserver/src/RestProcessor.kt new file mode 100644 index 00000000000..a2ff84c76d6 --- /dev/null +++ b/examples/src/nettyserver/src/RestProcessor.kt @@ -0,0 +1,37 @@ +package org.jetbrains.kotlin.examples.netty + +import org.jboss.netty.handler.codec.http.HttpMethod +import org.jboss.netty.handler.codec.http.HttpResponseStatus + +class RestBuilder() { + var onGet : (RequestResponse.()->Any?)? = null + var onPost : (RequestResponse.()->Any?)? = null + + fun GET(handler: RequestResponse.()->Unit) { + onGet = handler + } + + fun POST(handler: RequestResponse.()->Unit) { + onPost = handler + } +} + +class RestProcessor(val prefix: String, val builder: RestBuilder) : Processor { + override fun tryToProcess(request: RequestResponse): Boolean { + if(request.path.startsWith(prefix)) { + if(request.request.getMethod() == HttpMethod.GET && builder.onGet != null) { + request.ok() + request.(builder.onGet.sure())() + } + else if(request.request.getMethod() == HttpMethod.POST && builder.onPost != null) { + request.ok() + request.(builder.onPost.sure())() + } + else { + request.setError(HttpResponseStatus.METHOD_NOT_ALLOWED) + } + return true; + } + return false + } +} \ No newline at end of file diff --git a/examples/src/nettyserver/src/StaticFileProcessor.kt b/examples/src/nettyserver/src/StaticFileProcessor.kt new file mode 100644 index 00000000000..6abd56d0812 --- /dev/null +++ b/examples/src/nettyserver/src/StaticFileProcessor.kt @@ -0,0 +1,46 @@ +package org.jetbrains.kotlin.examples.netty + +import org.jboss.netty.handler.codec.http.HttpMethod +import java.io.File +import java.io.RandomAccessFile +import org.jboss.netty.handler.codec.http.DefaultHttpResponse +import org.jboss.netty.channel.DefaultFileRegion +import org.jboss.netty.handler.codec.http.HttpVersion +import org.jboss.netty.handler.codec.http.HttpResponseStatus.* + +class StaticFileProcessor(val prefix: String, val root: String) : Processor { + override fun tryToProcess(request: RequestResponse) : Boolean { + if(request.path.startsWith(prefix)) { + return request.processStaticFile() + } + return false; + } + + fun RequestResponse.processStaticFile() : Boolean { + val file = File(root + File.separator + path.substring(prefix.length)) + if(!file.exists()) + return false; + + if (request.getMethod() != HttpMethod.GET) { + setError(METHOD_NOT_ALLOWED); + return true; + } + + if (file.isHidden() || !file.isFile()) { + setError(FORBIDDEN) + return true + } + + var raf = RandomAccessFile(file, "r") + + val fileLength = raf.length() + + val response = DefaultHttpResponse(HttpVersion.HTTP_1_1, OK) + response["Content-Length"] = fileLength + + channel.write(response) + channel.write(DefaultFileRegion(raf.getChannel(), 0, fileLength)) + return true; + } +} + diff --git a/examples/src/nettyserver/static/index.html b/examples/src/nettyserver/static/index.html new file mode 100644 index 00000000000..6ff68ee8c9f --- /dev/null +++ b/examples/src/nettyserver/static/index.html @@ -0,0 +1,31 @@ + + + + + + +
Waiting...
+ +
+ + \ No newline at end of file