// Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. // // WSO2 Inc. licenses this file to you 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. import ballerina/file; import ballerina/filepath; import ballerina/http; import ballerina/log; import ballerina/mime; //// Configuration related properties. boolean isSinglePageApplication = true; // file extension mapping to content types final map<string> MIME_MAP = { "json": mime:APPLICATION_JSON, "xml": mime:TEXT_XML, balo: mime:APPLICATION_OCTET_STREAM, css: "text/css", gif: "image/gif", html: mime:TEXT_HTML, ico: "image/x-icon", jpeg: "image/jpeg", jpg: "image/jpeg", js: "application/javascript", png: "image/png", svg: "image/svg+xml", txt: mime:TEXT_PLAIN, woff2: "font/woff2", zip: "application/zip" }; @http:ServiceConfig { basePath: "/", chunking: http:CHUNKING_NEVER } service webServerSvc on new http:Listener(9090) { @http:ResourceConfig { methods: ["GET"], path: "/*" } resource function serveHtmlFiles(http:Caller caller, http:Request request) { http:Response res = new; string requestedFilePath = <@untainted> <string> request.extraPathInfo; requestedFilePath = checkpanic filepath:build("app", requestedFilePath); // If requested for a file(not directory), check if its available in the server and response. if (file:exists(requestedFilePath) && isFile(requestedFilePath)) { res = getFileAsResponse(requestedFilePath); error? clientResponse = caller->respond(res); if (clientResponse is error) { log:printError("unable respond back", err = clientResponse); } return; } // If requested is a directory, check and append forward slash. if (!requestedFilePath.endsWith("/")) { if (requestedFilePath.length() > 0) { requestedFilePath = requestedFilePath + "/"; } } // Resolve to index.html of the requested directory requestedFilePath = requestedFilePath + "index.html"; // resolve to index.html if not file it found. if (isSinglePageApplication && !file:exists(requestedFilePath)) { res = getFileAsResponse(checkpanic filepath:build("app", "index.html")); } else { res = getFileAsResponse(requestedFilePath); } error? clientResponse = caller->respond(res); if (clientResponse is error) { log:printError("unable respond back", err = clientResponse); } } @http:ResourceConfig { methods: ["GET"], path: "/static/*" } resource function serveStaticFiles(http:Caller caller, http:Request request) { // TODO: properly untaint the request.rawPath string|error requestedFilePath = filepath:build("app", <@untainted> <string> request.rawPath); http:Response res = new; if (requestedFilePath is string) { res = getFileAsResponse(requestedFilePath); } else { res.setTextPayload("server error occurred."); res.statusCode = 500; log:printError("unable to resolve file path", err = requestedFilePath); } error? clientResponse = caller->respond(res); if (clientResponse is error) { log:printError("unable respond back", err = clientResponse); } } } # Serve a file as a http response. # # + requestedFile - The path of the file to server. # + return - The http response. function getFileAsResponse(string requestedFile) returns http:Response { http:Response res = new; // Figure out content-type string contentType = mime:APPLICATION_OCTET_STREAM; string fileExtension = checkpanic filepath:extension(requestedFile); if (fileExtension != "") { contentType = getMimeTypeByExtension(fileExtension); } // Check if file exists. if (file:exists(requestedFile)) { res.setFileAsPayload(requestedFile, contentType = contentType); } else { log:printError("unable to find file: " + requestedFile); res.setTextPayload("the server was not able to find what you were looking for."); res.statusCode = http:STATUS_NOT_FOUND; } return res; } # Get the content type using a file extension. # # + extension - The file extension. # + return - The content type if a match is found, else application/octet-stream. function getMimeTypeByExtension(string extension) returns string { var contentType = MIME_MAP[extension.toLowerAscii()]; if (contentType is string) { return contentType; } else { return mime:APPLICATION_OCTET_STREAM; } } # Check if a file path is a file and not a directory. # # + filePath - Path to the file # + return - True if file, else false function isFile(string filePath) returns boolean { return !(file:readDir(filePath) is file:FileInfo[]); }