summaryrefslogtreecommitdiffstats
path: root/packages/gitbook/src/cli/server.js
blob: 930e5c0b242144d423ed8956b3cdc77c33d5b3e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
const events = require('events');
const http = require('http');
const send = require('send');
const url = require('url');

const Promise = require('../utils/promise');

class Server extends events.EventEmitter {
    constructor() {
        super();
        this.running = null;
        this.dir = null;
        this.port = 0;
        this.sockets = [];
    }

    /**
     * Return true if the server is running
     * @return {Boolean}
     */
    isRunning() {
        return !!this.running;
    }

    /**
     * Stop the server
     * @return {Promise}
     */
    stop() {
        const that = this;
        if (!this.isRunning()) return Promise();

        const d = Promise.defer();
        this.running.close((err) => {
            that.running = null;
            that.emit('state', false);

            if (err) d.reject(err);
            else d.resolve();
        });

        for (let i = 0; i < this.sockets.length; i++) {
            this.sockets[i].destroy();
        }

        return d.promise;
    }

    /**
     * Start the server
     * @return {Promise}
     */
    start(dir, port) {
        const that = this;
        let pre = Promise();
        port = port || 8004;

        if (that.isRunning()) pre = this.stop();
        return pre
        .then(() => {
            const d = Promise.defer();

            that.running = http.createServer((req, res) => {
                // Render error
                function error(err) {
                    res.statusCode = err.status || 500;
                    res.end(err.message);
                }

                // Redirect to directory's index.html
                function redirect() {
                    const resultURL = urlTransform(req.url, (parsed) => {
                        parsed.pathname += '/';
                        return parsed;
                    });

                    res.statusCode = 301;
                    res.setHeader('Location', resultURL);
                    res.end('Redirecting to ' + resultURL);
                }

                res.setHeader('X-Current-Location', req.url);

                // Send file
                send(req, url.parse(req.url).pathname, {
                    root: dir
                })
                .on('error', error)
                .on('directory', redirect)
                .pipe(res);
            });

            that.running.on('connection', (socket) => {
                that.sockets.push(socket);
                socket.setTimeout(4000);
                socket.on('close', () => {
                    that.sockets.splice(that.sockets.indexOf(socket), 1);
                });
            });

            that.running.listen(port, (err) => {
                if (err) return d.reject(err);

                that.port = port;
                that.dir = dir;
                that.emit('state', true);
                d.resolve();
            });

            return d.promise;
        });
    }
}

/**
 * urlTransform is a helper function that allows a function to transform
 * a url string in it's parsed form and returns the new url as a string
 *
 * @param {String} uri
 * @param {Function} fn
 * @return {String}
 */
function urlTransform(uri, fn) {
    return url.format(fn(url.parse(uri)));
}

module.exports = Server;