server.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Server = exports.BaseServer = void 0;
  4. const qs = require("querystring");
  5. const url_1 = require("url");
  6. const base64id = require("base64id");
  7. const transports_1 = require("./transports");
  8. const events_1 = require("events");
  9. const socket_1 = require("./socket");
  10. const debug_1 = require("debug");
  11. const cookie_1 = require("cookie");
  12. const ws_1 = require("ws");
  13. const debug = (0, debug_1.default)("engine");
  14. const kResponseHeaders = Symbol("responseHeaders");
  15. class BaseServer extends events_1.EventEmitter {
  16. /**
  17. * Server constructor.
  18. *
  19. * @param {Object} opts - options
  20. * @api public
  21. */
  22. constructor(opts = {}) {
  23. super();
  24. this.middlewares = [];
  25. this.clients = {};
  26. this.clientsCount = 0;
  27. this.opts = Object.assign({
  28. wsEngine: ws_1.Server,
  29. pingTimeout: 20000,
  30. pingInterval: 25000,
  31. upgradeTimeout: 10000,
  32. maxHttpBufferSize: 1e6,
  33. transports: Object.keys(transports_1.default),
  34. allowUpgrades: true,
  35. httpCompression: {
  36. threshold: 1024,
  37. },
  38. cors: false,
  39. allowEIO3: false,
  40. }, opts);
  41. if (opts.cookie) {
  42. this.opts.cookie = Object.assign({
  43. name: "io",
  44. path: "/",
  45. // @ts-ignore
  46. httpOnly: opts.cookie.path !== false,
  47. sameSite: "lax",
  48. }, opts.cookie);
  49. }
  50. if (this.opts.cors) {
  51. this.use(require("cors")(this.opts.cors));
  52. }
  53. if (opts.perMessageDeflate) {
  54. this.opts.perMessageDeflate = Object.assign({
  55. threshold: 1024,
  56. }, opts.perMessageDeflate);
  57. }
  58. this.init();
  59. }
  60. /**
  61. * Compute the pathname of the requests that are handled by the server
  62. * @param options
  63. * @protected
  64. */
  65. _computePath(options) {
  66. let path = (options.path || "/engine.io").replace(/\/$/, "");
  67. if (options.addTrailingSlash !== false) {
  68. // normalize path
  69. path += "/";
  70. }
  71. return path;
  72. }
  73. /**
  74. * Returns a list of available transports for upgrade given a certain transport.
  75. *
  76. * @return {Array}
  77. * @api public
  78. */
  79. upgrades(transport) {
  80. if (!this.opts.allowUpgrades)
  81. return [];
  82. return transports_1.default[transport].upgradesTo || [];
  83. }
  84. /**
  85. * Verifies a request.
  86. *
  87. * @param {http.IncomingMessage}
  88. * @return {Boolean} whether the request is valid
  89. * @api private
  90. */
  91. verify(req, upgrade, fn) {
  92. // transport check
  93. const transport = req._query.transport;
  94. if (!~this.opts.transports.indexOf(transport)) {
  95. debug('unknown transport "%s"', transport);
  96. return fn(Server.errors.UNKNOWN_TRANSPORT, { transport });
  97. }
  98. // 'Origin' header check
  99. const isOriginInvalid = checkInvalidHeaderChar(req.headers.origin);
  100. if (isOriginInvalid) {
  101. const origin = req.headers.origin;
  102. req.headers.origin = null;
  103. debug("origin header invalid");
  104. return fn(Server.errors.BAD_REQUEST, {
  105. name: "INVALID_ORIGIN",
  106. origin,
  107. });
  108. }
  109. // sid check
  110. const sid = req._query.sid;
  111. if (sid) {
  112. if (!this.clients.hasOwnProperty(sid)) {
  113. debug('unknown sid "%s"', sid);
  114. return fn(Server.errors.UNKNOWN_SID, {
  115. sid,
  116. });
  117. }
  118. const previousTransport = this.clients[sid].transport.name;
  119. if (!upgrade && previousTransport !== transport) {
  120. debug("bad request: unexpected transport without upgrade");
  121. return fn(Server.errors.BAD_REQUEST, {
  122. name: "TRANSPORT_MISMATCH",
  123. transport,
  124. previousTransport,
  125. });
  126. }
  127. }
  128. else {
  129. // handshake is GET only
  130. if ("GET" !== req.method) {
  131. return fn(Server.errors.BAD_HANDSHAKE_METHOD, {
  132. method: req.method,
  133. });
  134. }
  135. if (transport === "websocket" && !upgrade) {
  136. debug("invalid transport upgrade");
  137. return fn(Server.errors.BAD_REQUEST, {
  138. name: "TRANSPORT_HANDSHAKE_ERROR",
  139. });
  140. }
  141. if (!this.opts.allowRequest)
  142. return fn();
  143. return this.opts.allowRequest(req, (message, success) => {
  144. if (!success) {
  145. return fn(Server.errors.FORBIDDEN, {
  146. message,
  147. });
  148. }
  149. fn();
  150. });
  151. }
  152. fn();
  153. }
  154. /**
  155. * Adds a new middleware.
  156. *
  157. * @example
  158. * import helmet from "helmet";
  159. *
  160. * engine.use(helmet());
  161. *
  162. * @param fn
  163. */
  164. use(fn) {
  165. this.middlewares.push(fn);
  166. }
  167. /**
  168. * Apply the middlewares to the request.
  169. *
  170. * @param req
  171. * @param res
  172. * @param callback
  173. * @protected
  174. */
  175. _applyMiddlewares(req, res, callback) {
  176. if (this.middlewares.length === 0) {
  177. debug("no middleware to apply, skipping");
  178. return callback();
  179. }
  180. const apply = (i) => {
  181. debug("applying middleware n°%d", i + 1);
  182. this.middlewares[i](req, res, (err) => {
  183. if (err) {
  184. return callback(err);
  185. }
  186. if (i + 1 < this.middlewares.length) {
  187. apply(i + 1);
  188. }
  189. else {
  190. callback();
  191. }
  192. });
  193. };
  194. apply(0);
  195. }
  196. /**
  197. * Closes all clients.
  198. *
  199. * @api public
  200. */
  201. close() {
  202. debug("closing all open clients");
  203. for (let i in this.clients) {
  204. if (this.clients.hasOwnProperty(i)) {
  205. this.clients[i].close(true);
  206. }
  207. }
  208. this.cleanup();
  209. return this;
  210. }
  211. /**
  212. * generate a socket id.
  213. * Overwrite this method to generate your custom socket id
  214. *
  215. * @param {Object} request object
  216. * @api public
  217. */
  218. generateId(req) {
  219. return base64id.generateId();
  220. }
  221. /**
  222. * Handshakes a new client.
  223. *
  224. * @param {String} transport name
  225. * @param {Object} request object
  226. * @param {Function} closeConnection
  227. *
  228. * @api protected
  229. */
  230. async handshake(transportName, req, closeConnection) {
  231. const protocol = req._query.EIO === "4" ? 4 : 3; // 3rd revision by default
  232. if (protocol === 3 && !this.opts.allowEIO3) {
  233. debug("unsupported protocol version");
  234. this.emit("connection_error", {
  235. req,
  236. code: Server.errors.UNSUPPORTED_PROTOCOL_VERSION,
  237. message: Server.errorMessages[Server.errors.UNSUPPORTED_PROTOCOL_VERSION],
  238. context: {
  239. protocol,
  240. },
  241. });
  242. closeConnection(Server.errors.UNSUPPORTED_PROTOCOL_VERSION);
  243. return;
  244. }
  245. let id;
  246. try {
  247. id = await this.generateId(req);
  248. }
  249. catch (e) {
  250. debug("error while generating an id");
  251. this.emit("connection_error", {
  252. req,
  253. code: Server.errors.BAD_REQUEST,
  254. message: Server.errorMessages[Server.errors.BAD_REQUEST],
  255. context: {
  256. name: "ID_GENERATION_ERROR",
  257. error: e,
  258. },
  259. });
  260. closeConnection(Server.errors.BAD_REQUEST);
  261. return;
  262. }
  263. debug('handshaking client "%s"', id);
  264. try {
  265. var transport = this.createTransport(transportName, req);
  266. if ("polling" === transportName) {
  267. transport.maxHttpBufferSize = this.opts.maxHttpBufferSize;
  268. transport.httpCompression = this.opts.httpCompression;
  269. }
  270. else if ("websocket" === transportName) {
  271. transport.perMessageDeflate = this.opts.perMessageDeflate;
  272. }
  273. if (req._query && req._query.b64) {
  274. transport.supportsBinary = false;
  275. }
  276. else {
  277. transport.supportsBinary = true;
  278. }
  279. }
  280. catch (e) {
  281. debug('error handshaking to transport "%s"', transportName);
  282. this.emit("connection_error", {
  283. req,
  284. code: Server.errors.BAD_REQUEST,
  285. message: Server.errorMessages[Server.errors.BAD_REQUEST],
  286. context: {
  287. name: "TRANSPORT_HANDSHAKE_ERROR",
  288. error: e,
  289. },
  290. });
  291. closeConnection(Server.errors.BAD_REQUEST);
  292. return;
  293. }
  294. const socket = new socket_1.Socket(id, this, transport, req, protocol);
  295. transport.on("headers", (headers, req) => {
  296. const isInitialRequest = !req._query.sid;
  297. if (isInitialRequest) {
  298. if (this.opts.cookie) {
  299. headers["Set-Cookie"] = [
  300. // @ts-ignore
  301. (0, cookie_1.serialize)(this.opts.cookie.name, id, this.opts.cookie),
  302. ];
  303. }
  304. this.emit("initial_headers", headers, req);
  305. }
  306. this.emit("headers", headers, req);
  307. });
  308. transport.onRequest(req);
  309. this.clients[id] = socket;
  310. this.clientsCount++;
  311. socket.once("close", () => {
  312. delete this.clients[id];
  313. this.clientsCount--;
  314. });
  315. this.emit("connection", socket);
  316. return transport;
  317. }
  318. }
  319. exports.BaseServer = BaseServer;
  320. /**
  321. * Protocol errors mappings.
  322. */
  323. BaseServer.errors = {
  324. UNKNOWN_TRANSPORT: 0,
  325. UNKNOWN_SID: 1,
  326. BAD_HANDSHAKE_METHOD: 2,
  327. BAD_REQUEST: 3,
  328. FORBIDDEN: 4,
  329. UNSUPPORTED_PROTOCOL_VERSION: 5,
  330. };
  331. BaseServer.errorMessages = {
  332. 0: "Transport unknown",
  333. 1: "Session ID unknown",
  334. 2: "Bad handshake method",
  335. 3: "Bad request",
  336. 4: "Forbidden",
  337. 5: "Unsupported protocol version",
  338. };
  339. /**
  340. * Exposes a subset of the http.ServerResponse interface, in order to be able to apply the middlewares to an upgrade
  341. * request.
  342. *
  343. * @see https://nodejs.org/api/http.html#class-httpserverresponse
  344. */
  345. class WebSocketResponse {
  346. constructor(req, socket) {
  347. this.req = req;
  348. this.socket = socket;
  349. // temporarily store the response headers on the req object (see the "headers" event)
  350. req[kResponseHeaders] = {};
  351. }
  352. setHeader(name, value) {
  353. this.req[kResponseHeaders][name] = value;
  354. }
  355. getHeader(name) {
  356. return this.req[kResponseHeaders][name];
  357. }
  358. removeHeader(name) {
  359. delete this.req[kResponseHeaders][name];
  360. }
  361. write() { }
  362. writeHead() { }
  363. end() {
  364. // we could return a proper error code, but the WebSocket client will emit an "error" event anyway.
  365. this.socket.destroy();
  366. }
  367. }
  368. class Server extends BaseServer {
  369. /**
  370. * Initialize websocket server
  371. *
  372. * @api protected
  373. */
  374. init() {
  375. if (!~this.opts.transports.indexOf("websocket"))
  376. return;
  377. if (this.ws)
  378. this.ws.close();
  379. this.ws = new this.opts.wsEngine({
  380. noServer: true,
  381. clientTracking: false,
  382. perMessageDeflate: this.opts.perMessageDeflate,
  383. maxPayload: this.opts.maxHttpBufferSize,
  384. });
  385. if (typeof this.ws.on === "function") {
  386. this.ws.on("headers", (headersArray, req) => {
  387. // note: 'ws' uses an array of headers, while Engine.IO uses an object (response.writeHead() accepts both formats)
  388. // we could also try to parse the array and then sync the values, but that will be error-prone
  389. const additionalHeaders = req[kResponseHeaders] || {};
  390. delete req[kResponseHeaders];
  391. const isInitialRequest = !req._query.sid;
  392. if (isInitialRequest) {
  393. this.emit("initial_headers", additionalHeaders, req);
  394. }
  395. this.emit("headers", additionalHeaders, req);
  396. debug("writing headers: %j", additionalHeaders);
  397. Object.keys(additionalHeaders).forEach((key) => {
  398. headersArray.push(`${key}: ${additionalHeaders[key]}`);
  399. });
  400. });
  401. }
  402. }
  403. cleanup() {
  404. if (this.ws) {
  405. debug("closing webSocketServer");
  406. this.ws.close();
  407. // don't delete this.ws because it can be used again if the http server starts listening again
  408. }
  409. }
  410. /**
  411. * Prepares a request by processing the query string.
  412. *
  413. * @api private
  414. */
  415. prepare(req) {
  416. // try to leverage pre-existing `req._query` (e.g: from connect)
  417. if (!req._query) {
  418. req._query = ~req.url.indexOf("?") ? qs.parse((0, url_1.parse)(req.url).query) : {};
  419. }
  420. }
  421. createTransport(transportName, req) {
  422. return new transports_1.default[transportName](req);
  423. }
  424. /**
  425. * Handles an Engine.IO HTTP request.
  426. *
  427. * @param {IncomingMessage} req
  428. * @param {ServerResponse} res
  429. * @api public
  430. */
  431. handleRequest(req, res) {
  432. debug('handling "%s" http request "%s"', req.method, req.url);
  433. this.prepare(req);
  434. // @ts-ignore
  435. req.res = res;
  436. const callback = (errorCode, errorContext) => {
  437. if (errorCode !== undefined) {
  438. this.emit("connection_error", {
  439. req,
  440. code: errorCode,
  441. message: Server.errorMessages[errorCode],
  442. context: errorContext,
  443. });
  444. abortRequest(res, errorCode, errorContext);
  445. return;
  446. }
  447. // @ts-ignore
  448. if (req._query.sid) {
  449. debug("setting new request for existing client");
  450. // @ts-ignore
  451. this.clients[req._query.sid].transport.onRequest(req);
  452. }
  453. else {
  454. const closeConnection = (errorCode, errorContext) => abortRequest(res, errorCode, errorContext);
  455. // @ts-ignore
  456. this.handshake(req._query.transport, req, closeConnection);
  457. }
  458. };
  459. this._applyMiddlewares(req, res, (err) => {
  460. if (err) {
  461. callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
  462. }
  463. else {
  464. this.verify(req, false, callback);
  465. }
  466. });
  467. }
  468. /**
  469. * Handles an Engine.IO HTTP Upgrade.
  470. *
  471. * @api public
  472. */
  473. handleUpgrade(req, socket, upgradeHead) {
  474. this.prepare(req);
  475. const res = new WebSocketResponse(req, socket);
  476. const callback = (errorCode, errorContext) => {
  477. if (errorCode !== undefined) {
  478. this.emit("connection_error", {
  479. req,
  480. code: errorCode,
  481. message: Server.errorMessages[errorCode],
  482. context: errorContext,
  483. });
  484. abortUpgrade(socket, errorCode, errorContext);
  485. return;
  486. }
  487. const head = Buffer.from(upgradeHead);
  488. upgradeHead = null;
  489. // some middlewares (like express-session) wait for the writeHead() call to flush their headers
  490. // see https://github.com/expressjs/session/blob/1010fadc2f071ddf2add94235d72224cf65159c6/index.js#L220-L244
  491. res.writeHead();
  492. // delegate to ws
  493. this.ws.handleUpgrade(req, socket, head, (websocket) => {
  494. this.onWebSocket(req, socket, websocket);
  495. });
  496. };
  497. this._applyMiddlewares(req, res, (err) => {
  498. if (err) {
  499. callback(Server.errors.BAD_REQUEST, { name: "MIDDLEWARE_FAILURE" });
  500. }
  501. else {
  502. this.verify(req, true, callback);
  503. }
  504. });
  505. }
  506. /**
  507. * Called upon a ws.io connection.
  508. *
  509. * @param {ws.Socket} websocket
  510. * @api private
  511. */
  512. onWebSocket(req, socket, websocket) {
  513. websocket.on("error", onUpgradeError);
  514. if (transports_1.default[req._query.transport] !== undefined &&
  515. !transports_1.default[req._query.transport].prototype.handlesUpgrades) {
  516. debug("transport doesnt handle upgraded requests");
  517. websocket.close();
  518. return;
  519. }
  520. // get client id
  521. const id = req._query.sid;
  522. // keep a reference to the ws.Socket
  523. req.websocket = websocket;
  524. if (id) {
  525. const client = this.clients[id];
  526. if (!client) {
  527. debug("upgrade attempt for closed client");
  528. websocket.close();
  529. }
  530. else if (client.upgrading) {
  531. debug("transport has already been trying to upgrade");
  532. websocket.close();
  533. }
  534. else if (client.upgraded) {
  535. debug("transport had already been upgraded");
  536. websocket.close();
  537. }
  538. else {
  539. debug("upgrading existing transport");
  540. // transport error handling takes over
  541. websocket.removeListener("error", onUpgradeError);
  542. const transport = this.createTransport(req._query.transport, req);
  543. if (req._query && req._query.b64) {
  544. transport.supportsBinary = false;
  545. }
  546. else {
  547. transport.supportsBinary = true;
  548. }
  549. transport.perMessageDeflate = this.opts.perMessageDeflate;
  550. client.maybeUpgrade(transport);
  551. }
  552. }
  553. else {
  554. const closeConnection = (errorCode, errorContext) => abortUpgrade(socket, errorCode, errorContext);
  555. this.handshake(req._query.transport, req, closeConnection);
  556. }
  557. function onUpgradeError() {
  558. debug("websocket error before upgrade");
  559. // websocket.close() not needed
  560. }
  561. }
  562. /**
  563. * Captures upgrade requests for a http.Server.
  564. *
  565. * @param {http.Server} server
  566. * @param {Object} options
  567. * @api public
  568. */
  569. attach(server, options = {}) {
  570. const path = this._computePath(options);
  571. const destroyUpgradeTimeout = options.destroyUpgradeTimeout || 1000;
  572. function check(req) {
  573. // TODO use `path === new URL(...).pathname` in the next major release (ref: https://nodejs.org/api/url.html)
  574. return path === req.url.slice(0, path.length);
  575. }
  576. // cache and clean up listeners
  577. const listeners = server.listeners("request").slice(0);
  578. server.removeAllListeners("request");
  579. server.on("close", this.close.bind(this));
  580. server.on("listening", this.init.bind(this));
  581. // add request handler
  582. server.on("request", (req, res) => {
  583. if (check(req)) {
  584. debug('intercepting request for path "%s"', path);
  585. this.handleRequest(req, res);
  586. }
  587. else {
  588. let i = 0;
  589. const l = listeners.length;
  590. for (; i < l; i++) {
  591. listeners[i].call(server, req, res);
  592. }
  593. }
  594. });
  595. if (~this.opts.transports.indexOf("websocket")) {
  596. server.on("upgrade", (req, socket, head) => {
  597. if (check(req)) {
  598. this.handleUpgrade(req, socket, head);
  599. }
  600. else if (false !== options.destroyUpgrade) {
  601. // default node behavior is to disconnect when no handlers
  602. // but by adding a handler, we prevent that
  603. // and if no eio thing handles the upgrade
  604. // then the socket needs to die!
  605. setTimeout(function () {
  606. // @ts-ignore
  607. if (socket.writable && socket.bytesWritten <= 0) {
  608. socket.on("error", (e) => {
  609. debug("error while destroying upgrade: %s", e.message);
  610. });
  611. return socket.end();
  612. }
  613. }, destroyUpgradeTimeout);
  614. }
  615. });
  616. }
  617. }
  618. }
  619. exports.Server = Server;
  620. /**
  621. * Close the HTTP long-polling request
  622. *
  623. * @param res - the response object
  624. * @param errorCode - the error code
  625. * @param errorContext - additional error context
  626. *
  627. * @api private
  628. */
  629. function abortRequest(res, errorCode, errorContext) {
  630. const statusCode = errorCode === Server.errors.FORBIDDEN ? 403 : 400;
  631. const message = errorContext && errorContext.message
  632. ? errorContext.message
  633. : Server.errorMessages[errorCode];
  634. res.writeHead(statusCode, { "Content-Type": "application/json" });
  635. res.end(JSON.stringify({
  636. code: errorCode,
  637. message,
  638. }));
  639. }
  640. /**
  641. * Close the WebSocket connection
  642. *
  643. * @param {net.Socket} socket
  644. * @param {string} errorCode - the error code
  645. * @param {object} errorContext - additional error context
  646. *
  647. * @api private
  648. */
  649. function abortUpgrade(socket, errorCode, errorContext = {}) {
  650. socket.on("error", () => {
  651. debug("ignoring error from closed connection");
  652. });
  653. if (socket.writable) {
  654. const message = errorContext.message || Server.errorMessages[errorCode];
  655. const length = Buffer.byteLength(message);
  656. socket.write("HTTP/1.1 400 Bad Request\r\n" +
  657. "Connection: close\r\n" +
  658. "Content-type: text/html\r\n" +
  659. "Content-Length: " +
  660. length +
  661. "\r\n" +
  662. "\r\n" +
  663. message);
  664. }
  665. socket.destroy();
  666. }
  667. /* eslint-disable */
  668. /**
  669. * From https://github.com/nodejs/node/blob/v8.4.0/lib/_http_common.js#L303-L354
  670. *
  671. * True if val contains an invalid field-vchar
  672. * field-value = *( field-content / obs-fold )
  673. * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
  674. * field-vchar = VCHAR / obs-text
  675. *
  676. * checkInvalidHeaderChar() is currently designed to be inlinable by v8,
  677. * so take care when making changes to the implementation so that the source
  678. * code size does not exceed v8's default max_inlined_source_size setting.
  679. **/
  680. // prettier-ignore
  681. const validHdrChars = [
  682. 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
  683. 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  684. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  685. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  686. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  687. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  688. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  689. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
  690. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  691. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  692. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  693. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  694. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  695. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  696. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  697. 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // ... 255
  698. ];
  699. function checkInvalidHeaderChar(val) {
  700. val += "";
  701. if (val.length < 1)
  702. return false;
  703. if (!validHdrChars[val.charCodeAt(0)]) {
  704. debug('invalid header, index 0, char "%s"', val.charCodeAt(0));
  705. return true;
  706. }
  707. if (val.length < 2)
  708. return false;
  709. if (!validHdrChars[val.charCodeAt(1)]) {
  710. debug('invalid header, index 1, char "%s"', val.charCodeAt(1));
  711. return true;
  712. }
  713. if (val.length < 3)
  714. return false;
  715. if (!validHdrChars[val.charCodeAt(2)]) {
  716. debug('invalid header, index 2, char "%s"', val.charCodeAt(2));
  717. return true;
  718. }
  719. if (val.length < 4)
  720. return false;
  721. if (!validHdrChars[val.charCodeAt(3)]) {
  722. debug('invalid header, index 3, char "%s"', val.charCodeAt(3));
  723. return true;
  724. }
  725. for (let i = 4; i < val.length; ++i) {
  726. if (!validHdrChars[val.charCodeAt(i)]) {
  727. debug('invalid header, index "%i", char "%s"', i, val.charCodeAt(i));
  728. return true;
  729. }
  730. }
  731. return false;
  732. }