From 4daa0a80e47ba303ccd49eb8941b4760a287c933 Mon Sep 17 00:00:00 2001 From: fmontesi Date: Wed, 14 Oct 2020 10:49:40 +0200 Subject: [PATCH] Upgrade to Jolie modules --- LeonardoAdminIface.iol | 0 README.md | 4 +- config.iol | 19 -------- docker/Dockerfile | 15 ++----- docker/Dockerfile.jre | 15 ++----- hooks.ol | 26 ++++++++--- internal/hooks/post_response.ol | 34 ++++++++++---- internal/hooks/pre_response.ol | 35 +++++++++++---- launcher.ol | 43 ++++++++++++++++++ main.ol | 80 +++++++++++++++------------------ ports/LeonardoAdmin.iol | 5 --- {cmd/serve => scripts}/serve | 2 +- 12 files changed, 163 insertions(+), 115 deletions(-) delete mode 100644 LeonardoAdminIface.iol delete mode 100644 config.iol create mode 100644 launcher.ol delete mode 100644 ports/LeonardoAdmin.iol rename {cmd/serve => scripts}/serve (95%) diff --git a/LeonardoAdminIface.iol b/LeonardoAdminIface.iol deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md index daad317..e743256 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,8 @@ If you simply want to use Leonardo to host some static content, you can run it a You just have to tell Leonardo where the static content is located. You can do it in two ways: -- Pass the content directory as an argument. For example, if your content is in `/var/www`, then you should run the command `jolie main.ol /var/www` in directory `cmd/leonardo`. -- Pass the content directory by using the environment variable `LEONARDO_WWW`. In this case, you just need to invoke `jolie main.ol` in directory `cmd/leonardo`. +- Pass the content directory as an argument. For example, if your content is in `/var/www`, then you should run the command `jolie launcher.ol /var/www`. +- Pass the content directory by using the environment variable `LEONARDO_WWW`. In this case, you just need to invoke `jolie launcher.ol`. # Make a Docker image with your own website diff --git a/config.iol b/config.iol deleted file mode 100644 index 23ac88c..0000000 --- a/config.iol +++ /dev/null @@ -1,19 +0,0 @@ -constants { - // The deployment location for reaching the Leonardo web server - Location_Leonardo = "socket://localhost:8080/", - - // The root directory in which Leonardo will look for content to serve to clients - RootContentDirectory = "/var/lib/leonardo/www/", - - // The default page to serve in case clients do not specify one - DefaultPage = "index.html", - - // Print debug messages for all exchanged HTTP messages - DebugHttp = false, - - // Add the content of every HTTP message to their debug messages - DebugHttpContent = false, - - // If false, we wait for an initialisation message from an embedder - Standalone = true -} diff --git a/docker/Dockerfile b/docker/Dockerfile index 2e17413..5df900f 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,15 +1,8 @@ -FROM alpine/git as Build - -RUN mkdir -p /app/leonardo -RUN git clone --depth=1 https://github.com/jolie/leonardo.git /app/leonardo -RUN rm -rf /app/leonardo/docker -RUN rm -rf /app/leonardo/.git - -# Start from scratch, copy leonardo FROM jolielang/jolie -WORKDIR / -COPY --from=Build /app /app -WORKDIR /app/leonardo/cmd/leonardo +COPY . /leonardo +RUN rm -rf /leonardo/docker +RUN rm -rf /leonardo/.git +WORKDIR /leonardo CMD ["jolie","main.ol"] diff --git a/docker/Dockerfile.jre b/docker/Dockerfile.jre index 748e6e5..dd10f98 100644 --- a/docker/Dockerfile.jre +++ b/docker/Dockerfile.jre @@ -1,15 +1,8 @@ -FROM alpine/git as Build - -RUN mkdir -p /app/leonardo -RUN git clone --depth=1 https://github.com/jolie/leonardo.git /app/leonardo -RUN rm -rf /app/leonardo/docker -RUN rm -rf /app/leonardo/.git - -# Start from scratch, copy leonardo FROM jolielang/jolie:jre -WORKDIR / -COPY --from=Build /app /app -WORKDIR /app/leonardo/cmd/leonardo +COPY . /leonardo +RUN rm -rf /leonardo/docker +RUN rm -rf /leonardo/.git +WORKDIR /leonardo CMD ["jolie","main.ol"] diff --git a/hooks.ol b/hooks.ol index acf7243..b7e5f53 100644 --- a/hooks.ol +++ b/hooks.ol @@ -1,9 +1,23 @@ -type LeonardoConfig { - wwwDir:string -} +/* + Copyright 2018-2020 Fabrizio Montesi + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ type DecoratedResponse { - config:LeonardoConfig + config { + wwwDir:string + } request { path:string } @@ -18,9 +32,9 @@ type PreResponseFaultType { type MaybeString: void | string interface PreResponseHookIface { -RequestResponse: run(DecoratedResponse)(MaybeString) throws PreResponseFault(PreResponseFaultType) +RequestResponse: run( DecoratedResponse )( MaybeString ) throws PreResponseFault( PreResponseFaultType ) } interface PostResponseHookIface { -RequestResponse: run(DecoratedResponse)(void) +RequestResponse: run( DecoratedResponse )( void ) } diff --git a/internal/hooks/post_response.ol b/internal/hooks/post_response.ol index 8a87b57..85b07f8 100644 --- a/internal/hooks/post_response.ol +++ b/internal/hooks/post_response.ol @@ -1,12 +1,30 @@ -include "../../types/PostResponseHookIface.iol" +/* + Copyright 2018-2020 Fabrizio Montesi -execution { concurrent } + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -inputPort Input { -Location: "local" -Interfaces: PostResponseHookIface -} + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +from ...hooks import PostResponseHookIface + +service DefaultPostResponseHook { + execution: concurrent + + inputPort Input { + location: "local" + interfaces: PostResponseHookIface + } -main { - run(mesg)() + main { + run( mesg )() + } } diff --git a/internal/hooks/pre_response.ol b/internal/hooks/pre_response.ol index f5b9cd9..a524f73 100644 --- a/internal/hooks/pre_response.ol +++ b/internal/hooks/pre_response.ol @@ -1,13 +1,30 @@ -include "../../types/PreResponseHookIface.iol" +/* + Copyright 2018-2020 Fabrizio Montesi -execution { concurrent } + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at -inputPort Input { -Location: "local" -Interfaces: PreResponseHookIface -} + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +from ...hooks import PreResponseHookIface + +service DefaultPreResponseHook { + execution: concurrent + + inputPort Input { + location: "local" + interfaces: PreResponseHookIface + } -main -{ - run(mesg)(mesg.content) + main { + run( mesg )( mesg.content ) + } } diff --git a/launcher.ol b/launcher.ol new file mode 100644 index 0000000..e9f107f --- /dev/null +++ b/launcher.ol @@ -0,0 +1,43 @@ +/* + Copyright 2020 Fabrizio Montesi + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +from runtime import Runtime + +service Launcher { + embed Runtime as Runtime + main { + if ( is_defined( args[0] ) ) { + dir = args[0] + } else { + getenv@Runtime( "LEONARDO_WWW" )( dir ) + } + + if( !(dir instanceof void) ) { + config.wwwDir = dir + } + + config.location = "socket://localhost:8080" + config.defaultPage = "index.html" + + loadEmbeddedService@Runtime( { + filepath = "main.ol" + service = "Leonardo" + params -> config + } )() + + linkIn( Shutdown ) + } +} \ No newline at end of file diff --git a/main.ol b/main.ol index f649672..536734d 100644 --- a/main.ol +++ b/main.ol @@ -25,9 +25,9 @@ from .hooks import PreResponseHookIface, PostResponseHookIface /// Configuration parameters type Params { - location?:string //< location on which the web server should be exposed. Default: socket://localhost:8080 + location:string //< location on which the web server should be exposed. wwwDir?:string //< path to the directory containing the web files. Default: /var/lib/leonardo/www/ - defaultPage?:string //< default page to be served when clients ask for a directory. Default: "index.html" + defaultPage:string //< default page to be served when clients ask for a directory. Default: "index.html" /// configuration parameters for the HTTP input port httpConfig? { /// default = false @@ -58,10 +58,11 @@ service Leonardo( params:Params ) { embed Runtime as Runtime inputPort HTTPInput { + location: params.location protocol: http { keepAlive = true // Keep connections open - debug = (!(params.httpConfig.debug instanceof void) && params.httpConfig.debug) - debug.showContent = (!(params.httpConfig.debug.showContent instanceof void) && params.httpConfig.debug.showContent) + debug = is_defined( params.httpConfig.debug ) && params.httpConfig.debug + debug.showContent = is_defined( params.httpConfig.debug.showContent ) && params.httpConfig.debug.showContent format -> format contentType -> mime statusCode -> statusCode @@ -70,7 +71,6 @@ service Leonardo( params:Params ) { default = "default" } - location: Location_Leonardo interfaces: HTTPInterface } @@ -84,23 +84,23 @@ service Leonardo( params:Params ) { define setCacheHeaders { shouldCache = false - if ( s.result[0] == "image" ) { + if( s.result[0] == "image" ) { shouldCache = true } else { e = file.filename e.suffix = ".js" endsWith@StringUtils( e )( shouldCache ) - if ( !shouldCache ) { + if( !shouldCache ) { e.suffix = ".css" endsWith@StringUtils( e )( shouldCache ) - if ( !shouldCache ) { + if( !shouldCache ) { e.suffix = ".woff" endsWith@StringUtils( e )( shouldCache ) } } } - if ( shouldCache ) { + if( shouldCache ) { cacheMaxAge = 60 * 60 * 2 // 2 hours } } @@ -108,25 +108,27 @@ service Leonardo( params:Params ) { define checkForMaliciousPath { for( maliciousSubstring in maliciousSubstrings ) { contains@StringUtils( s.result[0] { substring = maliciousSubstring } )( b ) - if ( b ) { throw( FileNotFound ) } + if( b ) { + throw( FileNotFound ) + } } } define loadHooks { - if ( is_defined( params.PreResponseHook ) ) { + if( is_defined( params.PreResponseHook ) ) { PreResponseHook << params.PreResponseHook } else { loadEmbeddedService@Runtime( { - filepath = "../../internal/hooks/pre_response.ol" + filepath = "internal/hooks/pre_response.ol" type = "Jolie" } )( PreResponseHook.location ) } - if ( is_defined( params.PostResponseHook ) ) { + if( is_defined( params.PostResponseHook ) ) { PostResponseHook << params.PostResponseHook } else { loadEmbeddedService@Runtime( { - filepath = "../../internal/hooks/post_response.ol" + filepath = "internal/hooks/post_response.ol" type = "Jolie" } )( PostResponseHook.location ) } @@ -158,33 +160,24 @@ service Leonardo( params:Params ) { } init { - getenv@Runtime( "LEONARDO_WWW" )( config.wwwDir ); - if ( is_defined( args[0] ) ) { - config.wwwDir = args[0] - } - - if ( !is_defined( config.wwwDir ) || config.wwwDir instanceof void ) { - config.wwwDir = RootContentDirectory + if( !is_defined( params.wwwDir ) ) { + params.wwwDir = "/var/lib/leonardo/www/" } setRedirections - undef( config.redirection ) - loadHooks - undef( config.PreResponseHook ) - undef( config.PostResponseHook ) - toAbsolutePath@File( config.wwwDir )( config.wwwDir ) + toAbsolutePath@File( params.wwwDir )( params.wwwDir ) getFileSeparator@File()( fs ) - config.wwwDir += fs + params.wwwDir += fs getServiceParentPath@File()( dir ) - setMimeTypeFile@File( dir + fs + ".." + fs + ".." + fs + "internal" + fs + "mime.types" )() + setMimeTypeFile@File( dir + fs + "internal" + fs + "mime.types" )() undef( dir ) undef( fs ) format = "html" - println@Console( "Leonardo started\n\tLocation: " + global.inputPorts.HTTPInput.location + "\n\tWeb directory: " + config.wwwDir )() + println@Console( "Leonardo started\n\tLocation: " + global.inputPorts.HTTPInput.location + "\n\tWeb directory: " + params.wwwDir )() } main { @@ -201,37 +194,38 @@ service Leonardo( params:Params ) { split@StringUtils( request.operation { regex = "\\?" } )( s ) - // Default page + // shouldAddIndex = false - if ( s.result[0] == "" ) { + if( s.result[0] == "" ) { shouldAddIndex = true } else { endsWith@StringUtils( s.result[0] { suffix = "/" } )( shouldAddIndex ) } - if ( shouldAddIndex ) { - s.result[0] += DefaultPage + if( shouldAddIndex ) { + s.result[0] += params.defaultPage } + // - checkForMaliciousPath; + checkForMaliciousPath - requestPath = s.result[0]; + requestPath = s.result[0] - file.filename = config.wwwDir + requestPath; + file.filename = params.wwwDir + requestPath - isDirectory@File( file.filename )( isDirectory ); - if ( isDirectory ) { + isDirectory@File( file.filename )( isDirectory ) + if( isDirectory ) { redirect = requestPath + "/" throw( MovedPermanently ) } getMimeType@File( file.filename )( mime ) split@StringUtils( mime { .regex = "/" } )( s ) - if ( s.result[0] == "text" ) { + if( s.result[0] == "text" ) { file.format = "text" format = "html" } else { file.format = format = "binary" - }; + } setCacheHeaders @@ -245,19 +239,19 @@ service Leonardo( params:Params ) { runPostResponseHook = false ) with( decoratedResponse ) { - .config -> config; + .config.wwwDir = params.wwwDir; .request.path = requestPath; if ( file.format == "text" ) { .content -> response } } run@PreResponseHook( decoratedResponse )( newResponse ) - if ( !(newResponse instanceof void) ) { + if( !(newResponse instanceof void) ) { response -> newResponse } } } ] { - if ( runPostResponseHook ) { + if( runPostResponseHook ) { run@PostResponseHook( decoratedResponse )() } } diff --git a/ports/LeonardoAdmin.iol b/ports/LeonardoAdmin.iol deleted file mode 100644 index 9112217..0000000 --- a/ports/LeonardoAdmin.iol +++ /dev/null @@ -1,5 +0,0 @@ -include "../types/LeonardoAdminIface.iol" - -outputPort LeonardoAdmin { - interfaces: LeonardoAdminIface -} diff --git a/cmd/serve/serve b/scripts/serve similarity index 95% rename from cmd/serve/serve rename to scripts/serve index d158cd4..8af527c 100755 --- a/cmd/serve/serve +++ b/scripts/serve @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright 2018 Fabrizio Montesi +# Copyright 2018-2020 Fabrizio Montesi # # Permission is hereby granted, free of charge, to any person obtaining a copy of # this software and associated documentation files (the "Software"), to deal in