@ntrip/caster
Version:
NTRIP caster
123 lines (122 loc) • 5.47 kB
JavaScript
"use strict";
/*
* This file is part of the @ntrip/caster distribution (https://github.com/node-ntrip/caster).
* Copyright (c) 2020 Nebojsa Cvetkovic.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NtripHTTPParser = void 0;
const http_parser_ts_1 = require("http-parser-ts");
class NtripHTTPParser extends http_parser_ts_1.HTTPParser {
constructor() {
super(...arguments);
this.injectedLines = [];
}
injectLine(line) {
this.injectedLines.push(line);
}
consumeLine() {
var _a;
return (_a = this.injectedLines.pop()) !== null && _a !== void 0 ? _a : super.consumeLine();
}
REQUEST_LINE() {
var _a, _b, _c;
const line = this.consumeLine();
if (!line)
return;
const match = NtripHTTPParser.REQUEST_EXP.exec(line);
if (match === null)
throw parseError('HPE_INVALID_CONSTANT');
let method = match.groups['method'];
let protocol = (_a = match.groups['protocol']) !== null && _a !== void 0 ? _a : 'HTTP';
// Process SOURCE request secret
if (method.startsWith('SOURCE ')) {
method = 'SOURCE';
this.info.headers.push('Ntrip-Source-Secret', match.groups['secret']);
}
this.info.method = NtripHTTPParser.methods.indexOf(method);
if (this.info.method === -1)
throw new Error('invalid request method');
this.info.url = match.groups['url'];
this.info.headers.push('@protocol', protocol);
this.socket.protocol = protocol;
this.info.versionMajor = +((_b = match.groups['versionMajor']) !== null && _b !== void 0 ? _b : 1);
this.info.versionMinor = +((_c = match.groups['versionMinor']) !== null && _c !== void 0 ? _c : 1);
// TODO: Discover why keep-alive can't be forced with 1.0
if (protocol === 'RTSP') {
this.info.versionMajor = 1;
this.info.versionMinor = 1;
}
this.bodyBytes = 0;
this.state = 'HEADER';
}
RESPONSE_LINE() {
var _a, _b, _c, _d;
const line = this.consumeLine();
if (!line)
return;
const match = (_a = NtripHTTPParser.RESPONSE_EXP.exec(line)) !== null && _a !== void 0 ? _a : NtripHTTPParser.ERROR_EXP.exec(line);
if (match === null)
throw parseError('HPE_INVALID_CONSTANT');
let protocol = (_b = match.groups['protocol']) !== null && _b !== void 0 ? _b : 'HTTP';
// Inject newline for casters that don't send second \r\n
if (['ICY', 'SOURCETABLE', 'ERROR'].includes(protocol))
this.injectLine('');
let statusCode = +match.groups['code'];
let statusMessage = match.groups['message'];
// Error message doesn't have status code
if (isNaN(statusCode)) {
let statusMessageLower = statusMessage.toLowerCase();
if (statusMessageLower === 'bad password') {
statusCode = 401;
}
else if (statusMessageLower === 'already connected') {
statusCode = 409;
}
else if (statusMessageLower === 'mount point taken or invalid') {
statusCode = 404;
}
else {
statusCode = 400;
}
}
this.info.statusCode = statusCode;
this.info.statusMessage = statusMessage;
this.info.headers.push('@protocol', protocol);
this.socket.protocol = protocol;
this.info.versionMajor = +((_c = match.groups['versionMajor']) !== null && _c !== void 0 ? _c : 1);
this.info.versionMinor = +((_d = match.groups['versionMinor']) !== null && _d !== void 0 ? _d : 1);
// TODO: Discover why keep-alive can't be forced with 1.0
if (protocol === 'RTSP') {
this.info.versionMajor = 1;
this.info.versionMinor = 1;
}
// Implied zero length
if ((statusCode / 100 | 0) === 1 || statusCode === 204 || statusCode === 304) {
this.bodyBytes = 0;
}
this.state = 'HEADER';
}
}
exports.NtripHTTPParser = NtripHTTPParser;
NtripHTTPParser.REQUEST_EXP = /^(?<method>[A-Z-_]+|SOURCE (?<secret>[^ ]+)) (?<url>[^ ]+)(?: (?<protocol>HTTP|RTSP|RTP)\/(?<versionMajor>\d)\.(?<versionMinor>\d))?$/;
NtripHTTPParser.RESPONSE_EXP = /^(?<protocol>ICY|SOURCETABLE|HTTP|RTSP)(?:\/(?<versionMajor>\d)\.(?<versionMinor>\d))? (?<code>\d{3}) ?(?<message>.*)$/;
NtripHTTPParser.ERROR_EXP = /^(?<protocol>ERROR)(?: - (?<message>.*))?$/;
NtripHTTPParser.methods = http_parser_ts_1.HTTPParser.methods.concat(['SOURCE', 'SETUP', 'RECORD', 'PLAY', 'TEARDOWN', 'GET_PARAMETER']).sort();
function parseError(code) {
let err = new Error('Parse Error');
err.code = code;
return err;
}