web_plsql 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +123 -110
- package/examples/server_apex.js +28 -0
- package/examples/server_sample.js +30 -0
- package/examples/sql/{doc_table.sql → doctable.sql} +1 -1
- package/examples/sql/install.sql +6 -9
- package/examples/sql/sample_package.sql +27 -0
- package/examples/sql/sample_package_body.sql +245 -0
- package/examples/static/sample.css +11 -10
- package/package.json +90 -83
- package/src/cgi.js +127 -0
- package/src/error.js +24 -0
- package/src/errorPage.js +296 -0
- package/src/file.js +77 -0
- package/src/handlerLogger.js +21 -0
- package/src/handlerMetrics.js +46 -0
- package/src/handlerPlSql.js +65 -0
- package/src/handlerUpload.js +32 -0
- package/src/index.js +17 -0
- package/src/oracle.js +80 -0
- package/src/parsePage.js +193 -0
- package/src/procedure.js +260 -0
- package/src/procedureError.js +40 -0
- package/src/procedureNamed.js +233 -0
- package/src/procedureSanitize.js +215 -0
- package/src/procedureVariable.js +57 -0
- package/src/request.js +103 -0
- package/src/{requestError.ts → requestError.js} +8 -4
- package/src/sendResponse.js +104 -0
- package/src/server.js +106 -0
- package/src/shutdown.js +53 -0
- package/src/stream.js +28 -0
- package/src/trace.js +74 -0
- package/src/tty.js +48 -0
- package/src/types.js +146 -0
- package/src/upload.js +92 -0
- package/src/version.js +23 -0
- package/types/cgi.d.ts +4 -0
- package/types/error.d.ts +1 -0
- package/types/errorPage.d.ts +10 -0
- package/types/file.d.ts +5 -0
- package/types/handlerLogger.d.ts +2 -0
- package/types/handlerMetrics.d.ts +4 -0
- package/types/handlerPlSql.d.ts +8 -0
- package/types/handlerUpload.d.ts +7 -0
- package/types/index.d.ts +10 -0
- package/types/oracle.d.ts +5 -0
- package/types/parsePage.d.ts +3 -0
- package/types/procedure.d.ts +10 -0
- package/types/procedureError.d.ts +23 -0
- package/types/procedureNamed.d.ts +14 -0
- package/types/procedureSanitize.d.ts +14 -0
- package/types/procedureVariable.d.ts +9 -0
- package/types/request.d.ts +7 -0
- package/types/requestError.d.ts +8 -0
- package/types/sendResponse.d.ts +4 -0
- package/types/server.d.ts +7 -0
- package/types/shutdown.d.ts +2 -0
- package/types/stream.d.ts +1 -0
- package/types/trace.d.ts +5 -0
- package/types/tty.d.ts +4 -0
- package/types/types.d.ts +251 -0
- package/types/upload.d.ts +5 -0
- package/types/version.d.ts +7 -0
- package/.editorconfig +0 -8
- package/.eslintignore +0 -3
- package/.eslintrc.js +0 -347
- package/CHANGELOG.md +0 -127
- package/examples/apex.js +0 -70
- package/examples/credentials.js +0 -22
- package/examples/oracledb_example.js +0 -30
- package/examples/sample.js +0 -101
- package/examples/sql/sample.pkb +0 -223
- package/examples/sql/sample.pks +0 -24
- package/jest.config.js +0 -204
- package/src/cgi.ts +0 -95
- package/src/config.ts +0 -97
- package/src/errorPage.ts +0 -286
- package/src/fileUpload.ts +0 -126
- package/src/index.ts +0 -65
- package/src/page.ts +0 -275
- package/src/procedure.ts +0 -360
- package/src/procedureError.ts +0 -27
- package/src/request.ts +0 -139
- package/src/stream.ts +0 -26
- package/src/trace.ts +0 -194
- package/test/.eslintrc.json +0 -5
- package/test/__tests__/cgi.ts +0 -96
- package/test/__tests__/config.ts +0 -41
- package/test/__tests__/errorPage.ts +0 -101
- package/test/__tests__/oracledb_mock.ts +0 -98
- package/test/__tests__/server.ts +0 -495
- package/test/__tests__/stream.ts +0 -21
- package/test/mock/oracledb.ts +0 -85
- package/test/static/static.html +0 -1
- package/tsconfig.json +0 -24
- package/tsconfig.src.json +0 -23
package/src/file.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import {promises as fs, readFileSync} from 'node:fs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Read file.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} filePath - File name.
|
|
7
|
+
* @returns {Promise<Buffer>} The buffer.
|
|
8
|
+
*/
|
|
9
|
+
export const readFile = (filePath) => {
|
|
10
|
+
try {
|
|
11
|
+
return fs.readFile(filePath);
|
|
12
|
+
} catch (err) {
|
|
13
|
+
/* istanbul ignore next */
|
|
14
|
+
throw new Error(`Unable to read file "${filePath}"`);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Remove file.
|
|
20
|
+
*
|
|
21
|
+
* @param {string} filePath - File name.
|
|
22
|
+
* @returns {Promise<void>}.
|
|
23
|
+
*/
|
|
24
|
+
export const removeFile = (filePath) => {
|
|
25
|
+
try {
|
|
26
|
+
return fs.unlink(filePath);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
/* istanbul ignore next */
|
|
29
|
+
throw new Error(`Unable to remove file "${filePath}"`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Load a json file.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} filePath - File name.
|
|
37
|
+
* @returns {unknown} The json object.
|
|
38
|
+
*/
|
|
39
|
+
export const getJsonFile = (filePath) => {
|
|
40
|
+
const fileContent = readFileSync(filePath, 'utf8');
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(fileContent);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
throw new Error(`Unable to load file "${filePath}"`);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Is this a directory.
|
|
51
|
+
* @param {unknown} directoryPath - Directory name.
|
|
52
|
+
* @returns {Promise<boolean>} - Return true if it is a directory.
|
|
53
|
+
*/
|
|
54
|
+
export const isDirectory = async (directoryPath) => {
|
|
55
|
+
if (typeof directoryPath !== 'string') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const stats = await fs.stat(directoryPath);
|
|
60
|
+
|
|
61
|
+
return stats.isDirectory();
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Is this a file.
|
|
66
|
+
* @param {unknown} filePath - File name.
|
|
67
|
+
* @returns {Promise<boolean>} - Return true if it is a file.
|
|
68
|
+
*/
|
|
69
|
+
export const isFile = async (filePath) => {
|
|
70
|
+
if (typeof filePath !== 'string') {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const stats = await fs.stat(filePath);
|
|
75
|
+
|
|
76
|
+
return stats.isFile();
|
|
77
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import debugModule from 'debug';
|
|
2
|
+
const debug = debugModule('webplsql:handlerLogger');
|
|
3
|
+
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import morgan from 'morgan';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('express').RequestHandler} RequestHandler
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create the upload middleware.
|
|
14
|
+
* @param {string} filename - Output filename.
|
|
15
|
+
* @returns {RequestHandler} - Request handler.
|
|
16
|
+
*/
|
|
17
|
+
export const handlerLogger = (filename) => {
|
|
18
|
+
debug('register');
|
|
19
|
+
|
|
20
|
+
return morgan('combined', {stream: fs.createWriteStream(path.join(process.cwd(), filename), {flags: 'a'})});
|
|
21
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import debugModule from 'debug';
|
|
2
|
+
const debug = debugModule('webplsql:handlerMetrics');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('express').RequestHandler} RequestHandler
|
|
6
|
+
* @typedef {import('./types.js').metricsType} metricsType
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* metrics initializer.
|
|
11
|
+
* @returns {metricsType} The metrics.
|
|
12
|
+
*/
|
|
13
|
+
export const initMetrics = () => {
|
|
14
|
+
return {
|
|
15
|
+
totalRequests: 0,
|
|
16
|
+
requestsInLastInterval: 0,
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The metrics handler.
|
|
22
|
+
* @param {metricsType} metrics - the nmetrics.
|
|
23
|
+
* @returns {RequestHandler} - Request handler.
|
|
24
|
+
*/
|
|
25
|
+
export const handlerMetrics = (metrics) => {
|
|
26
|
+
debug('register');
|
|
27
|
+
|
|
28
|
+
metrics.totalRequests = 0;
|
|
29
|
+
metrics.requestsInLastInterval = 0;
|
|
30
|
+
|
|
31
|
+
return (req, res, next) => {
|
|
32
|
+
metrics.totalRequests++;
|
|
33
|
+
metrics.requestsInLastInterval++;
|
|
34
|
+
|
|
35
|
+
if (debug.enabled) {
|
|
36
|
+
const start = process.hrtime();
|
|
37
|
+
res.on('finish', () => {
|
|
38
|
+
const [seconds, nanoseconds] = process.hrtime(start);
|
|
39
|
+
const duration = seconds * 1000 + nanoseconds / 1_000_000;
|
|
40
|
+
debug(`Request to ${req.params?.name} ${req.url} took ${duration.toFixed(3)}ms`);
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
next();
|
|
45
|
+
};
|
|
46
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Express middleware for Oracle PL/SQL
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import debugModule from 'debug';
|
|
6
|
+
const debug = debugModule('webplsql:handlerPlSql');
|
|
7
|
+
|
|
8
|
+
import url from 'node:url';
|
|
9
|
+
import {processRequest} from './request.js';
|
|
10
|
+
import {RequestError} from './requestError.js';
|
|
11
|
+
import {errorPage} from './errorPage.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {import('express').RequestHandler} RequestHandler
|
|
15
|
+
* @typedef {import('express').Request} Request
|
|
16
|
+
* @typedef {import('express').Response} Response
|
|
17
|
+
* @typedef {import('express').NextFunction} NextFunction
|
|
18
|
+
* @typedef {import('oracledb').Pool} Pool
|
|
19
|
+
* @typedef {import('./types.js').environmentType} environmentType
|
|
20
|
+
* @typedef {import('./types.js').configPlSqlHandlerType} configPlSqlHandlerType
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* express.Request handler
|
|
25
|
+
* @param {Request} req - The req object represents the HTTP request.
|
|
26
|
+
* @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
|
|
27
|
+
* @param {NextFunction} next - The next function.
|
|
28
|
+
* @param {Pool} connectionPool - The connection pool.
|
|
29
|
+
* @param {configPlSqlHandlerType} options - the options for the middleware.
|
|
30
|
+
*/
|
|
31
|
+
const requestHandler = async (req, res, next, connectionPool, options) => {
|
|
32
|
+
try {
|
|
33
|
+
// should we switch to the default page if there is one defined
|
|
34
|
+
if (typeof req.params.name !== 'string' || req.params.name.length === 0) {
|
|
35
|
+
if (typeof options.defaultPage === 'string' && options.defaultPage.length > 0) {
|
|
36
|
+
const newUrl = url.resolve(`${req.originalUrl}/${options.defaultPage}`, '');
|
|
37
|
+
debug(`Redirect to the url "${newUrl}"`);
|
|
38
|
+
res.redirect(newUrl);
|
|
39
|
+
} else {
|
|
40
|
+
/* istanbul ignore next */
|
|
41
|
+
errorPage(req, res, options, new RequestError('No procedure name given and no default page has been specified'));
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
await processRequest(req, res, options, connectionPool);
|
|
45
|
+
}
|
|
46
|
+
} catch (err) {
|
|
47
|
+
/* istanbul ignore next */
|
|
48
|
+
errorPage(req, res, options, err);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Express middleware.
|
|
54
|
+
*
|
|
55
|
+
* @param {Pool} connectionPool - The connection pool.
|
|
56
|
+
* @param {configPlSqlHandlerType} config - The configuration options.
|
|
57
|
+
* @returns {RequestHandler} - The handler.
|
|
58
|
+
*/
|
|
59
|
+
export const handlerWebPlSql = (connectionPool, config) => {
|
|
60
|
+
debug('options', config);
|
|
61
|
+
|
|
62
|
+
return (req, res, next) => {
|
|
63
|
+
void requestHandler(req, res, next, connectionPool, config);
|
|
64
|
+
};
|
|
65
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import multer from 'multer';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {import('express').RequestHandler} RequestHandler
|
|
6
|
+
* @typedef {import('express').Response} Response
|
|
7
|
+
* @typedef {import('express').NextFunction} NextFunction
|
|
8
|
+
* @typedef {import('oracledb').Pool} Pool
|
|
9
|
+
* @typedef {import('./types.js').environmentType} environmentType
|
|
10
|
+
* @typedef {import('./types.js').configType} configType
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create the upload middleware.
|
|
15
|
+
* @returns {RequestHandler} - Request handler.
|
|
16
|
+
*/
|
|
17
|
+
export const handlerUpload = () => {
|
|
18
|
+
const upload = multer({
|
|
19
|
+
storage: multer.diskStorage({
|
|
20
|
+
destination: '/tmp/uploads',
|
|
21
|
+
filename: (req, file, cb) => {
|
|
22
|
+
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
|
|
23
|
+
cb(null, `${file.fieldname}-${uniqueSuffix}${path.extname(file.originalname)}`);
|
|
24
|
+
},
|
|
25
|
+
}),
|
|
26
|
+
limits: {
|
|
27
|
+
fileSize: 50 * 1024 * 1024, // 50MB limit
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return upload.any();
|
|
32
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// server
|
|
2
|
+
export {startServer} from './server.js';
|
|
3
|
+
export * from './shutdown.js';
|
|
4
|
+
|
|
5
|
+
// handler
|
|
6
|
+
export {handlerWebPlSql} from './handlerPlSql.js';
|
|
7
|
+
export {handlerLogger} from './handlerLogger.js';
|
|
8
|
+
export {initMetrics, handlerMetrics} from './handlerMetrics.js';
|
|
9
|
+
export {handlerUpload} from './handlerUpload.js';
|
|
10
|
+
|
|
11
|
+
// oracle
|
|
12
|
+
export * from './oracle.js';
|
|
13
|
+
|
|
14
|
+
// util
|
|
15
|
+
export * from './file.js';
|
|
16
|
+
export * from './tty.js';
|
|
17
|
+
export * from './version.js';
|
package/src/oracle.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import debugModule from 'debug';
|
|
2
|
+
const debug = debugModule('webplsql:oracle');
|
|
3
|
+
|
|
4
|
+
import oracledb from 'oracledb';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('oracledb').Pool} Pool
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Test is the connection is valid.
|
|
12
|
+
* @param {Pool} connectionPool - database connection
|
|
13
|
+
* @returns {Promise<boolean>} - success
|
|
14
|
+
*/
|
|
15
|
+
export const connectionValid = async (connectionPool) => {
|
|
16
|
+
let connection;
|
|
17
|
+
try {
|
|
18
|
+
connection = await connectionPool.getConnection();
|
|
19
|
+
debug('connection valid');
|
|
20
|
+
return true;
|
|
21
|
+
} catch (err) {
|
|
22
|
+
debug('connection broken', err);
|
|
23
|
+
return false;
|
|
24
|
+
} finally {
|
|
25
|
+
if (connection) {
|
|
26
|
+
await connection.release();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Allocate the Oracle database pool.
|
|
33
|
+
* @param {string} user - The database user name.
|
|
34
|
+
* @param {string} password - The password of the database user.
|
|
35
|
+
* @param {string} connectString - The Oracle database instance to connect to. The string can be an Easy Connect string, or a Net Service Name from a tnsnames.ora file, or the name of a local Oracle database instance.
|
|
36
|
+
* @param {number} poolMin - The minimum number of connections a connection pool maintains.
|
|
37
|
+
* @param {number} poolMax - The maximum number of connections to which a connection pool can grow.
|
|
38
|
+
* @returns {Promise<Pool>} - The connection pool.
|
|
39
|
+
*/
|
|
40
|
+
export const poolCreate = async (user, password, connectString, poolMin = 10, poolMax = 1000) => {
|
|
41
|
+
const pool = await oracledb.createPool({
|
|
42
|
+
user, // The database user name.
|
|
43
|
+
password, // The password of the database user.
|
|
44
|
+
connectString, // The Oracle database instance to connect to. The string can be an Easy Connect string, or a Net Service Name from a tnsnames.ora file, or the name of a local Oracle database instance.
|
|
45
|
+
poolMin, // The minimum number of connections a connection pool maintains, even when there is no activity to the target database.
|
|
46
|
+
poolMax, // The maximum number of connections to which a connection pool can grow.
|
|
47
|
+
poolIncrement: 10, // The number of connections that are opened whenever a connection request exceeds the number of currently open connections.
|
|
48
|
+
queueTimeout: 1000, // The number of milliseconds after which connection requests waiting in the connection request queue are terminated. If queueTimeout is 0, then queued connection requests are never terminated.
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const valid = await connectionValid(pool);
|
|
52
|
+
if (!valid) {
|
|
53
|
+
await poolClose(pool);
|
|
54
|
+
throw new Error(`Unable to connect with Oracle Database as ${user}@${connectString}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return pool;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Close the Oracle database pool.
|
|
62
|
+
* @param {Pool} pool - The connection pool.
|
|
63
|
+
* @returns {Promise<void>}
|
|
64
|
+
*/
|
|
65
|
+
export const poolClose = async (pool) => {
|
|
66
|
+
try {
|
|
67
|
+
await pool.close(0);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
debug('Cannot close pool', err);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Close the Oracle database pools.
|
|
75
|
+
* @param {Pool[]} pools - The connection pools.
|
|
76
|
+
* @returns {Promise<void>}
|
|
77
|
+
*/
|
|
78
|
+
export const poolsClose = async (pools) => {
|
|
79
|
+
await Promise.all(pools.map(poolClose));
|
|
80
|
+
};
|
package/src/parsePage.js
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {import('./types.js').pageType} pageType
|
|
3
|
+
* @typedef {import('./types.js').cookieType} cookieType
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Parse the header and split it up into the individual components
|
|
8
|
+
*
|
|
9
|
+
* @param {string} text - The text returned from the PL/SQL procedure.
|
|
10
|
+
* @returns {pageType} - The parsed page.
|
|
11
|
+
*/
|
|
12
|
+
export const parsePage = (text) => {
|
|
13
|
+
/** @type {pageType} */
|
|
14
|
+
const page = {
|
|
15
|
+
body: '',
|
|
16
|
+
head: {
|
|
17
|
+
cookies: [],
|
|
18
|
+
otherHeaders: {},
|
|
19
|
+
},
|
|
20
|
+
file: {
|
|
21
|
+
fileType: null,
|
|
22
|
+
fileSize: null,
|
|
23
|
+
fileBlob: null,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
//
|
|
28
|
+
// 1) Split up the text in header and body
|
|
29
|
+
//
|
|
30
|
+
|
|
31
|
+
// Find the end of the header identified by \n\n
|
|
32
|
+
let head = '';
|
|
33
|
+
const headerEndPosition = text.indexOf('\n\n');
|
|
34
|
+
if (headerEndPosition === -1) {
|
|
35
|
+
head = text;
|
|
36
|
+
} else {
|
|
37
|
+
head = text.substring(0, headerEndPosition + 2);
|
|
38
|
+
page.body = text.substring(headerEndPosition + 2);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
//
|
|
42
|
+
// 2) parse the headers
|
|
43
|
+
//
|
|
44
|
+
|
|
45
|
+
head.split('\n').forEach((line) => {
|
|
46
|
+
const header = getHeader(line);
|
|
47
|
+
|
|
48
|
+
if (header) {
|
|
49
|
+
switch (header.name.toLowerCase()) {
|
|
50
|
+
case 'set-cookie':
|
|
51
|
+
{
|
|
52
|
+
const cookie = parseCookie(header.value);
|
|
53
|
+
/* istanbul ignore else */
|
|
54
|
+
if (cookie !== null) {
|
|
55
|
+
page.head.cookies.push(cookie);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
|
|
60
|
+
case 'content-type':
|
|
61
|
+
page.head.contentType = header.value;
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case 'x-db-content-length':
|
|
65
|
+
{
|
|
66
|
+
const contentLength = parseInt(header.value, 10);
|
|
67
|
+
/* istanbul ignore else */
|
|
68
|
+
if (!Number.isNaN(contentLength)) {
|
|
69
|
+
page.head.contentLength = contentLength;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case 'status':
|
|
75
|
+
{
|
|
76
|
+
const statusCode = parseInt(header.value, 10);
|
|
77
|
+
/* istanbul ignore else */
|
|
78
|
+
if (!Number.isNaN(statusCode)) {
|
|
79
|
+
page.head.statusCode = statusCode;
|
|
80
|
+
const index = header.value.indexOf(' ');
|
|
81
|
+
/* istanbul ignore else */
|
|
82
|
+
if (index !== -1) {
|
|
83
|
+
page.head.statusDescription = header.value.substring(index + 1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
|
|
89
|
+
case 'location':
|
|
90
|
+
page.head.redirectLocation = header.value;
|
|
91
|
+
break;
|
|
92
|
+
|
|
93
|
+
case 'x-oracle-ignore':
|
|
94
|
+
break;
|
|
95
|
+
|
|
96
|
+
default:
|
|
97
|
+
page.head.otherHeaders[header.name] = header.value;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
return page;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get a header line
|
|
108
|
+
* @param {string} line - The line
|
|
109
|
+
* @returns {{name: string, value: string} | null} - The header.
|
|
110
|
+
*/
|
|
111
|
+
const getHeader = (line) => {
|
|
112
|
+
const index = line.indexOf(':');
|
|
113
|
+
|
|
114
|
+
if (index !== -1) {
|
|
115
|
+
return {
|
|
116
|
+
name: line.substring(0, index).trim(),
|
|
117
|
+
value: line.substring(index + 1).trim(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return null;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Parses a cookie string
|
|
126
|
+
* @param {string} text - The cookie string.
|
|
127
|
+
* @returns {cookieType | null} - The parsed cookie.
|
|
128
|
+
*/
|
|
129
|
+
const parseCookie = (text) => {
|
|
130
|
+
// validate
|
|
131
|
+
/* istanbul ignore next */
|
|
132
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// split the cookie into it's parts
|
|
137
|
+
let cookieElements = text.split(';');
|
|
138
|
+
|
|
139
|
+
// trim cookie elements
|
|
140
|
+
cookieElements = cookieElements.map((element) => element.trim());
|
|
141
|
+
|
|
142
|
+
// get name and value
|
|
143
|
+
const index = cookieElements[0].indexOf('=');
|
|
144
|
+
/* istanbul ignore next */
|
|
145
|
+
if (index <= 0) {
|
|
146
|
+
// if the index is -1, there is no equal sign and if it's 0 the name is empty
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** @type {cookieType} */
|
|
151
|
+
const cookie = {
|
|
152
|
+
name: cookieElements[0].substring(0, index).trim(),
|
|
153
|
+
value: cookieElements[0].substring(index + 1).trim(),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// remove the fisrt element
|
|
157
|
+
cookieElements.shift();
|
|
158
|
+
|
|
159
|
+
// get the other options
|
|
160
|
+
cookieElements.forEach((element) => {
|
|
161
|
+
if (element.startsWith('path=')) {
|
|
162
|
+
cookie.path = element.substring(5);
|
|
163
|
+
} else if (element.toLowerCase().startsWith('domain=')) {
|
|
164
|
+
cookie.domain = element.substring(7);
|
|
165
|
+
} else if (element.toLowerCase().startsWith('secure=')) {
|
|
166
|
+
/* istanbul ignore next */
|
|
167
|
+
cookie.secure = element.substring(7);
|
|
168
|
+
} else if (element.toLowerCase().startsWith('expires=')) {
|
|
169
|
+
const date = tryDecodeDate(element.substring(8));
|
|
170
|
+
if (date) {
|
|
171
|
+
cookie.expires = date;
|
|
172
|
+
}
|
|
173
|
+
} else if (element.toLowerCase().startsWith('httponly')) {
|
|
174
|
+
cookie.httpOnly = true;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return cookie;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Try to decode a date
|
|
183
|
+
* @param {string} value - The value to decode.
|
|
184
|
+
* @returns {Date | null} - The decoded date or null.
|
|
185
|
+
*/
|
|
186
|
+
const tryDecodeDate = (value) => {
|
|
187
|
+
try {
|
|
188
|
+
return new Date(value);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
/* istanbul ignore next */
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
};
|