UNPKG

web_plsql

Version:

The Express Middleware for Oracle PL/SQL

194 lines (168 loc) 4.36 kB
/** * @typedef {import('./types.js').pageType} pageType * @typedef {import('./types.js').cookieType} cookieType */ /** * Parse the header and split it up into the individual components * * @param {string} text - The text returned from the PL/SQL procedure. * @returns {pageType} - The parsed page. */ export const parsePage = (text) => { /** @type {pageType} */ const page = { body: '', head: { cookies: [], otherHeaders: {}, }, file: { fileType: null, fileSize: null, fileBlob: null, }, }; // // 1) Split up the text in header and body // // Find the end of the header identified by \n\n let head = ''; const headerEndPosition = text.indexOf('\n\n'); if (headerEndPosition === -1) { head = text; } else { head = text.substring(0, headerEndPosition + 2); page.body = text.substring(headerEndPosition + 2); } // // 2) parse the headers // head.split('\n').forEach((line) => { const header = getHeader(line); if (header) { switch (header.name.toLowerCase()) { case 'set-cookie': { const cookie = parseCookie(header.value); /* istanbul ignore else */ if (cookie !== null) { page.head.cookies.push(cookie); } } break; case 'content-type': page.head.contentType = header.value; break; case 'x-db-content-length': { const contentLength = parseInt(header.value, 10); /* istanbul ignore else */ if (!Number.isNaN(contentLength)) { page.head.contentLength = contentLength; } } break; case 'status': { const statusCode = parseInt(header.value, 10); /* istanbul ignore else */ if (!Number.isNaN(statusCode)) { page.head.statusCode = statusCode; const index = header.value.indexOf(' '); /* istanbul ignore else */ if (index !== -1) { page.head.statusDescription = header.value.substring(index + 1); } } } break; case 'location': page.head.redirectLocation = header.value; break; case 'x-oracle-ignore': break; default: page.head.otherHeaders[header.name] = header.value; break; } } }); return page; }; /** * Get a header line * @param {string} line - The line * @returns {{name: string, value: string} | null} - The header. */ const getHeader = (line) => { const index = line.indexOf(':'); if (index !== -1) { return { name: line.substring(0, index).trim(), value: line.substring(index + 1).trim(), }; } return null; }; /** * Parses a cookie string * @param {string} text - The cookie string. * @returns {cookieType | null} - The parsed cookie. */ const parseCookie = (text) => { // validate /* istanbul ignore next */ if (typeof text !== 'string' || text.trim().length === 0) { return null; } // split the cookie into it's parts let cookieElements = text.split(';'); // trim cookie elements cookieElements = cookieElements.map((element) => element.trim()); // get name and value const index = cookieElements[0].indexOf('='); /* istanbul ignore next */ if (index <= 0) { // if the index is -1, there is no equal sign and if it's 0 the name is empty return null; } /** @type {cookieType} */ const cookie = { name: cookieElements[0].substring(0, index).trim(), value: cookieElements[0].substring(index + 1).trim(), }; // remove the fisrt element cookieElements.shift(); // get the other options cookieElements.forEach((element) => { if (element.startsWith('path=')) { cookie.path = element.substring(5); } else if (element.toLowerCase().startsWith('domain=')) { cookie.domain = element.substring(7); } else if (element.toLowerCase().startsWith('secure=')) { /* istanbul ignore next */ cookie.secure = element.substring(7); } else if (element.toLowerCase().startsWith('expires=')) { const date = tryDecodeDate(element.substring(8)); if (date) { cookie.expires = date; } } else if (element.toLowerCase().startsWith('httponly')) { cookie.httpOnly = true; } }); return cookie; }; /** * Try to decode a date * @param {string} value - The value to decode. * @returns {Date | null} - The decoded date or null. */ const tryDecodeDate = (value) => { try { return new Date(value); } catch (err) { /* istanbul ignore next */ return null; } };