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(key, value) { 51 if (typeof key !== 'string') { 52 throw new TypeError('Key argument must be a string'); 53 } 54 55 let header_key_len = Buffer.byteLength(key, 'latin1'); 56 let header_len = 0 57 let header_count = 0; 58 59 if (Array.isArray(value)) { 60 header_count = value.length; 61 62 value.forEach(function(val) { 63 if (typeof val !== 'string' && typeof val !== 'number') { 64 throw new TypeError('Array entries must be string or number'); 65 } 66 67 header_len += Buffer.byteLength(val + "", 'latin1'); 68 }); 69 70 } else { 71 if (typeof value !== 'string' && typeof value !== 'number') { 72 throw new TypeError('Value argument must be string, number, or array'); 73 } 74 75 header_count = 1; 76 header_len = Buffer.byteLength(value + "", 'latin1'); 77 } 78 79 this.removeHeader(key); 80 81 this.headers[key] = value; 82 this.headers_len += header_len + (header_key_len * header_count); 83 this.headers_count += header_count; 84}; 85 86ServerResponse.prototype.getHeader = function getHeader(name) { 87 return this.headers[name]; 88}; 89 90ServerResponse.prototype.getHeaderNames = function getHeaderNames() { 91 return Object.keys(this.headers); 92}; 93 94ServerResponse.prototype.getHeaders = function getHeaders() { 95 return this.headers; 96}; 97 98ServerResponse.prototype.hasHeader = function hasHeader(name) { 99 return name in this.headers; 100}; 101 102ServerResponse.prototype.removeHeader = function removeHeader(name) { 103 if (!(name in this.headers)) { 104 return; 105 } 106 107 let name_len = Buffer.byteLength(name + "", 'latin1'); 108 109 if (Array.isArray(this.headers[name])) { 110 this.headers_count -= this.headers[name].length; 111 this.headers_len -= this.headers[name].length * name_len; 112 113 this.headers[name].forEach(function(val) { 114 this.headers_len -= Buffer.byteLength(val + "", 'latin1'); 115 }); 116 117 } else { 118 this.headers_count--; 119 this.headers_len -= name_len + Buffer.byteLength(this.headers[name] + "", 'latin1'); 120 } 121 122 delete this.headers[name]; 123}; 124 125ServerResponse.prototype.sendDate = function sendDate() { 126 throw new Error("Not supported"); 127}; 128 129ServerResponse.prototype.setTimeout = function setTimeout(msecs, callback) { 130 this.timeout = msecs; 131 132 if (callback) { 133 this.on('timeout', callback); 134 } 135 136 return this; 137}; 138 139// for Express 140ServerResponse.prototype._implicitHeader = function _implicitHeader() { 141 this.writeHead(this.statusCode); 142}; 143 144ServerResponse.prototype.writeHead = writeHead; 145ServerResponse.prototype.writeHeader = ServerResponse.prototype.writeHead; 146 147function writeHead(statusCode, reason, obj) { 148 var originalStatusCode = statusCode; 149 150 statusCode |= 0; 151 152 if (statusCode < 100 || statusCode > 999) { 153 throw new ERR_HTTP_INVALID_STATUS_CODE(originalStatusCode); 154 } 155 156 if (typeof reason === 'string') { 157 this.statusMessage = reason; 158 159 } else { 160 if (!this.statusMessage) { 161 this.statusMessage = http.STATUS_CODES[statusCode] || 'unknown'; 162 } 163 164 obj = reason; 165 } 166 167 this.statusCode = statusCode; 168 169 if (obj) { 170 var k; 171 var keys = Object.keys(obj); 172 173 for (var i = 0; i < keys.length; i++) { 174 k = keys[i]; 175 176 if (k) { 177 this.setHeader(k, obj[k]); 178 } 179 } 180 } 181 182 unit_lib.unit_response_headers(this, statusCode, this.headers, this.headers_count, this.headers_len); 183 184 this.headersSent = true; 185}; 186 187ServerResponse.prototype._writeBody = function(chunk, encoding, callback) { 188 var contentLength = 0; 189 190 if (!this.headersSent) { 191 this.writeHead(this.statusCode); 192 } 193 194 if (this.finished) { 195 return this; 196 } 197 198 if (typeof chunk === 'function') { 199 callback = chunk; 200 chunk = null; 201 202 } else if (typeof encoding === 'function') { 203 callback = encoding; 204 encoding = null; 205 } 206 207 if (chunk) { 208 if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) { 209 throw new TypeError('First argument must be a string or Buffer'); 210 } 211 212 if (typeof chunk === 'string') { 213 contentLength = Buffer.byteLength(chunk, encoding); 214 215 } else { 216 contentLength = chunk.length; 217 } 218 219 unit_lib.unit_response_write(this, chunk, contentLength); 220 } 221 222 if (typeof callback === 'function') { 223 callback(this); 224 } 225}; 226 227ServerResponse.prototype.write = function write(chunk, encoding, callback) { 228 this._writeBody(chunk, encoding, callback); 229 230 return this; 231}; 232 233ServerResponse.prototype.end = function end(chunk, encoding, callback) { 234 this._writeBody(chunk, encoding, callback); 235 236 this.finished = true; 237 238 return this; 239}; 240 241function ServerRequest(server) { 242 EventEmitter.call(this); 243 244 this.server = server; 245} 246util.inherits(ServerRequest, EventEmitter); 247 248ServerRequest.prototype.unpipe = undefined; 249 250ServerRequest.prototype.setTimeout = function setTimeout(msecs, callback) { 251 this.timeout = msecs; 252 253 if (callback) { 254 this.on('timeout', callback); 255 } 256 257 return this; 258}; 259 260ServerRequest.prototype.statusCode = function statusCode() { 261 /* Only valid for response obtained from http.ClientRequest. */ 262}; 263 264ServerRequest.prototype.statusMessage = function statusMessage() { 265 /* Only valid for response obtained from http.ClientRequest. */ 266}; 267 268ServerRequest.prototype.trailers = function trailers() { 269 throw new Error("Not supported"); 270}; 271 272ServerRequest.prototype.METHODS = function METHODS() { 273 return http.METHODS; 274}; 275 276ServerRequest.prototype.STATUS_CODES = function STATUS_CODES() { 277 return http.STATUS_CODES; 278}; 279 280ServerRequest.prototype.listeners = function listeners() { 281 return []; 282}; 283 284ServerRequest.prototype.resume = function resume() { 285 return []; 286}; 287 288function Server(requestListener) { 289 EventEmitter.call(this); 290 291 this.unit = new unit_lib.Unit(); 292 this.unit.server = this; 293 294 this.unit.createServer(); 295 296 this.socket = Socket; 297 this.request = ServerRequest; 298 this.response = ServerResponse; 299 300 if (requestListener) { 301 this.on('request', requestListener); 302 } 303} 304util.inherits(Server, EventEmitter); 305 306Server.prototype.setTimeout = function setTimeout(msecs, callback) { 307 this.timeout = msecs; 308 309 if (callback) { 310 this.on('timeout', callback); 311 } 312 313 return this; 314}; 315 316Server.prototype.listen = function () { 317 this.unit.listen(); 318}; 319 320Server.prototype.run_events = function (server, req, res) { 321 /* Important!!! setImmediate starts the next iteration in Node.js loop. */ 322 setImmediate(function () { 323 server.emit("request", req, res); 324 325 Promise.resolve().then(() => { 326 let buf = server.unit._read(req.socket.req_pointer); 327 328 if (buf.length != 0) { 329 req.emit("data", buf); 330 } 331 332 req.emit("end"); 333 }); 334 335 Promise.resolve().then(() => { 336 req.emit("finish"); 337 338 if (res.finished) { 339 unit_lib.unit_response_end(res); 340 } 341 }); 342 }); 343}; 344 345function connectionListener(socket) { 346} 347 348module.exports = { 349 STATUS_CODES: http.STATUS_CODES, 350 Server, 351 ServerResponse, 352 ServerRequest, 353 _connectionListener: connectionListener 354}; 355