1 2/* 3 * Copyright (C) NGINX, Inc. 4 */ 5 6'use strict'; 7 8const EventEmitter = require('events'); 9const http = require('http'); 10const util = require('util'); 11const unit_lib = require('unit-http/build/Release/unit-http.node'); 12const unit_socket = require('unit-http/socket'); 13 14const { Socket } = unit_socket; 15 16 17function ServerResponse(req) { 18 EventEmitter.call(this); 19 20 this.headers = {}; 21} 22util.inherits(ServerResponse, EventEmitter); 23 24ServerResponse.prototype.statusCode = 200; 25ServerResponse.prototype.statusMessage = undefined; 26ServerResponse.prototype.headers_len = 0; 27ServerResponse.prototype.headers_count = 0; 28ServerResponse.prototype.headersSent = false; 29ServerResponse.prototype.finished = false; 30 31ServerResponse.prototype._finish = function _finish() { 32 this.headers = {}; 33 this.headers_len = 0; 34 this.headers_count = 0; 35 this.finished = true; 36}; 37 38ServerResponse.prototype.assignSocket = function assignSocket(socket) { 39}; 40 41ServerResponse.prototype.detachSocket = function detachSocket(socket) { 42}; 43 44ServerResponse.prototype.writeContinue = function writeContinue(cb) { 45}; 46 47ServerResponse.prototype.writeProcessing = function writeProcessing(cb) { 48}; 49 50ServerResponse.prototype.setHeader = function setHeader(name, value) { 51 if (typeof name !== 'string') { 52 throw new TypeError('Name argument must be a string'); 53 } 54 55 let value_len = 0 56 let count = 0; 57 58 if (Array.isArray(value)) { 59 count = value.length; 60 61 value.forEach(function(val) { 62 if (typeof val !== 'string' && typeof val !== 'number') { 63 throw new TypeError('Array entries must be string or number'); 64 } 65 66 value_len += Buffer.byteLength(val + "", 'latin1'); 67 }); 68 69 } else { 70 if (typeof value !== 'string' && typeof value !== 'number') { 71 throw new TypeError('Value argument must be string, number, or array'); 72 } 73 74 count = 1; 75 value_len = Buffer.byteLength(value + "", 'latin1'); 76 } 77 78 let lc_name = name.toLowerCase(); 79 80 if (lc_name in this.headers) { 81 this._removeHeader(lc_name); 82 } 83 84 let name_len = Buffer.byteLength(name, 'latin1'); 85 86 this.headers[lc_name] = [name, value]; 87 this.headers_len += value_len + (name_len * count); 88 this.headers_count += count; 89}; 90 91ServerResponse.prototype.getHeader = function getHeader(name) { 92 const entry = this.headers[name.toLowerCase()]; 93 94 return entry && entry[1]; 95}; 96 97ServerResponse.prototype.getHeaderNames = function getHeaderNames() { 98 return Object.keys(this.headers); 99}; 100 101ServerResponse.prototype.getHeaders = function getHeaders() { 102 const ret = Object.create(null); 103 104 if (this.headers) { 105 const keys = Object.keys(this.headers); 106 107 for (var i = 0; i < keys.length; i++) { 108 const key = keys[i]; 109 110 ret[key] = this.headers[key][1]; 111 } 112 } 113 114 return ret; 115}; 116 117ServerResponse.prototype.hasHeader = function hasHeader(name) { 118 return name.toLowerCase() in this.headers; 119}; 120 121ServerResponse.prototype.removeHeader = function removeHeader(name) { 122 if (typeof name !== 'string') { 123 throw new TypeError('Name argument must be a string'); 124 } 125 126 let lc_name = name.toLowerCase(); 127 128 if (lc_name in this.headers) { 129 this._removeHeader(lc_name); 130 } 131}; 132 133ServerResponse.prototype._removeHeader = function _removeHeader(lc_name) { 134 let entry = this.headers[lc_name]; 135 let name_len = Buffer.byteLength(entry[0] + "", 'latin1'); 136 let value = entry[1]; 137 138 delete this.headers[lc_name]; 139 140 if (Array.isArray(value)) { 141 this.headers_count -= value.length; 142 this.headers_len -= value.length * name_len; 143 144 value.forEach(function(val) { 145 this.headers_len -= Buffer.byteLength(val + "", 'latin1'); 146 }); 147 148 return; 149 } 150 151 this.headers_count--; 152 this.headers_len -= name_len + Buffer.byteLength(value + "", 'latin1'); 153}; 154 155ServerResponse.prototype.sendDate = function sendDate() { 156 throw new Error("Not supported"); 157}; 158 159ServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) { 160 this.timeout = msecs; 161 162 if (callback) { 163 this.on('timeout', callback); 164 } 165 166 return this; 167}; 168 169// for Express 170ServerResponse.prototype._implicitHeader = function _implicitHeader() { 171 this.writeHead(this.statusCode); 172}; 173 174ServerResponse.prototype.writeHead = writeHead; 175ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead; 176 177function writeHead(statusCode, reason, obj) { 178 var originalStatusCode = statusCode; 179 180 statusCode |= 0; 181 182 if (statusCode < 100 || statusCode > 999) { 183 throw new ERR_HTTP_INVALID_STATUS_CODE(originalStatusCode); 184 } 185 186 if (typeof reason === 'string') { 187 this.statusMessage = reason; 188 189 } else { 190 if (!this.statusMessage) { 191 this.statusMessage = http.STATUS_CODES[statusCode] || 'unknown'; 192 } 193 194 obj = reason; 195 } 196 197 this.statusCode = statusCode; 198 199 if (obj) { 200 var k; 201 var keys = Object.keys(obj); 202 203 for (var i = 0; i < keys.length; i++) { 204 k = keys[i]; 205 206 if (k) { 207 this.setHeader(k, obj[k]); 208 } 209 } 210 } 211}; 212 213ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { 214 var contentLength = 0; 215 216 if (!this.headersSent) { 217 unit_lib.unit_response_headers(this, this.statusCode, this.headers, 218 this.headers_count, this.headers_len); 219 220 this.headersSent = true; 221 } 222 223 if (typeof chunk === 'function') { 224 callback = chunk; 225 chunk = null; 226 227 } else if (typeof encoding === 'function') { 228 callback = encoding; 229 encoding = null; 230 } 231 232 if (chunk) { 233 if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { 234 throw new TypeError('First argument must be a string or Buffer'); 235 } 236 237 if (typeof chunk === 'string') { 238 contentLength = Buffer.byteLength(chunk, encoding); 239 240 } else { 241 contentLength = chunk.length; 242 } 243 244 unit_lib.unit_response_write(this, chunk, contentLength); 245 } 246 247 if (typeof callback === 'function') { 248 /* 249 * The callback must be called only when response.write() caller 250 * completes. process.nextTick() postpones the callback execution. 251 * 252 * process.nextTick() is not technically part of the event loop. 253 * Instead, the nextTickQueue will be processed after the current 254 * operation completes, regardless of the current phase of 255 * the event loop. All callbacks passed to process.nextTick() 256 * will be resolved before the event loop continues. 257 */ 258 process.nextTick(function () { 259 callback(this); 260 }.bind(this)); 261 } 262}; 263 264ServerResponse.prototype.write = function write(chunk, encoding, callback) { 265 if (this.finished) { 266 throw new Error("Write after end"); 267 } 268 269 this._writeBody(chunk, encoding, callback); 270 271 return true; 272}; 273 274ServerResponse.prototype.end = function end(chunk, encoding, callback) { 275 if (!this.finished) { 276 this._writeBody(chunk, encoding, callback); 277 278 unit_lib.unit_response_end(this); 279 280 this.finished = true; 281 } 282 283 return this; 284}; 285 286function ServerRequest(server) { 287 EventEmitter.call(this); 288 289 this.server = server; 290} 291util.inherits(ServerRequest, EventEmitter); 292 293ServerRequest.prototype.unpipe = undefined; 294 295ServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) { 296 this.timeout = msecs; 297 298 if (callback) { 299 this.on('timeout', callback); 300 } 301 302 return this; 303}; 304 305ServerRequest.prototype.statusCode = function statusCode() { 306 /* Only valid for response obtained from http.ClientRequest. */ 307}; 308 309ServerRequest.prototype.statusMessage = function statusMessage() { 310 /* Only valid for response obtained from http.ClientRequest. */ 311}; 312 313ServerRequest.prototype.trailers = function trailers() { 314 throw new Error("Not supported"); 315}; 316 317ServerRequest.prototype.METHODS = function METHODS() { 318 return http.METHODS; 319}; 320 321ServerRequest.prototype.STATUS_CODES = function STATUS_CODES() { 322 return http.STATUS_CODES; 323}; 324 325ServerRequest.prototype.listeners = function listeners() { 326 return []; 327}; 328 329ServerRequest.prototype.resume = function resume() { 330 return []; 331}; 332 333/* 334 * The "on" method is overridden to defer reading data until user code is 335 * ready, that is (ev === "data"). This can occur after req.emit("end") is 336 * executed, since the user code can be scheduled asynchronously by Promises 337 * and so on. Passing the data is postponed by process.nextTick() until 338 * the "on" method caller completes. 339 */ 340ServerRequest.prototype.on = function on(ev, fn) { 341 Server.prototype.on.call(this, ev, fn); 342 343 if (ev === "data") { 344 process.nextTick(function () { 345 if (this.server.buffer.length !== 0) { 346 this.emit("data", this.server.buffer); 347 } 348 349 }.bind(this)); 350 } 351}; 352 353ServerRequest.prototype.addListener = ServerRequest.prototype.on; 354 355function Server(requestListener) { 356 EventEmitter.call(this); 357 358 this.unit = new unit_lib.Unit(); 359 this.unit.server = this; 360 361 this.unit.createServer(); 362 363 this.socket = Socket; 364 this.request = ServerRequest; 365 this.response = ServerResponse; 366 367 if (requestListener) { 368 this.on('request', requestListener); 369 } 370} 371util.inherits(Server, EventEmitter); 372 373Server.prototype.setTimeout = function setTimeout(msecs, callback) { 374 this.timeout = msecs; 375 376 if (callback) { 377 this.on('timeout', callback); 378 } 379 380 return this; 381}; 382 383Server.prototype.listen = function () { 384 this.unit.listen(); 385}; 386 387Server.prototype.emit_events = function (server, req, res) { 388 req.server = server; 389 res.server = server; 390 req.res = res; 391 res.req = req; 392 393 server.buffer = server.unit._read(req.socket.req_pointer); 394 395 server.emit("request", req, res); 396 397 process.nextTick(() => { 398 req.emit("finish"); 399 req.emit("end"); 400 }); 401}; 402 403function connectionListener(socket) { 404} 405 406module.exports = { 407 STATUS_CODES: http.STATUS_CODES, 408 Server, 409 ServerResponse, 410 ServerRequest, 411 _connectionListener: connectionListener 412}; 413