Fix url problems (#1679)

* Fix Uri errors
This commit is contained in:
Rafael Saes 2022-07-12 16:58:18 -03:00 committed by GitHub
parent 68a4697c7d
commit 68ceb7f440
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 78 additions and 32 deletions

View file

@ -13,6 +13,8 @@ declare type LbryUrlObj = {
primaryBidPosition?: number, primaryBidPosition?: number,
secondaryBidPosition?: number, secondaryBidPosition?: number,
startTime?: number, startTime?: number,
queryString?: string,
pathHash?: string,
// Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url // Below are considered deprecated and should not be used due to unreliableness with claim.canonical_url
claimName?: string, claimName?: string,

View file

@ -5,7 +5,7 @@ import { lazyImport } from 'util/lazyImport';
import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus'; import { tusUnlockAndNotify, tusHandleTabUpdates } from 'util/tus';
import analytics from 'analytics'; import analytics from 'analytics';
import { setSearchUserId } from 'redux/actions/search'; import { setSearchUserId } from 'redux/actions/search';
import { normalizeURI } from 'util/lbryURI'; import { parseURI, buildURI } from 'util/lbryURI';
import { generateGoogleCacheUrl } from 'util/url'; import { generateGoogleCacheUrl } from 'util/url';
import Router from 'component/router/index'; import Router from 'component/router/index';
import ModalRouter from 'modal/modalRouter'; import ModalRouter from 'modal/modalRouter';
@ -187,7 +187,10 @@ function App(props: Props) {
let uri; let uri;
try { try {
uri = normalizeURI(path); // here queryString and startTime are "removed" from the buildURI process
// to build only the uri itself
const { queryString, startTime, ...parsedUri } = parseURI(path);
uri = buildURI({ ...parsedUri });
} catch (e) { } catch (e) {
const match = path.match(/[#/:]/); const match = path.match(/[#/:]/);

View file

@ -64,7 +64,7 @@ function Page(props: Props) {
} = props; } = props;
const { const {
location: { pathname }, location: { pathname, hash },
} = useHistory(); } = useHistory();
const theaterMode = renderMode === 'video' || renderMode === 'audio' ? videoTheaterMode : false; const theaterMode = renderMode === 'video' || renderMode === 'audio' ? videoTheaterMode : false;
@ -73,11 +73,10 @@ function Page(props: Props) {
const isLandscapeRotated = useIsMobileLandscape(); const isLandscapeRotated = useIsMobileLandscape();
const [sidebarOpen, setSidebarOpen] = usePersistedState('sidebar', false); const [sidebarOpen, setSidebarOpen] = usePersistedState('sidebar', false);
const url = pathname.slice(1).replace(/:/g, '#'); const urlPath = `lbry://${(pathname + hash).slice(1).replace(/:/g, '#')}`;
let isOnFilePage = false; let isOnFilePage = false;
try { try {
const url = pathname.slice(1).replace(/:/g, '#'); const { isChannel } = parseURI(urlPath);
const { isChannel } = parseURI(url);
if (!isChannel) isOnFilePage = true; if (!isChannel) isOnFilePage = true;
} catch (e) {} } catch (e) {}
@ -93,7 +92,7 @@ function Page(props: Props) {
return ( return (
<> <>
<Wallpaper uri={url} /> <Wallpaper uri={urlPath} />
{!noHeader && ( {!noHeader && (
<Header <Header
authHeader={authPage} authHeader={authPage}

View file

@ -31,18 +31,26 @@ import { doClaimEligiblePurchaseRewards } from 'redux/actions/rewards';
import { selectDaemonSettings, selectClientSetting, selectHomepageData } from 'redux/selectors/settings'; import { selectDaemonSettings, selectClientSetting, selectHomepageData } from 'redux/selectors/settings';
import { toggleVideoTheaterMode, toggleAutoplayNext, doSetClientSetting } from 'redux/actions/settings'; import { toggleVideoTheaterMode, toggleAutoplayNext, doSetClientSetting } from 'redux/actions/settings';
import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user'; import { selectUserVerifiedEmail, selectUser } from 'redux/selectors/user';
import { parseURI } from 'util/lbryURI';
import { doToast } from 'redux/actions/notifications'; import { doToast } from 'redux/actions/notifications';
const select = (state, props) => { const select = (state, props) => {
const { search } = props.location; const { search, pathname, hash } = props.location;
const urlParams = new URLSearchParams(search); const urlParams = new URLSearchParams(search);
const autoplay = urlParams.get('autoplay'); const autoplay = urlParams.get('autoplay');
const uri = props.uri; const uri = props.uri;
const urlPath = `lbry://${(pathname + hash).slice(1)}`;
let startTime;
try {
({ startTime } = parseURI(urlPath));
} catch (e) {}
const claim = selectClaimForUri(state, uri); const claim = selectClaimForUri(state, uri);
// TODO: eventually this should be received from DB and not local state (https://github.com/lbryio/lbry-desktop/issues/6796) // TODO: eventually this should be received from DB and not local state (https://github.com/lbryio/lbry-desktop/issues/6796)
const position = urlParams.get('t') !== null ? urlParams.get('t') : selectContentPositionForUri(state, uri); const position =
startTime || (urlParams.get('t') !== null ? urlParams.get('t') : selectContentPositionForUri(state, uri));
const userId = selectUser(state) && selectUser(state).id; const userId = selectUser(state) && selectUser(state).id;
const internalFeature = selectUser(state) && selectUser(state).internal_feature; const internalFeature = selectUser(state) && selectUser(state).internal_feature;
const playingUri = selectPlayingUri(state); const playingUri = selectPlayingUri(state);

View file

@ -137,29 +137,37 @@ export default function ShowPage(props: Props) {
useEffect(() => { useEffect(() => {
if (canonicalUrl) { if (canonicalUrl) {
const urlPath = pathname + hash; const statePos =
const fullParams = hash.indexOf('#state') > -1
urlPath.indexOf('?') > 0 ? urlPath.substring(urlPath.indexOf('?')) : search.length > 0 ? search : ''; ? hash.indexOf('#state')
const canonicalUrlPath = '/' + canonicalUrl.replace(/^lbry:\/\//, '').replace(/#/g, ':') + fullParams; : hash.indexOf('&state') > -1
? hash.indexOf('&state')
: undefined;
const pageHash = statePos === undefined ? hash : hash.substring(0, statePos);
const urlPath = pathname + pageHash;
const path = urlPath.slice(1).replace(/:/g, '#');
// parseURI can parse queries and hashes when they are mixed with the uri
let queryString, pathHash;
try {
({ queryString, pathHash } = parseURI(path));
} catch (e) {}
const canonicalUrlPath = '/' + canonicalUrl.replace(/^lbry:\/\//, '').replace(/#/g, ':');
// replaceState will fail if on a different domain (like webcache.googleusercontent.com) // replaceState will fail if on a different domain (like webcache.googleusercontent.com)
const hostname = isDev ? 'localhost' : DOMAIN; const hostname = isDev ? 'localhost' : DOMAIN;
if (canonicalUrlPath !== pathname && hostname === window.location.hostname && fullParams !== search) { let replaceUrl = canonicalUrlPath;
const urlParams = new URLSearchParams(search); if (canonicalUrlPath !== urlPath && hostname === window.location.hostname) {
let replaceUrl = canonicalUrlPath; const urlParams = new URLSearchParams(search || queryString);
if (urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID)) { if (urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID)) {
const listId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID) || ''; const listId = urlParams.get(COLLECTIONS_CONSTS.COLLECTION_ID) || '';
urlParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, listId); urlParams.set(COLLECTIONS_CONSTS.COLLECTION_ID, listId);
replaceUrl += `?${urlParams.toString()}`;
} }
history.replaceState(history.state, '', replaceUrl);
}
const windowHref = window.location.href; if (urlParams.toString()) replaceUrl += `?${urlParams.toString()}`;
const noUrlParams = search.length === 0; if (pathHash || (!pathHash && !queryString && pageHash)) replaceUrl += String(pathHash || pageHash);
if (windowHref.includes('?') && noUrlParams) {
history.replaceState(history.state, '', windowHref.substring(0, windowHref.length - 1)); history.replaceState(history.state, '', replaceUrl);
} }
} }
}, [canonicalUrl, pathname, hash, search]); }, [canonicalUrl, pathname, hash, search]);

View file

@ -103,17 +103,18 @@ export function parseURI(url: string, requireProto: boolean = false): LbryUrlObj
} }
// Validate and process modifier // Validate and process modifier
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier( const [primaryClaimId, primaryClaimSequence, primaryBidPosition, primaryPathHash] = parseURIModifier(
primaryModSeparator, primaryModSeparator,
primaryModValue primaryModValue
); );
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier( const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition, secondaryPathHash] = parseURIModifier(
secondaryModSeparator, secondaryModSeparator,
secondaryModValue secondaryModValue
); );
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName; const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId; const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
const channelClaimId = includesChannel && primaryClaimId; const channelClaimId = includesChannel && primaryClaimId;
const pathHash = primaryPathHash || secondaryPathHash;
return { return {
isChannel, isChannel,
@ -127,6 +128,7 @@ export function parseURI(url: string, requireProto: boolean = false): LbryUrlObj
...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}), ...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}),
...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}), ...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}),
...(startTime ? { startTime: parseInt(startTime, 10) } : {}), ...(startTime ? { startTime: parseInt(startTime, 10) } : {}),
...(pathHash ? { pathHash } : {}),
// The values below should not be used for new uses of parseURI // The values below should not be used for new uses of parseURI
// They will not work properly with canonical_urls // They will not work properly with canonical_urls
@ -141,6 +143,7 @@ function parseURIModifier(modSeperator: ?string, modValue: ?string) {
let claimId; let claimId;
let claimSequence; let claimSequence;
let bidPosition; let bidPosition;
let pathHash;
if (modSeperator) { if (modSeperator) {
if (!modValue) { if (!modValue) {
@ -157,7 +160,17 @@ function parseURIModifier(modSeperator: ?string, modValue: ?string) {
} }
if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) { if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) {
throw new Error(__(`Invalid claim ID %claimId%.`, { claimId })); const hashIndex = claimId.indexOf('#');
if (hashIndex >= 0) {
pathHash = claimId.substring(hashIndex);
claimId = claimId.substring(0, hashIndex);
// As a pre-caution to catch future odd urls coming in,
// validate the new claimId length and characters again after stripping off the pathHash
[claimId] = parseURIModifier(modSeperator, claimId);
} else {
throw new Error(__(`Invalid claim ID %claimId%.`, { claimId }));
}
} }
if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) { if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
@ -168,7 +181,7 @@ function parseURIModifier(modSeperator: ?string, modValue: ?string) {
throw new Error(__('Bid position must be a number.')); throw new Error(__('Bid position must be a number.'));
} }
return [claimId, claimSequence, bidPosition]; return [claimId, claimSequence, bidPosition, pathHash];
} }
const errorHistory = []; const errorHistory = [];

View file

@ -205,7 +205,7 @@ async function buildClaimOgMetadata(uri, claim, overrideOptions = {}, referrerQu
const title = overrideOptions.title || claimTitle; const title = overrideOptions.title || claimTitle;
const description = overrideOptions.description || claimDescription; const description = overrideOptions.description || claimDescription;
const cleanDescription = removeMd(description); const cleanDescription = removeMd(description);
const claimPath = `${URL}/${claim.canonical_url.replace('lbry://', '').replace('#', ':')}`; const claimPath = `${URL}/${claim.canonical_url.replace('lbry://', '').replace(/#/g, ':')}`;
let head = ''; let head = '';

View file

@ -108,17 +108,18 @@ function parseURI(url, requireProto = false) {
} }
// Validate and process modifier // Validate and process modifier
const [primaryClaimId, primaryClaimSequence, primaryBidPosition] = parseURIModifier( const [primaryClaimId, primaryClaimSequence, primaryBidPosition, primaryPathHash] = parseURIModifier(
primaryModSeparator, primaryModSeparator,
primaryModValue primaryModValue
); );
const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition] = parseURIModifier( const [secondaryClaimId, secondaryClaimSequence, secondaryBidPosition, secondaryPathHash] = parseURIModifier(
secondaryModSeparator, secondaryModSeparator,
secondaryModValue secondaryModValue
); );
const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName; const streamName = includesChannel ? possibleStreamName : streamNameOrChannelName;
const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId; const streamClaimId = includesChannel ? secondaryClaimId : primaryClaimId;
const channelClaimId = includesChannel && primaryClaimId; const channelClaimId = includesChannel && primaryClaimId;
const pathHash = primaryPathHash || secondaryPathHash;
return { return {
isChannel, isChannel,
@ -132,6 +133,7 @@ function parseURI(url, requireProto = false) {
...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}), ...(primaryBidPosition ? { primaryBidPosition: parseInt(primaryBidPosition, 10) } : {}),
...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}), ...(secondaryBidPosition ? { secondaryBidPosition: parseInt(secondaryBidPosition, 10) } : {}),
...(startTime ? { startTime: parseInt(startTime, 10) } : {}), ...(startTime ? { startTime: parseInt(startTime, 10) } : {}),
...(pathHash ? { pathHash } : {}),
// The values below should not be used for new uses of parseURI // The values below should not be used for new uses of parseURI
// They will not work properly with canonical_urls // They will not work properly with canonical_urls
@ -146,6 +148,7 @@ function parseURIModifier(modSeperator, modValue) {
let claimId; let claimId;
let claimSequence; let claimSequence;
let bidPosition; let bidPosition;
let pathHash;
if (modSeperator) { if (modSeperator) {
if (!modValue) { if (!modValue) {
@ -162,7 +165,17 @@ function parseURIModifier(modSeperator, modValue) {
} }
if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) { if (claimId && (claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/))) {
throw new Error(__(`Invalid claim ID ${claimId}.`, { claimId })); const hashIndex = claimId.indexOf('#');
if (hashIndex >= 0) {
pathHash = claimId.substring(hashIndex);
claimId = claimId.substring(0, hashIndex);
// As a pre-caution to catch future odd urls coming in,
// validate the new claimId length and characters again after stripping off the pathHash
[claimId] = parseURIModifier(modSeperator, claimId);
} else {
throw new Error(__(`Invalid claim ID %claimId%.`, { claimId }));
}
} }
if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) { if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
@ -173,7 +186,7 @@ function parseURIModifier(modSeperator, modValue) {
throw new Error(__('Bid position must be a number.')); throw new Error(__('Bid position must be a number.'));
} }
return [claimId, claimSequence, bidPosition]; return [claimId, claimSequence, bidPosition, pathHash];
} }
/** /**