• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / roku-debug / 26120672946

19 May 2026 07:37PM UTC coverage: 70.727% (+0.7%) from 70.049%
26120672946

Pull #351

github

web-flow
Merge eb0b2e542 into 5bbd82240
Pull Request #351: 0.23.8

3328 of 5046 branches covered (65.95%)

Branch coverage included in aggregate %.

5834 of 7908 relevant lines covered (73.77%)

35.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

20.75
/src/ComponentLibraryServer.ts
1
import * as fs from 'fs';
2
import * as http from 'http';
2✔
3
import * as os from 'os';
2✔
4
import * as path from 'path';
2✔
5
import * as url from 'url';
2✔
6

2✔
7
import { util } from './util';
2✔
8

2✔
9
export class ComponentLibraryServer {
2✔
10

11
    public componentLibrariesOutDir: string;
12

13
    private server: http.Server;
×
14

×
15
    public async startStaticFileHosting(componentLibrariesOutDir: string, port: number, sendDebugLogLine) {
16

×
17
        // Make sure the requested port is not already being used by another service
18
        if (await util.isPortInUse(port)) {
19
            throw new Error(`Could not host component library files.\nPort ${port} is currently occupied.`);
×
20
        }
21

22
        this.componentLibrariesOutDir = componentLibrariesOutDir;
23

24
        // prepare static file hosting
25
        // maps file extension to MIME types
26
        const mimeType = {
27
            '.ico': 'image/x-icon',
28
            '.html': 'text/html',
29
            '.js': 'text/javascript',
30
            '.json': 'application/json',
31
            '.css': 'text/css',
32
            '.png': 'image/png',
33
            '.jpg': 'image/jpeg',
34
            '.wav': 'audio/wav',
35
            '.mp3': 'audio/mpeg',
36
            '.svg': 'image/svg+xml',
×
37
            '.pdf': 'application/pdf',
×
38
            '.doc': 'application/msword',
39
            '.eot': 'appliaction/vnd.ms-fontobject',
×
40
            '.ttf': 'aplication/font-sfnt',
41
            '.zip': 'application/zip'
42
        } as Record<string, string>;
43

44
        this.server = http.createServer((req, res) => {
×
45
            sendDebugLogLine(`${req.method} ${req.url}`);
×
46

×
47
            // parse URL
×
48
            const parsedUrl = url.parse(req.url);
49

×
50
            // extract URL path
×
51
            // Avoid https://en.wikipedia.org/wiki/Directory_traversal_attack
×
52
            // e.g curl --path-as-is http://localhost:9000/../fileInDanger.txt
53
            // by limiting the path to component libraries out directory only
54
            const sanitizePath = path.normalize(parsedUrl.pathname).replace(/^(\.\.[\/\\])+/, '');
55
            let pathname = path.join(this.componentLibrariesOutDir, sanitizePath);
56

57
            fs.exists(pathname, (exist) => {
58
                if (!exist) {
×
59
                    // if the file is not found, return 404
×
60
                    res.statusCode = 404;
×
61
                    res.end(`File ${pathname} not found!`);
×
62
                    return;
63
                }
64

65
                // if is a directory
×
66
                // if (fs.statSync(pathname).isDirectory()) {
67
                // TODO: add support for directory listing
×
68
                // }
×
69

70
                // read file from file system
71
                fs.readFile(pathname, (err, data) => {
72
                    if (err) {
73
                        res.statusCode = 500;
×
74
                        res.end(`Error getting the file: ${err}.`);
75
                    } else {
×
76
                        // based on the URL path, extract the file extension. e.g. .js, .doc, ...
×
77
                        const ext = path.parse(pathname).ext;
×
78
                        // if the file is found, set Content-type and send data
×
79
                        res.setHeader('Content-type', mimeType[ext] || 'text/plain');
80
                        res.end(data);
×
81
                    }
82
                });
×
83
            });
84

85
        }).listen(port);
86

87
        sendDebugLogLine(`Server listening on port ${port}`);
88

89
        // print possible IP addresses that may be the users local ip
90
        let ifaces = os.networkInterfaces();
15!
91
        for (const ifname of Object.keys(ifaces)) {
×
92
            for (const iface of ifaces[ifname]) {
93
                if (iface.family !== 'IPv4' || iface.internal !== false) {
94
                    // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses
95
                    return;
2✔
96
                }
97

98
                sendDebugLogLine(`Potential target ip for component libraries: ${iface.address}`);
99
            }
100
        }
101
    }
102

103
    /**
104
     * Stop the server (if it's running)
105
     */
106
    public stop() {
107
        if (this.server) {
108
            this.server.close();
109
        }
110
    }
111
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc