]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - client/src/assets/player/shared/p2p-media-loader/segment-validator.ts
Fix token injection if unlogged user
[github/Chocobozzz/PeerTube.git] / client / src / assets / player / shared / p2p-media-loader / segment-validator.ts
CommitLineData
09209296 1import { basename } from 'path'
42b40636
C
2import { Segment } from '@peertube/p2p-media-loader-core'
3import { logger } from '@root-helpers/logger'
4import { wait } from '@root-helpers/utils'
3545e72c 5import { isSameOrigin } from '../common'
09209296 6
c6c0fa6c
C
7type SegmentsJSON = { [filename: string]: string | { [byterange: string]: string } }
8
210856a7
C
9const maxRetries = 3
10
3545e72c
C
11function segmentValidatorFactory (options: {
12 serverUrl: string
13 segmentsSha256Url: string
14 isLive: boolean
15 authorizationHeader: () => string
16 requiresAuth: boolean
17}) {
18 const { serverUrl, segmentsSha256Url, isLive, authorizationHeader, requiresAuth } = options
19
20 let segmentsJSON = fetchSha256Segments({ serverUrl, segmentsSha256Url, authorizationHeader, requiresAuth })
4c280004 21 const regex = /bytes=(\d+)-(\d+)/
09209296 22
501af82d 23 return async function segmentValidator (segment: Segment, _method: string, _peerId: string, retry = 1) {
25b7c847
C
24 // Wait for hash generation from the server
25 if (isLive) await wait(1000)
26
4c280004 27 const filename = basename(segment.url)
09209296 28
c6c0fa6c
C
29 const segmentValue = (await segmentsJSON)[filename]
30
210856a7 31 if (!segmentValue && retry > maxRetries) {
c6c0fa6c
C
32 throw new Error(`Unknown segment name ${filename} in segment validator`)
33 }
34
35 if (!segmentValue) {
42b40636 36 logger.info(`Refetching sha segments for ${filename}`)
c6c0fa6c 37
25b7c847
C
38 await wait(1000)
39
3545e72c 40 segmentsJSON = fetchSha256Segments({ serverUrl, segmentsSha256Url, authorizationHeader, requiresAuth })
501af82d 41 await segmentValidator(segment, _method, _peerId, retry + 1)
210856a7 42
c6c0fa6c
C
43 return
44 }
45
46 let hashShouldBe: string
47 let range = ''
48
49 if (typeof segmentValue === 'string') {
50 hashShouldBe = segmentValue
51 } else {
52 const captured = regex.exec(segment.range)
53 range = captured[1] + '-' + captured[2]
54
55 hashShouldBe = segmentValue[range]
56 }
4c280004 57
09209296 58 if (hashShouldBe === undefined) {
4c280004 59 throw new Error(`Unknown segment name ${filename}/${range} in segment validator`)
09209296
C
60 }
61
6d61da4e 62 const calculatedSha = await sha256Hex(segment.data)
09209296 63 if (calculatedSha !== hashShouldBe) {
4c280004
C
64 throw new Error(
65 `Hashes does not correspond for segment ${filename}/${range}` +
66 `(expected: ${hashShouldBe} instead of ${calculatedSha})`
67 )
09209296
C
68 }
69 }
70}
71
72// ---------------------------------------------------------------------------
73
74export {
75 segmentValidatorFactory
76}
77
78// ---------------------------------------------------------------------------
79
3545e72c
C
80function fetchSha256Segments (options: {
81 serverUrl: string
82 segmentsSha256Url: string
83 authorizationHeader: () => string
84 requiresAuth: boolean
85}) {
86 const { serverUrl, segmentsSha256Url, requiresAuth, authorizationHeader } = options
87
88 const headers = requiresAuth && isSameOrigin(serverUrl, segmentsSha256Url)
89 ? { Authorization: authorizationHeader() }
90 : {}
91
92 return fetch(segmentsSha256Url, { headers })
c6c0fa6c 93 .then(res => res.json() as Promise<SegmentsJSON>)
09209296 94 .catch(err => {
42b40636 95 logger.error('Cannot get sha256 segments', err)
09209296
C
96 return {}
97 })
98}
99
6d61da4e 100async function sha256Hex (data?: ArrayBuffer) {
09209296
C
101 if (!data) return undefined
102
6d61da4e
C
103 if (window.crypto.subtle) {
104 return window.crypto.subtle.digest('SHA-256', data)
105 .then(data => bufferToHex(data))
106 }
107
108 // Fallback for non HTTPS context
ff5f37e4 109 const shaModule = (await import('sha.js') as any).default
9df52d66 110 // eslint-disable-next-line new-cap
6d61da4e 111 return new shaModule.sha256().update(Buffer.from(data)).digest('hex')
09209296
C
112}
113
114// Thanks: https://stackoverflow.com/a/53307879
6d61da4e 115function bufferToHex (buffer?: ArrayBuffer) {
09209296
C
116 if (!buffer) return ''
117
118 let s = ''
119 const h = '0123456789abcdef'
120 const o = new Uint8Array(buffer)
121
9df52d66
C
122 o.forEach((v: any) => {
123 s += h[v >> 4] + h[v & 15]
124 })
09209296
C
125
126 return s
127}