added regex url parsing
This commit is contained in:
parent
4f8cdc06b0
commit
87d026b5ec
1 changed files with 124 additions and 61 deletions
|
@ -7,7 +7,6 @@ const db = require('../models');
|
||||||
const SERVE = 'SERVE';
|
const SERVE = 'SERVE';
|
||||||
const SHOW = 'SHOW';
|
const SHOW = 'SHOW';
|
||||||
const SHOWLITE = 'SHOWLITE';
|
const SHOWLITE = 'SHOWLITE';
|
||||||
const CLAIM_ID_CHAR = ':';
|
|
||||||
const CHANNEL_CHAR = '@';
|
const CHANNEL_CHAR = '@';
|
||||||
const CLAIMS_PER_PAGE = 10;
|
const CLAIMS_PER_PAGE = 10;
|
||||||
const NO_CHANNEL = 'NO_CHANNEL';
|
const NO_CHANNEL = 'NO_CHANNEL';
|
||||||
|
@ -26,24 +25,6 @@ function isValidShortIdOrClaimId (input) {
|
||||||
return (isValidClaimId(input) || isValidShortId(input));
|
return (isValidClaimId(input) || isValidShortId(input));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isUriAChannel (name) {
|
|
||||||
return (name.charAt(0) === CHANNEL_CHAR);
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnChannelNameFromUri (uri) {
|
|
||||||
if (uri.indexOf(CLAIM_ID_CHAR) !== -1) {
|
|
||||||
return uri.substring(0, uri.indexOf(CLAIM_ID_CHAR));
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnChannelIdFromUri (uri) {
|
|
||||||
if (uri.indexOf(CLAIM_ID_CHAR) !== -1) {
|
|
||||||
return uri.substring(uri.indexOf(CLAIM_ID_CHAR) + 1);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPage (query) {
|
function getPage (query) {
|
||||||
if (query.p) {
|
if (query.p) {
|
||||||
return parseInt(query.p);
|
return parseInt(query.p);
|
||||||
|
@ -128,11 +109,7 @@ function sendChannelInfoAndContentToClient (result, query, res) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showChannelPageToClient (uri, originalUrl, ip, query, res) {
|
function showChannelPageToClient (channelName, channelClaimId, originalUrl, ip, query, res) {
|
||||||
let channelName = returnChannelNameFromUri(uri);
|
|
||||||
logger.debug('channel name =', channelName);
|
|
||||||
let channelClaimId = returnChannelIdFromUri(uri);
|
|
||||||
logger.debug('channel Id =', channelClaimId);
|
|
||||||
// 1. retrieve the channel contents
|
// 1. retrieve the channel contents
|
||||||
getChannelInfoAndContent(channelName, channelClaimId)
|
getChannelInfoAndContent(channelName, channelClaimId)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
|
@ -143,17 +120,13 @@ function showChannelPageToClient (uri, originalUrl, ip, query, res) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function characterExistsInString (character, string) {
|
|
||||||
return (string.indexOf(character) !== -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clientAcceptsHtml (headers) {
|
function clientAcceptsHtml (headers) {
|
||||||
return headers['accept'] && headers['accept'].split(',').includes('text/html');
|
return headers['accept'] && headers['accept'].split(',').includes('text/html');
|
||||||
}
|
}
|
||||||
|
|
||||||
function determineResponseType (uri, headers) {
|
function determineResponseType (isServeRequest, headers) {
|
||||||
let responseType;
|
let responseType;
|
||||||
if (characterExistsInString('.', uri)) {
|
if (isServeRequest) {
|
||||||
responseType = SERVE;
|
responseType = SERVE;
|
||||||
if (clientAcceptsHtml(headers)) { // this is in case a serve request comes from a browser
|
if (clientAcceptsHtml(headers)) { // this is in case a serve request comes from a browser
|
||||||
responseType = SHOWLITE;
|
responseType = SHOWLITE;
|
||||||
|
@ -167,18 +140,6 @@ function determineResponseType (uri, headers) {
|
||||||
return responseType;
|
return responseType;
|
||||||
}
|
}
|
||||||
|
|
||||||
function determineName (uri) {
|
|
||||||
/* patch because twitter player preview adds '>' before file extension. */
|
|
||||||
if (characterExistsInString('>', uri)) {
|
|
||||||
return uri.substring(0, uri.indexOf('>'));
|
|
||||||
}
|
|
||||||
/* end patch */
|
|
||||||
if (characterExistsInString('.', uri)) {
|
|
||||||
return uri.substring(0, uri.indexOf('.'));
|
|
||||||
}
|
|
||||||
return uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showAssetToClient (claimId, name, res) {
|
function showAssetToClient (claimId, name, res) {
|
||||||
return Promise
|
return Promise
|
||||||
.all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)])
|
.all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)])
|
||||||
|
@ -250,23 +211,110 @@ function logRequestData (responseType, claimName, channelName, claimId) {
|
||||||
logger.debug('claim id ===', claimId);
|
logger.debug('claim id ===', claimId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const lbryuri = {};
|
||||||
|
|
||||||
|
lbryuri.REGEXP_INVALID_URI = /[^A-Za-z0-9-]/g;
|
||||||
|
lbryuri.REGEXP_ADDRESS = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||||
|
|
||||||
|
lbryuri.parseIdentifier = function (identifier) {
|
||||||
|
logger.debug('parsing identifier:', identifier);
|
||||||
|
const componentsRegex = new RegExp(
|
||||||
|
'([^:$#/]*)' + // value (stops at the first separator or end)
|
||||||
|
'([:$#]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
|
||||||
|
);
|
||||||
|
const [proto, value, modifierSeperator, modifier] = componentsRegex
|
||||||
|
.exec(identifier)
|
||||||
|
.map(match => match || null);
|
||||||
|
logger.debug(`${proto}, ${value}, ${modifierSeperator}, ${modifier}`);
|
||||||
|
|
||||||
|
// Validate and process name
|
||||||
|
const isChannel = value.startsWith(CHANNEL_CHAR);
|
||||||
|
const channelName = isChannel ? value : null;
|
||||||
|
let claimId;
|
||||||
|
if (isChannel) {
|
||||||
|
if (!channelName) {
|
||||||
|
throw new Error('No channel name after @.');
|
||||||
|
}
|
||||||
|
const nameBadChars = (channelName).match(lbryuri.REGEXP_INVALID_URI);
|
||||||
|
if (nameBadChars) {
|
||||||
|
throw new Error(`Invalid characters in channel name: ${nameBadChars.join(', ')}.`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
claimId = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate and process modifier
|
||||||
|
let channelClaimId;
|
||||||
|
if (modifierSeperator) {
|
||||||
|
if (!modifier) {
|
||||||
|
throw new Error(`No modifier provided after separator ${modifierSeperator}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (modifierSeperator === ':') {
|
||||||
|
channelClaimId = modifier;
|
||||||
|
} else {
|
||||||
|
throw new Error(`The ${modifierSeperator} modifier is not currently supported.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
isChannel,
|
||||||
|
channelName,
|
||||||
|
channelClaimId,
|
||||||
|
claimId,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
lbryuri.parseName = function (name) {
|
||||||
|
logger.debug('parsing name:', name);
|
||||||
|
const componentsRegex = new RegExp(
|
||||||
|
'([^:$#/.]*)' + // name (stops at the first modifier)
|
||||||
|
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
|
||||||
|
);
|
||||||
|
const [proto, claimName, modifierSeperator, modifier] = componentsRegex
|
||||||
|
.exec(name)
|
||||||
|
.map(match => match || null);
|
||||||
|
logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`);
|
||||||
|
|
||||||
|
// Validate and process name
|
||||||
|
if (!claimName) {
|
||||||
|
throw new Error('No claim name provided before .');
|
||||||
|
}
|
||||||
|
const nameBadChars = (claimName).match(lbryuri.REGEXP_INVALID_URI);
|
||||||
|
if (nameBadChars) {
|
||||||
|
throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`);
|
||||||
|
}
|
||||||
|
// Validate and process modifier
|
||||||
|
let isServeRequest = false;
|
||||||
|
if (modifierSeperator) {
|
||||||
|
if (!modifier) {
|
||||||
|
throw new Error(`No file extension provided after separator ${modifierSeperator}.`);
|
||||||
|
}
|
||||||
|
if (modifierSeperator !== '.') {
|
||||||
|
throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`);
|
||||||
|
}
|
||||||
|
isServeRequest = true;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
claimName,
|
||||||
|
isServeRequest,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = (app) => {
|
module.exports = (app) => {
|
||||||
// route to serve a specific asset using the channel or claim id
|
// route to serve a specific asset using the channel or claim id
|
||||||
app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => {
|
app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => {
|
||||||
let identifier = params.identifier; // '@channel', '@channel:channelClaimId', or 'claimId'
|
let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest;
|
||||||
let name = params.name; // 'example' or 'example.ext'
|
try {
|
||||||
[identifier, name] = flipClaimNameAndIdForBackwardsCompatibility(identifier, name);
|
({ isChannel, channelName, channelClaimId, claimId } = lbryuri.parseIdentifier(params.identifier));
|
||||||
let channelName = null;
|
({ claimName, isServeRequest } = lbryuri.parseName(params.name));
|
||||||
let claimId = null;
|
} catch (error) {
|
||||||
let channelClaimId = null;
|
logger.error(error);
|
||||||
let responseType = determineResponseType(name, headers);
|
return res.status(400).json({success: false, message: error});
|
||||||
let claimName = determineName(name);
|
|
||||||
if (isUriAChannel(identifier)) {
|
|
||||||
channelName = returnChannelNameFromUri(identifier);
|
|
||||||
channelClaimId = returnChannelIdFromUri(identifier);
|
|
||||||
} else {
|
|
||||||
claimId = identifier;
|
|
||||||
}
|
}
|
||||||
|
if (!isChannel) {
|
||||||
|
[claimId, claimName] = flipClaimNameAndIdForBackwardsCompatibility(claimId, claimName);
|
||||||
|
}
|
||||||
|
let responseType = determineResponseType(isServeRequest, headers);
|
||||||
// log the request data for debugging
|
// log the request data for debugging
|
||||||
logRequestData(responseType, claimName, channelName, claimId);
|
logRequestData(responseType, claimName, channelName, claimId);
|
||||||
// get the claim Id and then serve/show the asset
|
// get the claim Id and then serve/show the asset
|
||||||
|
@ -284,13 +332,28 @@ module.exports = (app) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
// route to serve the winning asset at a claim or a channel page
|
// route to serve the winning asset at a claim or a channel page
|
||||||
app.get('/:uri', ({ headers, ip, originalUrl, params, query }, res) => {
|
app.get('/:identifier', ({ headers, ip, originalUrl, params, query }, res) => {
|
||||||
let uri = params.uri;
|
let isChannel, channelName, channelClaimId;
|
||||||
if (isUriAChannel(uri)) {
|
try {
|
||||||
showChannelPageToClient(uri, originalUrl, ip, query, res);
|
({ isChannel, channelName, channelClaimId } = lbryuri.parseIdentifier(params.identifier));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return res.status(400).json({success: false, message: error});
|
||||||
|
}
|
||||||
|
if (isChannel) {
|
||||||
|
// log the request data for debugging
|
||||||
|
logRequestData(null, null, channelName, null);
|
||||||
|
// handle showing the channel page
|
||||||
|
showChannelPageToClient(channelName, channelClaimId, originalUrl, ip, query, res);
|
||||||
} else {
|
} else {
|
||||||
let responseType = determineResponseType(uri, headers);
|
let claimName, isServeRequest;
|
||||||
let claimName = determineName(uri);
|
try {
|
||||||
|
({claimName, isServeRequest} = lbryuri.parseName(params.identifier));
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error);
|
||||||
|
return res.status(400).json({success: false, message: error});
|
||||||
|
}
|
||||||
|
let responseType = determineResponseType(isServeRequest, headers);
|
||||||
// log the request data for debugging
|
// log the request data for debugging
|
||||||
logRequestData(responseType, claimName, null, null);
|
logRequestData(responseType, claimName, null, null);
|
||||||
// get the claim Id and then serve/show the asset
|
// get the claim Id and then serve/show the asset
|
||||||
|
|
Loading…
Reference in a new issue