xref: /unit/src/nodejs/unit-http/http_server.js (revision 2649:7621e8f40ef6)
1802Salexander.borisov@nginx.com
2802Salexander.borisov@nginx.com/*
3802Salexander.borisov@nginx.com * Copyright (C) NGINX, Inc.
4802Salexander.borisov@nginx.com */
5802Salexander.borisov@nginx.com
6802Salexander.borisov@nginx.com'use strict';
7802Salexander.borisov@nginx.com
8802Salexander.borisov@nginx.comconst EventEmitter = require('events');
9802Salexander.borisov@nginx.comconst http = require('http');
10802Salexander.borisov@nginx.comconst util = require('util');
111132Smax.romanov@nginx.comconst unit_lib = require('./build/Release/unit-http');
121132Smax.romanov@nginx.comconst Socket = require('./socket');
131132Smax.romanov@nginx.comconst WebSocketFrame = require('./websocket_frame');
141766Smax.romanov@nginx.comconst Readable = require('stream').Readable;
15802Salexander.borisov@nginx.com
16802Salexander.borisov@nginx.com
17802Salexander.borisov@nginx.comfunction ServerResponse(req) {
18802Salexander.borisov@nginx.com    EventEmitter.call(this);
19802Salexander.borisov@nginx.com
20802Salexander.borisov@nginx.com    this.headers = {};
211132Smax.romanov@nginx.com
221132Smax.romanov@nginx.com    this.server = req.server;
231132Smax.romanov@nginx.com    this._request = req;
241132Smax.romanov@nginx.com    req._response = this;
251132Smax.romanov@nginx.com    this.socket = req.socket;
261132Smax.romanov@nginx.com    this.connection = req.connection;
271766Smax.romanov@nginx.com    this.writable = true;
28802Salexander.borisov@nginx.com}
29802Salexander.borisov@nginx.comutil.inherits(ServerResponse, EventEmitter);
30802Salexander.borisov@nginx.com
31802Salexander.borisov@nginx.comServerResponse.prototype.statusCode = 200;
32802Salexander.borisov@nginx.comServerResponse.prototype.statusMessage = undefined;
33802Salexander.borisov@nginx.comServerResponse.prototype.headers_len = 0;
34802Salexander.borisov@nginx.comServerResponse.prototype.headers_count = 0;
35802Salexander.borisov@nginx.comServerResponse.prototype.headersSent = false;
362560Szelenkov@nginx.comServerResponse.prototype.destroyed = false;
37802Salexander.borisov@nginx.comServerResponse.prototype.finished = false;
38802Salexander.borisov@nginx.com
392560Szelenkov@nginx.comServerResponse.prototype.destroy = function destroy(error) {
402560Szelenkov@nginx.com    if (!this.destroyed) {
412560Szelenkov@nginx.com        this.destroyed = true;
422560Szelenkov@nginx.com    }
432560Szelenkov@nginx.com
442560Szelenkov@nginx.com    return this;
452560Szelenkov@nginx.com};
462560Szelenkov@nginx.com
47802Salexander.borisov@nginx.comServerResponse.prototype._finish = function _finish() {
48802Salexander.borisov@nginx.com    this.headers = {};
49802Salexander.borisov@nginx.com    this.headers_len = 0;
50802Salexander.borisov@nginx.com    this.headers_count = 0;
51802Salexander.borisov@nginx.com    this.finished = true;
52802Salexander.borisov@nginx.com};
53802Salexander.borisov@nginx.com
54802Salexander.borisov@nginx.comServerResponse.prototype.assignSocket = function assignSocket(socket) {
55802Salexander.borisov@nginx.com};
56802Salexander.borisov@nginx.com
57802Salexander.borisov@nginx.comServerResponse.prototype.detachSocket = function detachSocket(socket) {
58802Salexander.borisov@nginx.com};
59802Salexander.borisov@nginx.com
60802Salexander.borisov@nginx.comServerResponse.prototype.writeContinue = function writeContinue(cb) {
61802Salexander.borisov@nginx.com};
62802Salexander.borisov@nginx.com
63802Salexander.borisov@nginx.comServerResponse.prototype.writeProcessing = function writeProcessing(cb) {
64802Salexander.borisov@nginx.com};
65802Salexander.borisov@nginx.com
66875Salexander.borisov@nginx.comServerResponse.prototype.setHeader = function setHeader(name, value) {
67875Salexander.borisov@nginx.com    if (typeof name !== 'string') {
68875Salexander.borisov@nginx.com        throw new TypeError('Name argument must be a string');
69802Salexander.borisov@nginx.com    }
70802Salexander.borisov@nginx.com
71875Salexander.borisov@nginx.com    let value_len = 0
72875Salexander.borisov@nginx.com    let count = 0;
73802Salexander.borisov@nginx.com
74802Salexander.borisov@nginx.com    if (Array.isArray(value)) {
75875Salexander.borisov@nginx.com        count = value.length;
76802Salexander.borisov@nginx.com
77802Salexander.borisov@nginx.com        value.forEach(function(val) {
78875Salexander.borisov@nginx.com            value_len += Buffer.byteLength(val + "", 'latin1');
79802Salexander.borisov@nginx.com        });
80802Salexander.borisov@nginx.com
81802Salexander.borisov@nginx.com    } else {
82875Salexander.borisov@nginx.com        count = 1;
83875Salexander.borisov@nginx.com        value_len = Buffer.byteLength(value + "", 'latin1');
84802Salexander.borisov@nginx.com    }
85802Salexander.borisov@nginx.com
86875Salexander.borisov@nginx.com    let lc_name = name.toLowerCase();
87875Salexander.borisov@nginx.com
88875Salexander.borisov@nginx.com    if (lc_name in this.headers) {
89875Salexander.borisov@nginx.com        this._removeHeader(lc_name);
90875Salexander.borisov@nginx.com    }
91802Salexander.borisov@nginx.com
92875Salexander.borisov@nginx.com    let name_len = Buffer.byteLength(name, 'latin1');
93875Salexander.borisov@nginx.com
94875Salexander.borisov@nginx.com    this.headers[lc_name] = [name, value];
95875Salexander.borisov@nginx.com    this.headers_len += value_len + (name_len * count);
96875Salexander.borisov@nginx.com    this.headers_count += count;
97802Salexander.borisov@nginx.com};
98802Salexander.borisov@nginx.com
99802Salexander.borisov@nginx.comServerResponse.prototype.getHeader = function getHeader(name) {
100875Salexander.borisov@nginx.com    const entry = this.headers[name.toLowerCase()];
101875Salexander.borisov@nginx.com
102875Salexander.borisov@nginx.com    return entry && entry[1];
103802Salexander.borisov@nginx.com};
104802Salexander.borisov@nginx.com
105802Salexander.borisov@nginx.comServerResponse.prototype.getHeaderNames = function getHeaderNames() {
106802Salexander.borisov@nginx.com    return Object.keys(this.headers);
107802Salexander.borisov@nginx.com};
108802Salexander.borisov@nginx.com
109802Salexander.borisov@nginx.comServerResponse.prototype.getHeaders = function getHeaders() {
110875Salexander.borisov@nginx.com    const ret = Object.create(null);
111875Salexander.borisov@nginx.com
112875Salexander.borisov@nginx.com    if (this.headers) {
113875Salexander.borisov@nginx.com        const keys = Object.keys(this.headers);
114875Salexander.borisov@nginx.com
115875Salexander.borisov@nginx.com        for (var i = 0; i < keys.length; i++) {
116875Salexander.borisov@nginx.com            const key = keys[i];
117875Salexander.borisov@nginx.com
118875Salexander.borisov@nginx.com            ret[key] = this.headers[key][1];
119875Salexander.borisov@nginx.com        }
120875Salexander.borisov@nginx.com    }
121875Salexander.borisov@nginx.com
122875Salexander.borisov@nginx.com    return ret;
123802Salexander.borisov@nginx.com};
124802Salexander.borisov@nginx.com
125802Salexander.borisov@nginx.comServerResponse.prototype.hasHeader = function hasHeader(name) {
126875Salexander.borisov@nginx.com    return name.toLowerCase() in this.headers;
127802Salexander.borisov@nginx.com};
128802Salexander.borisov@nginx.com
129802Salexander.borisov@nginx.comServerResponse.prototype.removeHeader = function removeHeader(name) {
130875Salexander.borisov@nginx.com    if (typeof name !== 'string') {
131875Salexander.borisov@nginx.com        throw new TypeError('Name argument must be a string');
132802Salexander.borisov@nginx.com    }
133802Salexander.borisov@nginx.com
134875Salexander.borisov@nginx.com    let lc_name = name.toLowerCase();
135875Salexander.borisov@nginx.com
136875Salexander.borisov@nginx.com    if (lc_name in this.headers) {
137875Salexander.borisov@nginx.com        this._removeHeader(lc_name);
138875Salexander.borisov@nginx.com    }
139875Salexander.borisov@nginx.com};
140802Salexander.borisov@nginx.com
1412598Szelenkov@nginx.comServerResponse.prototype.flushHeaders = function flushHeaders() {
1422598Szelenkov@nginx.com    this._sendHeaders();
1432598Szelenkov@nginx.com};
1442598Szelenkov@nginx.com
145875Salexander.borisov@nginx.comServerResponse.prototype._removeHeader = function _removeHeader(lc_name) {
146875Salexander.borisov@nginx.com    let entry = this.headers[lc_name];
147875Salexander.borisov@nginx.com    let name_len = Buffer.byteLength(entry[0] + "", 'latin1');
148875Salexander.borisov@nginx.com    let value = entry[1];
149875Salexander.borisov@nginx.com
150875Salexander.borisov@nginx.com    delete this.headers[lc_name];
151802Salexander.borisov@nginx.com
152873Salexander.borisov@nginx.com    if (Array.isArray(value)) {
153873Salexander.borisov@nginx.com        this.headers_count -= value.length;
154873Salexander.borisov@nginx.com        this.headers_len -= value.length * name_len;
155873Salexander.borisov@nginx.com
156873Salexander.borisov@nginx.com        value.forEach(function(val) {
157802Salexander.borisov@nginx.com            this.headers_len -= Buffer.byteLength(val + "", 'latin1');
158802Salexander.borisov@nginx.com        });
159802Salexander.borisov@nginx.com
160873Salexander.borisov@nginx.com        return;
161802Salexander.borisov@nginx.com    }
162802Salexander.borisov@nginx.com
163873Salexander.borisov@nginx.com    this.headers_count--;
164873Salexander.borisov@nginx.com    this.headers_len -= name_len + Buffer.byteLength(value + "", 'latin1');
165802Salexander.borisov@nginx.com};
166802Salexander.borisov@nginx.com
167802Salexander.borisov@nginx.comServerResponse.prototype.sendDate = function sendDate() {
168802Salexander.borisov@nginx.com    throw new Error("Not supported");
169802Salexander.borisov@nginx.com};
170802Salexander.borisov@nginx.com
171802Salexander.borisov@nginx.comServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) {
172802Salexander.borisov@nginx.com    this.timeout = msecs;
173802Salexander.borisov@nginx.com
174802Salexander.borisov@nginx.com    if (callback) {
175802Salexander.borisov@nginx.com        this.on('timeout', callback);
176802Salexander.borisov@nginx.com    }
177802Salexander.borisov@nginx.com
178802Salexander.borisov@nginx.com    return this;
179802Salexander.borisov@nginx.com};
180802Salexander.borisov@nginx.com
181802Salexander.borisov@nginx.comServerResponse.prototype.writeHead = writeHead;
182802Salexander.borisov@nginx.comServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead;
183802Salexander.borisov@nginx.com
184802Salexander.borisov@nginx.comfunction writeHead(statusCode, reason, obj) {
185802Salexander.borisov@nginx.com    var originalStatusCode = statusCode;
186802Salexander.borisov@nginx.com
187802Salexander.borisov@nginx.com    statusCode |= 0;
188802Salexander.borisov@nginx.com
189802Salexander.borisov@nginx.com    if (statusCode < 100 || statusCode > 999) {
190802Salexander.borisov@nginx.com        throw new ERR_HTTP_INVALID_STATUS_CODE(originalStatusCode);
191802Salexander.borisov@nginx.com    }
192802Salexander.borisov@nginx.com
193802Salexander.borisov@nginx.com    if (typeof reason === 'string') {
194802Salexander.borisov@nginx.com        this.statusMessage = reason;
195802Salexander.borisov@nginx.com
196802Salexander.borisov@nginx.com    } else {
197802Salexander.borisov@nginx.com        if (!this.statusMessage) {
198802Salexander.borisov@nginx.com            this.statusMessage = http.STATUS_CODES[statusCode] || 'unknown';
199802Salexander.borisov@nginx.com        }
200802Salexander.borisov@nginx.com
201802Salexander.borisov@nginx.com        obj = reason;
202802Salexander.borisov@nginx.com    }
203802Salexander.borisov@nginx.com
204802Salexander.borisov@nginx.com    this.statusCode = statusCode;
205802Salexander.borisov@nginx.com
206802Salexander.borisov@nginx.com    if (obj) {
207802Salexander.borisov@nginx.com        var k;
208802Salexander.borisov@nginx.com        var keys = Object.keys(obj);
209802Salexander.borisov@nginx.com
210802Salexander.borisov@nginx.com        for (var i = 0; i < keys.length; i++) {
211802Salexander.borisov@nginx.com            k = keys[i];
212802Salexander.borisov@nginx.com
213802Salexander.borisov@nginx.com            if (k) {
214802Salexander.borisov@nginx.com                this.setHeader(k, obj[k]);
215802Salexander.borisov@nginx.com            }
216802Salexander.borisov@nginx.com        }
217802Salexander.borisov@nginx.com    }
2181114Svbart@nginx.com
2191114Svbart@nginx.com    return this;
220802Salexander.borisov@nginx.com};
221802Salexander.borisov@nginx.com
2221024Svbart@nginx.com/*
2231024Svbart@nginx.com * Some Node.js packages are known to be using this undocumented function,
2241024Svbart@nginx.com * notably "compression" middleware.
2251024Svbart@nginx.com */
2261024Svbart@nginx.comServerResponse.prototype._implicitHeader = function _implicitHeader() {
2271024Svbart@nginx.com    this.writeHead(this.statusCode);
2281024Svbart@nginx.com};
2291024Svbart@nginx.com
2301132Smax.romanov@nginx.comServerResponse.prototype._send_headers = unit_lib.response_send_headers;
2311132Smax.romanov@nginx.com
2321132Smax.romanov@nginx.comServerResponse.prototype._sendHeaders = function _sendHeaders() {
2331132Smax.romanov@nginx.com    if (!this.headersSent) {
2341132Smax.romanov@nginx.com        this._send_headers(this.statusCode, this.headers, this.headers_count,
2351132Smax.romanov@nginx.com                           this.headers_len);
2361132Smax.romanov@nginx.com
2371132Smax.romanov@nginx.com        this.headersSent = true;
2381132Smax.romanov@nginx.com    }
2391132Smax.romanov@nginx.com};
2401132Smax.romanov@nginx.com
2411132Smax.romanov@nginx.comServerResponse.prototype._write = unit_lib.response_write;
2421132Smax.romanov@nginx.com
243802Salexander.borisov@nginx.comServerResponse.prototype._writeBody = function(chunk, encoding, callback) {
244802Salexander.borisov@nginx.com    var contentLength = 0;
2451322Smax.romanov@nginx.com    var res, o;
246802Salexander.borisov@nginx.com
2471132Smax.romanov@nginx.com    this._sendHeaders();
248802Salexander.borisov@nginx.com
249802Salexander.borisov@nginx.com    if (typeof chunk === 'function') {
250802Salexander.borisov@nginx.com        callback = chunk;
251802Salexander.borisov@nginx.com        chunk = null;
252802Salexander.borisov@nginx.com
253802Salexander.borisov@nginx.com    } else if (typeof encoding === 'function') {
254802Salexander.borisov@nginx.com        callback = encoding;
255802Salexander.borisov@nginx.com        encoding = null;
256802Salexander.borisov@nginx.com    }
257802Salexander.borisov@nginx.com
258802Salexander.borisov@nginx.com    if (chunk) {
2592559Szelenkov@nginx.com        if (typeof chunk !== 'string' && !(chunk instanceof Buffer ||
2602559Szelenkov@nginx.com                chunk instanceof Uint8Array)) {
2612559Szelenkov@nginx.com            throw new TypeError(
2622559Szelenkov@nginx.com                'First argument must be a string, Buffer, ' +
2632559Szelenkov@nginx.com                'or Uint8Array');
264802Salexander.borisov@nginx.com        }
265802Salexander.borisov@nginx.com
266802Salexander.borisov@nginx.com        if (typeof chunk === 'string') {
267802Salexander.borisov@nginx.com            contentLength = Buffer.byteLength(chunk, encoding);
268802Salexander.borisov@nginx.com
2691322Smax.romanov@nginx.com            if (contentLength > unit_lib.buf_min) {
2701322Smax.romanov@nginx.com                chunk = Buffer.from(chunk, encoding);
2711322Smax.romanov@nginx.com
2721322Smax.romanov@nginx.com                contentLength = chunk.length;
2731322Smax.romanov@nginx.com            }
2741322Smax.romanov@nginx.com
275802Salexander.borisov@nginx.com        } else {
276802Salexander.borisov@nginx.com            contentLength = chunk.length;
277802Salexander.borisov@nginx.com        }
278802Salexander.borisov@nginx.com
2791322Smax.romanov@nginx.com        if (this.server._output.length > 0 || !this.socket.writable) {
2801322Smax.romanov@nginx.com            o = new BufferedOutput(this, 0, chunk, encoding, callback);
2811322Smax.romanov@nginx.com            this.server._output.push(o);
2821322Smax.romanov@nginx.com
2831322Smax.romanov@nginx.com            return false;
2841322Smax.romanov@nginx.com        }
2851322Smax.romanov@nginx.com
2861322Smax.romanov@nginx.com        res = this._write(chunk, 0, contentLength);
2871322Smax.romanov@nginx.com        if (res < contentLength) {
2881322Smax.romanov@nginx.com            this.socket.writable = false;
2891766Smax.romanov@nginx.com            this.writable = false;
2901322Smax.romanov@nginx.com
2911322Smax.romanov@nginx.com            o = new BufferedOutput(this, res, chunk, encoding, callback);
2921322Smax.romanov@nginx.com            this.server._output.push(o);
2931322Smax.romanov@nginx.com
2941322Smax.romanov@nginx.com            return false;
2951322Smax.romanov@nginx.com        }
296802Salexander.borisov@nginx.com    }
297802Salexander.borisov@nginx.com
298802Salexander.borisov@nginx.com    if (typeof callback === 'function') {
299874Salexander.borisov@nginx.com        /*
300874Salexander.borisov@nginx.com         * The callback must be called only when response.write() caller
301874Salexander.borisov@nginx.com         * completes.  process.nextTick() postpones the callback execution.
302874Salexander.borisov@nginx.com         *
303874Salexander.borisov@nginx.com         * process.nextTick() is not technically part of the event loop.
304874Salexander.borisov@nginx.com         * Instead, the nextTickQueue will be processed after the current
305874Salexander.borisov@nginx.com         * operation completes, regardless of the current phase of
306874Salexander.borisov@nginx.com         * the event loop.  All callbacks passed to process.nextTick()
307874Salexander.borisov@nginx.com         * will be resolved before the event loop continues.
308874Salexander.borisov@nginx.com         */
3091322Smax.romanov@nginx.com        process.nextTick(callback);
310802Salexander.borisov@nginx.com    }
3111322Smax.romanov@nginx.com
3121322Smax.romanov@nginx.com    return true;
313802Salexander.borisov@nginx.com};
314802Salexander.borisov@nginx.com
315802Salexander.borisov@nginx.comServerResponse.prototype.write = function write(chunk, encoding, callback) {
316869Salexander.borisov@nginx.com    if (this.finished) {
3171322Smax.romanov@nginx.com        if (typeof encoding === 'function') {
3181322Smax.romanov@nginx.com            callback = encoding;
3191322Smax.romanov@nginx.com            encoding = null;
3201322Smax.romanov@nginx.com        }
3211322Smax.romanov@nginx.com
3221322Smax.romanov@nginx.com        var err = new Error("Write after end");
3231322Smax.romanov@nginx.com        process.nextTick(() => {
3241322Smax.romanov@nginx.com            this.emit('error', err);
3251322Smax.romanov@nginx.com
3261322Smax.romanov@nginx.com            if (typeof callback === 'function') {
3271322Smax.romanov@nginx.com                callback(err);
3281322Smax.romanov@nginx.com            }
3291322Smax.romanov@nginx.com        })
330869Salexander.borisov@nginx.com    }
331869Salexander.borisov@nginx.com
3321322Smax.romanov@nginx.com    return this._writeBody(chunk, encoding, callback);
333802Salexander.borisov@nginx.com};
334802Salexander.borisov@nginx.com
3351132Smax.romanov@nginx.comServerResponse.prototype._end = unit_lib.response_end;
3361132Smax.romanov@nginx.com
337802Salexander.borisov@nginx.comServerResponse.prototype.end = function end(chunk, encoding, callback) {
338869Salexander.borisov@nginx.com    if (!this.finished) {
3391322Smax.romanov@nginx.com        if (typeof encoding === 'function') {
3401322Smax.romanov@nginx.com            callback = encoding;
3411322Smax.romanov@nginx.com            encoding = null;
3421322Smax.romanov@nginx.com        }
343802Salexander.borisov@nginx.com
3441322Smax.romanov@nginx.com        this._writeBody(chunk, encoding, () => {
3451322Smax.romanov@nginx.com            this._end();
3461322Smax.romanov@nginx.com
3471322Smax.romanov@nginx.com            if (typeof callback === 'function') {
3481322Smax.romanov@nginx.com                callback();
3491322Smax.romanov@nginx.com            }
3501766Smax.romanov@nginx.com
3511766Smax.romanov@nginx.com            this.emit("finish");
3521322Smax.romanov@nginx.com        });
353869Salexander.borisov@nginx.com
354869Salexander.borisov@nginx.com        this.finished = true;
355869Salexander.borisov@nginx.com    }
356802Salexander.borisov@nginx.com
357802Salexander.borisov@nginx.com    return this;
358802Salexander.borisov@nginx.com};
359802Salexander.borisov@nginx.com
3601132Smax.romanov@nginx.comfunction ServerRequest(server, socket) {
3611766Smax.romanov@nginx.com    Readable.call(this);
362802Salexander.borisov@nginx.com
363802Salexander.borisov@nginx.com    this.server = server;
3641132Smax.romanov@nginx.com    this.socket = socket;
3651132Smax.romanov@nginx.com    this.connection = socket;
3661766Smax.romanov@nginx.com    this._pushed_eofchunk = false;
367802Salexander.borisov@nginx.com}
3681766Smax.romanov@nginx.comutil.inherits(ServerRequest, Readable);
369802Salexander.borisov@nginx.com
370802Salexander.borisov@nginx.comServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) {
371802Salexander.borisov@nginx.com    this.timeout = msecs;
372802Salexander.borisov@nginx.com
373802Salexander.borisov@nginx.com    if (callback) {
374802Salexander.borisov@nginx.com        this.on('timeout', callback);
375802Salexander.borisov@nginx.com    }
376802Salexander.borisov@nginx.com
377802Salexander.borisov@nginx.com    return this;
378802Salexander.borisov@nginx.com};
379802Salexander.borisov@nginx.com
380802Salexander.borisov@nginx.comServerRequest.prototype.statusCode = function statusCode() {
381802Salexander.borisov@nginx.com    /* Only valid for response obtained from http.ClientRequest. */
382802Salexander.borisov@nginx.com};
383802Salexander.borisov@nginx.com
384802Salexander.borisov@nginx.comServerRequest.prototype.statusMessage = function statusMessage() {
385802Salexander.borisov@nginx.com    /* Only valid for response obtained from http.ClientRequest. */
386802Salexander.borisov@nginx.com};
387802Salexander.borisov@nginx.com
388802Salexander.borisov@nginx.comServerRequest.prototype.trailers = function trailers() {
389802Salexander.borisov@nginx.com    throw new Error("Not supported");
390802Salexander.borisov@nginx.com};
391802Salexander.borisov@nginx.com
392802Salexander.borisov@nginx.comServerRequest.prototype.METHODS = function METHODS() {
393802Salexander.borisov@nginx.com    return http.METHODS;
394802Salexander.borisov@nginx.com};
395802Salexander.borisov@nginx.com
396802Salexander.borisov@nginx.comServerRequest.prototype.STATUS_CODES = function STATUS_CODES() {
397802Salexander.borisov@nginx.com    return http.STATUS_CODES;
398802Salexander.borisov@nginx.com};
399802Salexander.borisov@nginx.com
4001766Smax.romanov@nginx.comServerRequest.prototype._request_read = unit_lib.request_read;
401802Salexander.borisov@nginx.com
4021766Smax.romanov@nginx.comServerRequest.prototype._read = function _read(n) {
4031766Smax.romanov@nginx.com    const b = this._request_read(n);
404802Salexander.borisov@nginx.com
4051766Smax.romanov@nginx.com    if (b != null) {
4061766Smax.romanov@nginx.com        this.push(b);
4071766Smax.romanov@nginx.com    }
408870Salexander.borisov@nginx.com
4091766Smax.romanov@nginx.com    if (!this._pushed_eofchunk && (b == null || b.length < n)) {
4101766Smax.romanov@nginx.com        this._pushed_eofchunk = true;
4111766Smax.romanov@nginx.com        this.push(null);
412870Salexander.borisov@nginx.com    }
413870Salexander.borisov@nginx.com};
414870Salexander.borisov@nginx.com
415870Salexander.borisov@nginx.com
4162642Sg.javorszky@f5.comfunction Server(options, requestListener) {
4172642Sg.javorszky@f5.com    if (typeof options === 'function') {
4182642Sg.javorszky@f5.com        requestListener = options;
4192642Sg.javorszky@f5.com        options = {};
4202642Sg.javorszky@f5.com    } else {
421*2649Sd.callahan@f5.com        console.warn("http.Server constructor was called with unsupported options, using default settings");
4222642Sg.javorszky@f5.com    }
4232642Sg.javorszky@f5.com
424802Salexander.borisov@nginx.com    EventEmitter.call(this);
425802Salexander.borisov@nginx.com
426802Salexander.borisov@nginx.com    this.unit = new unit_lib.Unit();
427828Salexander.borisov@nginx.com    this.unit.server = this;
428802Salexander.borisov@nginx.com
429828Salexander.borisov@nginx.com    this.unit.createServer();
430802Salexander.borisov@nginx.com
4311132Smax.romanov@nginx.com    this.Socket = Socket;
4321132Smax.romanov@nginx.com    this.ServerRequest = ServerRequest;
4331132Smax.romanov@nginx.com    this.ServerResponse = ServerResponse;
4341132Smax.romanov@nginx.com    this.WebSocketFrame = WebSocketFrame;
435802Salexander.borisov@nginx.com
436802Salexander.borisov@nginx.com    if (requestListener) {
437802Salexander.borisov@nginx.com        this.on('request', requestListener);
438802Salexander.borisov@nginx.com    }
4391132Smax.romanov@nginx.com
4401132Smax.romanov@nginx.com    this._upgradeListenerCount = 0;
4411132Smax.romanov@nginx.com    this.on('newListener', function(ev) {
4421132Smax.romanov@nginx.com        if (ev === 'upgrade'){
4431132Smax.romanov@nginx.com            this._upgradeListenerCount++;
4441132Smax.romanov@nginx.com        }
4451132Smax.romanov@nginx.com      }).on('removeListener', function(ev) {
4461132Smax.romanov@nginx.com        if (ev === 'upgrade') {
4471132Smax.romanov@nginx.com            this._upgradeListenerCount--;
4481132Smax.romanov@nginx.com        }
4491132Smax.romanov@nginx.com    });
4501322Smax.romanov@nginx.com
4511322Smax.romanov@nginx.com    this._output = [];
4521322Smax.romanov@nginx.com    this._drain_resp = new Set();
453802Salexander.borisov@nginx.com}
4541132Smax.romanov@nginx.com
455802Salexander.borisov@nginx.comutil.inherits(Server, EventEmitter);
456802Salexander.borisov@nginx.com
457802Salexander.borisov@nginx.comServer.prototype.setTimeout = function setTimeout(msecs, callback) {
458802Salexander.borisov@nginx.com    this.timeout = msecs;
459802Salexander.borisov@nginx.com
460802Salexander.borisov@nginx.com    if (callback) {
461802Salexander.borisov@nginx.com        this.on('timeout', callback);
462802Salexander.borisov@nginx.com    }
463802Salexander.borisov@nginx.com
464802Salexander.borisov@nginx.com    return this;
465802Salexander.borisov@nginx.com};
466802Salexander.borisov@nginx.com
4671447Smax.romanov@nginx.comServer.prototype.listen = function (...args) {
468802Salexander.borisov@nginx.com    this.unit.listen();
4691447Smax.romanov@nginx.com
4701865So.canty@f5.com    if (typeof args[args.length - 1] === 'function') {
4711865So.canty@f5.com        this.once('listening', args[args.length - 1]);
4721447Smax.romanov@nginx.com    }
4731447Smax.romanov@nginx.com
4741865So.canty@f5.com    /*
4751865So.canty@f5.com     * Some express.js apps use the returned server object inside the listening
4761865So.canty@f5.com     * callback, so we timeout the listening event to occur after this function
4771865So.canty@f5.com     * returns.
4781865So.canty@f5.com     */
4791865So.canty@f5.com    setImmediate(function() {
4801865So.canty@f5.com        this.emit('listening')
4811865So.canty@f5.com    }.bind(this))
4821447Smax.romanov@nginx.com
4831447Smax.romanov@nginx.com    return this;
484802Salexander.borisov@nginx.com};
485802Salexander.borisov@nginx.com
4861865So.canty@f5.comServer.prototype.address = function () {
4871865So.canty@f5.com    return  {
4881865So.canty@f5.com        family: "IPv4",
4891865So.canty@f5.com        address: "127.0.0.1",
4901865So.canty@f5.com        port: 80
4911865So.canty@f5.com    }
4921865So.canty@f5.com}
4931865So.canty@f5.com
4941132Smax.romanov@nginx.comServer.prototype.emit_request = function (req, res) {
4951132Smax.romanov@nginx.com    if (req._websocket_handshake && this._upgradeListenerCount > 0) {
4961132Smax.romanov@nginx.com        this.emit('upgrade', req, req.socket);
497870Salexander.borisov@nginx.com
4981132Smax.romanov@nginx.com    } else {
4991132Smax.romanov@nginx.com        this.emit("request", req, res);
5001132Smax.romanov@nginx.com    }
501828Salexander.borisov@nginx.com};
502828Salexander.borisov@nginx.com
5031021Smax.romanov@nginx.comServer.prototype.emit_close = function () {
5041021Smax.romanov@nginx.com    this.emit('close');
5051021Smax.romanov@nginx.com};
5061021Smax.romanov@nginx.com
5071322Smax.romanov@nginx.comServer.prototype.emit_drain = function () {
5081322Smax.romanov@nginx.com    var res, o, l;
5091322Smax.romanov@nginx.com
5101322Smax.romanov@nginx.com    if (this._output.length <= 0) {
5111322Smax.romanov@nginx.com        return;
5121322Smax.romanov@nginx.com    }
5131322Smax.romanov@nginx.com
5141322Smax.romanov@nginx.com    while (this._output.length > 0) {
5151322Smax.romanov@nginx.com        o = this._output[0];
5161322Smax.romanov@nginx.com
5171322Smax.romanov@nginx.com        if (typeof o.chunk === 'string') {
5181322Smax.romanov@nginx.com            l = Buffer.byteLength(o.chunk, o.encoding);
5191322Smax.romanov@nginx.com
5201322Smax.romanov@nginx.com        } else {
5211322Smax.romanov@nginx.com            l = o.chunk.length;
5221322Smax.romanov@nginx.com        }
5231322Smax.romanov@nginx.com
5241322Smax.romanov@nginx.com        res = o.resp._write(o.chunk, o.offset, l);
5251322Smax.romanov@nginx.com
5261322Smax.romanov@nginx.com        o.offset += res;
5271322Smax.romanov@nginx.com        if (o.offset < l) {
5281322Smax.romanov@nginx.com            return;
5291322Smax.romanov@nginx.com        }
5301322Smax.romanov@nginx.com
5311322Smax.romanov@nginx.com        this._drain_resp.add(o.resp);
5321322Smax.romanov@nginx.com
5331322Smax.romanov@nginx.com        if (typeof o.callback === 'function') {
5341322Smax.romanov@nginx.com            process.nextTick(o.callback);
5351322Smax.romanov@nginx.com        }
5361322Smax.romanov@nginx.com
5371322Smax.romanov@nginx.com        this._output.shift();
5381322Smax.romanov@nginx.com    }
5391322Smax.romanov@nginx.com
5401322Smax.romanov@nginx.com    for (var resp of this._drain_resp) {
5411322Smax.romanov@nginx.com
5421322Smax.romanov@nginx.com        if (resp.socket.writable) {
5431322Smax.romanov@nginx.com            continue;
5441322Smax.romanov@nginx.com        }
5451322Smax.romanov@nginx.com
5461322Smax.romanov@nginx.com        resp.socket.writable = true;
5471766Smax.romanov@nginx.com        resp.writable = true;
5481322Smax.romanov@nginx.com
5491322Smax.romanov@nginx.com        process.nextTick(() => {
5501322Smax.romanov@nginx.com            resp.emit("drain");
5511322Smax.romanov@nginx.com        });
5521322Smax.romanov@nginx.com    }
5531322Smax.romanov@nginx.com
5541322Smax.romanov@nginx.com    this._drain_resp.clear();
5551322Smax.romanov@nginx.com};
5561322Smax.romanov@nginx.com
5571322Smax.romanov@nginx.comfunction BufferedOutput(resp, offset, chunk, encoding, callback) {
5581322Smax.romanov@nginx.com    this.resp = resp;
5591322Smax.romanov@nginx.com    this.offset = offset;
5601322Smax.romanov@nginx.com    this.chunk = chunk;
5611322Smax.romanov@nginx.com    this.encoding = encoding;
5621322Smax.romanov@nginx.com    this.callback = callback;
5631322Smax.romanov@nginx.com}
5641322Smax.romanov@nginx.com
565802Salexander.borisov@nginx.comfunction connectionListener(socket) {
566802Salexander.borisov@nginx.com}
567802Salexander.borisov@nginx.com
568802Salexander.borisov@nginx.commodule.exports = {
569802Salexander.borisov@nginx.com    Server,
570802Salexander.borisov@nginx.com    ServerResponse,
571802Salexander.borisov@nginx.com    ServerRequest,
572802Salexander.borisov@nginx.com    _connectionListener: connectionListener
573802Salexander.borisov@nginx.com};
574