diff --git a/.github/test-logging.js b/.github/test-logging.js new file mode 100644 index 0000000..7b3677b --- /dev/null +++ b/.github/test-logging.js @@ -0,0 +1,31 @@ +const http = require('http'); +const fs = require('fs'); +const process = require('process'); + +const options = { + hostname: 'localhost', + port: 9090, + path: '/', + method: 'POST', +}; + + +const req = http.request(options, res => { + const buf = fs.readFileSync('log.txt').toString(); + const expect = `POST / "Hello World"\n` + if (expect !== buf) { + console.error(`expected ${expect}, but got ${buf}`); + process.exit(1); + return; + } +}) + +req.on('error', error => { + console.error(error); + process.exit(1); + return; +}); + +req.write('Hello World'); + +req.end(); \ No newline at end of file diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 22171f4..1353c4e 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -26,11 +26,8 @@ jobs: { "md": "text/markdown" } - log: "log.txt" - - run: | - node .github/test-default.js - cat log.txt + run: node .github/test-default.js test-index-files: strategy: @@ -52,11 +49,8 @@ jobs: no-cache: true index-files: | ["PULL_REQUEST_TEMPLATE.md"] - log: "log.txt" - - run: | - node .github/test-index-files.js - cat log.txt + run: node .github/test-index-files.js test-allowed-methods: strategy: @@ -78,23 +72,8 @@ jobs: no-cache: true allowed-methods: | ["POST"] - log: "log.txt" - - run: | - node .github/test-allowed-methods.js - cat log.txt - - # draft your next release notes as pull requests are merged into "master" - # the configuration is at /.github/release-drafter.yml. - update_release_draft: - runs-on: ubuntu-latest - steps: - - uses: release-drafter/release-drafter@v5 - if: github.ref == 'refs/heads/master' - with: - config-name: release-drafter.yml - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: node .github/test-allowed-methods.js test-logging: strategy: matrix: @@ -115,10 +94,20 @@ jobs: no-cache: true allowed-methods: | ["GET", "POST"] - log: "log.txt" + log: log.txt + logTime: false - - run: | - curl -vvvv http://localhost:9090/ - curl --header "Content-Type: application/json" --request POST --data '[{"msg":"hello"},{"msg":"World"}]' http://localhost:9090/ - - name: Show log - run: cat log.txt + run: node .github/test-logging.js + + + # draft your next release notes as pull requests are merged into "master" + # the configuration is at /.github/release-drafter.yml. + update_release_draft: + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + if: github.ref == 'refs/heads/master' + with: + config-name: release-drafter.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index e1ed241..6749bba 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,10 @@ Default: } ``` ### `log` -Create a log file with the given name (default is `off`) (*optional*) +Create a log file with the given name (default is ``, which means no logging) (*optional*) + +### `logTime` +Include the time of access in the log file (default is `true`) (*optional*) ## Example ```yaml @@ -73,6 +76,7 @@ steps: "xml": "text/xml" } log: "log.txt" + logTime: "false" - run: | curl -vvvv http://localhost:8080/index.html diff --git a/action.yml b/action.yml index 6f17d5f..4e911e3 100644 --- a/action.yml +++ b/action.yml @@ -49,7 +49,11 @@ inputs: log: description: 'Create a log file with given name' required: false - default: 'off' + default: '' + logTime: + description: 'Whether to include the time of access in the log file or not' + required: false + default: true runs: using: 'node16' main: 'main.js' diff --git a/main.js b/main.js index fce243f..a36ffa5 100644 --- a/main.js +++ b/main.js @@ -33,7 +33,8 @@ let config = { indexFiles: null, allowedMethods: null, contentTypes: null, - log: null + log: null, + logTime: null }; config.root = core.getInput('directory'); @@ -96,6 +97,13 @@ if (config.allowedMethods === null || config.allowedMethods.length == 0) { config.log = core.getInput("log"); +config.logTime = core.getInput('logTime'); +if (config.logTime === null || config.logTime.length == 0) { + config.logTime = true; +} else { + config.logTime = config.logTime === 'true'; +} + const cp = require('child_process'); const child = cp.fork(__filename, ['serve'], { detached: true, silent: true }); child.on('error', (err) => { diff --git a/server.js b/server.js index 821aa7f..0f165a8 100644 --- a/server.js +++ b/server.js @@ -26,7 +26,10 @@ function deploy(config, ready) { config.contentTypes = {}; } if (config.log == undefined || config.log == null) { - config.log = "off"; + config.log = ""; + } + if (config.logTime == undefined || config.logTime == null) { + config.logTime = true; } const root = path.resolve(path.normalize(config.root)); @@ -39,18 +42,14 @@ function deploy(config, ready) { return path.posix.join(...url.split(path.sep)); } - let writeLine = (line) => { - if (config.log !== "off") { - let txtLogger = fs.createWriteStream(config.log, { - flags: 'a' - }); - - txtLogger.write(`\n${line}`); - } - }; + let txtLogger = undefined; + if (config.log !== "") { + txtLogger = fs.createWriteStream(config.log, { + flags: 'a' + }); + } server.on('request', (request, response) => { - let now = formatTime.format(new Date()); let data = ''; request.on('data', (chunk) => { @@ -58,116 +57,116 @@ function deploy(config, ready) { }); request.on('end', () => { - writeLine(`[${now}] ${request.method} ${request.url} ${JSON.stringify(data)}`); - }); - + if (config.log !== "") { + let now = config.logTime ? `[${formatTime.format(new Date())}] ` : ''; + txtLogger.write(`${now}${request.method} ${request.url} ${JSON.stringify(data)}\n`); + } - if (config.noCache) { - response.setHeader( - 'Cache-Control', - 'no-cache, no-store, must-revalidate' - ); - } - - if (!config.allowedMethods.includes(request.method)) { - const body = 'Method Not Allowed'; - response.writeHead(405, { - 'Content-Length': Buffer.byteLength(body), - 'Content-Type': 'text/plain' - }); - response.end(body); - return; - } - const url = new URL(request.url, `http://${request.headers.host}`); - let requestedFile = path.resolve(path.normalize(path.join(cwd, ...url.pathname.split(path.posix.sep)))); - if (requestedFile !== root) { - if (!requestedFile.startsWith(cwd)) { - const body = 'Not Found'; - response.writeHead(404, { - 'Content-Length': Buffer.byteLength(body), - 'Content-Type': 'text/plain' - }); - response.end(body); - return; + if (config.noCache) { + response.setHeader( + 'Cache-Control', + 'no-cache, no-store, must-revalidate' + ); } - if (!fs.existsSync(requestedFile)) { - const body = 'Not Found'; - response.writeHead(404, { + if (!config.allowedMethods.includes(request.method)) { + const body = 'Method Not Allowed'; + response.writeHead(405, { 'Content-Length': Buffer.byteLength(body), 'Content-Type': 'text/plain' }); response.end(body); return; } - } - - let stat = fs.statSync(requestedFile); + const url = new URL(request.url, `http://${request.headers.host}`); + let requestedFile = path.resolve(path.normalize(path.join(cwd, ...url.pathname.split(path.posix.sep)))); + if (requestedFile !== root) { + if (!requestedFile.startsWith(cwd)) { + const body = 'Not Found'; + response.writeHead(404, { + 'Content-Length': Buffer.byteLength(body), + 'Content-Type': 'text/plain' + }); + response.end(body); + return; + } - if (stat.isDirectory()) { - if (!requestedFile.endsWith(path.sep)) { - requestedFile += path.sep; - } - const noIndexFound = config.indexFiles.every(elem => { - const indexFile = requestedFile + elem; - if (fs.existsSync(indexFile)) { - requestedFile = indexFile; - stat = fs.statSync(requestedFile); - return false; + if (!fs.existsSync(requestedFile)) { + const body = 'Not Found'; + response.writeHead(404, { + 'Content-Length': Buffer.byteLength(body), + 'Content-Type': 'text/plain' + }); + response.end(body); + return; } - return true; - }); + } - if (noIndexFound) { - response.writeHead(200, { - 'Content-Type': 'text/html' + let stat = fs.statSync(requestedFile); + if (stat.isDirectory()) { + if (!requestedFile.endsWith(path.sep)) { + requestedFile += path.sep; + } + const noIndexFound = config.indexFiles.every(elem => { + const indexFile = requestedFile + elem; + if (fs.existsSync(indexFile)) { + requestedFile = indexFile; + stat = fs.statSync(requestedFile); + return false; + } + return true; }); - if (request.method === 'HEAD') { + if (noIndexFound) { + response.writeHead(200, { + 'Content-Type': 'text/html' + }); + + if (request.method === 'HEAD') { + response.end(); + return; + } + response.write('
\n');
+
+                    let parentDir = path.resolve(path.normalize(path.join(requestedFile, '..')));
+                    if (!parentDir.endsWith(path.sep)) {
+                        parentDir += path.sep;
+                    }
+                    if (parentDir.startsWith(cwd)) {
+                        let parentLink = '/' + toPosixPath(parentDir.slice(cwd.length));
+                        if (parentLink === '/.') {
+                            parentLink = '/';
+                        }
+                        response.write(`..\n`);
+                    }
+
+                    for (const file of fs.readdirSync(requestedFile)) {
+                        const fullPath = requestedFile + file;
+                        response.write(`${file}\n`);
+                    }
+                    response.write('
'); response.end(); return; } - response.write('
\n');
+            }
 
-                let parentDir = path.resolve(path.normalize(path.join(requestedFile, '..')));
-                if (!parentDir.endsWith(path.sep)) {
-                    parentDir += path.sep;
-                }
-                if (parentDir.startsWith(cwd)) {
-                    let parentLink = '/' + toPosixPath(parentDir.slice(cwd.length));
-                    if (parentLink === '/.') {
-                        parentLink = '/';
-                    }
-                    response.write(`..\n`);
-                }
+            let headers = {
+                'Content-Length': stat.size,
+            }
 
-                for (const file of fs.readdirSync(requestedFile)) {
-                    const fullPath = requestedFile + file;
-                    response.write(`${file}\n`);
-                }
-                response.write('
'); + const contentType = path.extname(requestedFile).slice(1); + if (config.contentTypes[contentType]) { + headers['Content-Type'] = config.contentTypes[contentType]; + } + response.writeHead(200, headers); + if (request.method === 'HEAD') { response.end(); return; } - } - - const contentType = path.extname(requestedFile).slice(1); - - let headers = { - 'Content-Length': stat.size, - } - - if (config.contentTypes[contentType]) { - headers['Content-Type'] = config.contentTypes[contentType]; - } - response.writeHead(200, headers); - if (request.method === 'HEAD') { - response.end(); - return; - } - var readStream = fs.createReadStream(requestedFile); - readStream.pipe(response); + var readStream = fs.createReadStream(requestedFile); + readStream.pipe(response); + }) }); server.listen(config.port, () => {