advplyr / audiobookshelf

Self-hosted audiobook and podcast server
https://audiobookshelf.org
GNU General Public License v3.0
6.5k stars 466 forks source link

[Bug]: `ERR_FR_REDIRECTION_FAILURE` while adding a new podcast #3142

Closed iuliandita closed 2 weeks ago

iuliandita commented 3 months ago

What happened?

I added several podcasts, and most of them worked just fine, except a couple, such as D&D is for Nerds or Radiolab.

What did you expect to happen?

I would like to be able to add the podcast mentioned before.

Steps to reproduce the issue

  1. search for podcast, ie D&D is for Nerds
  2. click on it to add
  3. error pop up shows saying Failed to get podcast feed
  4. error log shows ERROR: [podcastUtils] getPodcastFeed Error Error [ERR_FR_REDIRECTION_FAILURE]: Redirected request failed: Protocol "https:" not supported. Expected "http:"

Audiobookshelf version

v2.10.1

How are you running audiobookshelf?

Docker

What OS is your Audiobookshelf server hosted from?

Linux

If the issue is being seen in the UI, what browsers are you seeing the problem on?

Firefox

Logs

audiobookshelf  | [2024-07-09 12:33:00.182] ERROR: [podcastUtils] getPodcastFeed Error Error [ERR_FR_REDIRECTION_FAILURE]: Redirected request failed: Protocol "https:" not supported. Expected "http:"
audiobookshelf  |     at RedirectableRequest._onNativeResponse (/node_modules/follow-redirects/index.js:95:17)
audiobookshelf  |     at Object.onceWrapper (node:events:634:26)
audiobookshelf  |     at ClientRequest.emit (node:events:519:28)
audiobookshelf  |     at HTTPParser.parserOnIncomingClient [as onIncoming] (node:_http_client:698:27)
audiobookshelf  |     at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17)
audiobookshelf  |     at Socket.socketOnData (node:_http_client:540:22)
audiobookshelf  |     at Socket.emit (node:events:519:28)
audiobookshelf  |     at addChunk (node:internal/streams/readable:559:12)
audiobookshelf  |     at readableAddChunkPushByteMode (node:internal/streams/readable:510:3)
audiobookshelf  |     at Readable.push (node:internal/streams/readable:390:5) {
audiobookshelf  |   code: 'ERR_FR_REDIRECTION_FAILURE',
audiobookshelf  |   cause: TypeError [ERR_INVALID_PROTOCOL]: Protocol "https:" not supported. Expected "http:"
audiobookshelf  |       at new ClientRequest (node:_http_client:183:11)
audiobookshelf  |       at Object.request (node:https:379:10)
audiobookshelf  |       at RedirectableRequest._performRequest (/node_modules/follow-redirects/index.js:326:24)
audiobookshelf  |       at RedirectableRequest._processResponse (/node_modules/follow-redirects/index.js:483:8)
audiobookshelf  |       at RedirectableRequest._onNativeResponse (/node_modules/follow-redirects/index.js:91:12)
audiobookshelf  |       at Object.onceWrapper (node:events:634:26)
audiobookshelf  |       at ClientRequest.emit (node:events:519:28)
audiobookshelf  |       at HTTPParser.parserOnIncomingClient (node:_http_client:698:27)
audiobookshelf  |       at HTTPParser.parserOnHeadersComplete (node:_http_common:119:17)
audiobookshelf  |       at Socket.socketOnData (node:_http_client:540:22) {
audiobookshelf  |     code: 'ERR_INVALID_PROTOCOL'
audiobookshelf  |   },
audiobookshelf  |   config: {
audiobookshelf  |     transitional: {
audiobookshelf  |       silentJSONParsing: true,
audiobookshelf  |       forcedJSONParsing: true,
audiobookshelf  |       clarifyTimeoutError: false
audiobookshelf  |     },
audiobookshelf  |     adapter: [Function: httpAdapter],
audiobookshelf  |     transformRequest: [ [Function: transformRequest] ],
audiobookshelf  |     transformResponse: [ [Function: transformResponse] ],
audiobookshelf  |     timeout: 12000,
audiobookshelf  |     xsrfCookieName: 'XSRF-TOKEN',
audiobookshelf  |     xsrfHeaderName: 'X-XSRF-TOKEN',
audiobookshelf  |     maxContentLength: -1,
audiobookshelf  |     maxBodyLength: -1,
audiobookshelf  |     env: { FormData: [Function] },
audiobookshelf  |     validateStatus: [Function: validateStatus],
audiobookshelf  |     headers: {
audiobookshelf  |       Accept: 'application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8',
audiobookshelf  |       'User-Agent': 'axios/0.27.2'
audiobookshelf  |     },
audiobookshelf  |     url: 'http://rss.acast.com/danddisfornerds',
audiobookshelf  |     method: 'get',
audiobookshelf  |     responseType: 'arraybuffer',
audiobookshelf  |     httpAgent: Agent {
audiobookshelf  |       _events: [Object: null prototype],
audiobookshelf  |       _eventsCount: 2,
audiobookshelf  |       _maxListeners: undefined,
audiobookshelf  |       defaultPort: 80,
audiobookshelf  |       protocol: 'http:',
audiobookshelf  |       options: [Object: null prototype],
audiobookshelf  |       requests: [Object: null prototype] {},
audiobookshelf  |       sockets: [Object: null prototype],
audiobookshelf  |       freeSockets: [Object: null prototype] {},
audiobookshelf  |       keepAliveMsecs: 1000,
audiobookshelf  |       keepAlive: false,
audiobookshelf  |       maxSockets: Infinity,
audiobookshelf  |       maxFreeSockets: 256,
audiobookshelf  |       scheduling: 'lifo',
audiobookshelf  |       maxTotalSockets: Infinity,
audiobookshelf  |       totalSocketCount: 1,
audiobookshelf  |       createConnection: [Function (anonymous)],
audiobookshelf  |       [Symbol(shapeMode)]: false,
audiobookshelf  |       [Symbol(kCapture)]: false,
audiobookshelf  |       [Symbol(active)]: true
audiobookshelf  |     },
audiobookshelf  |     httpsAgent: Agent {
audiobookshelf  |       _events: [Object: null prototype],
audiobookshelf  |       _eventsCount: 2,
audiobookshelf  |       _maxListeners: undefined,
audiobookshelf  |       defaultPort: 80,
audiobookshelf  |       protocol: 'http:',
audiobookshelf  |       options: [Object: null prototype],
audiobookshelf  |       requests: [Object: null prototype] {},
audiobookshelf  |       sockets: [Object: null prototype] {},
audiobookshelf  |       freeSockets: [Object: null prototype] {},
audiobookshelf  |       keepAliveMsecs: 1000,
audiobookshelf  |       keepAlive: false,
audiobookshelf  |       maxSockets: Infinity,
audiobookshelf  |       maxFreeSockets: 256,
audiobookshelf  |       scheduling: 'lifo',
audiobookshelf  |       maxTotalSockets: Infinity,
audiobookshelf  |       totalSocketCount: 0,
audiobookshelf  |       createConnection: [Function (anonymous)],
audiobookshelf  |       [Symbol(shapeMode)]: false,
audiobookshelf  |       [Symbol(kCapture)]: false,
audiobookshelf  |       [Symbol(active)]: true
audiobookshelf  |     },
audiobookshelf  |     data: undefined
audiobookshelf  |   },
audiobookshelf  |   request: <ref *1> Writable {
audiobookshelf  |     _events: {
audiobookshelf  |       close: undefined,
audiobookshelf  |       error: [Function: handleRequestError],
audiobookshelf  |       prefinish: undefined,
audiobookshelf  |       finish: undefined,
audiobookshelf  |       drain: undefined,
audiobookshelf  |       response: [Function: handleResponse],
audiobookshelf  |       socket: [Array],
audiobookshelf  |       timeout: undefined,
audiobookshelf  |       abort: undefined
audiobookshelf  |     },
audiobookshelf  |     _writableState: WritableState {
audiobookshelf  |       highWaterMark: 16384,
audiobookshelf  |       length: 0,
audiobookshelf  |       corked: 0,
audiobookshelf  |       onwrite: [Function: bound onwrite],
audiobookshelf  |       writelen: 0,
audiobookshelf  |       bufferedIndex: 0,
audiobookshelf  |       pendingcb: 0,
audiobookshelf  |       [Symbol(kState)]: 17580812,
audiobookshelf  |       [Symbol(kBufferedValue)]: null
audiobookshelf  |     },
audiobookshelf  |     _maxListeners: undefined,
audiobookshelf  |     _options: {
audiobookshelf  |       maxRedirects: 21,
audiobookshelf  |       maxBodyLength: 10485760,
audiobookshelf  |       protocol: 'https:',
audiobookshelf  |       path: '/public/shows/2158de25-bc3d-4828-b81f-efac999110de',
audiobookshelf  |       method: 'GET',
audiobookshelf  |       headers: [Object],
audiobookshelf  |       agent: [Agent],
audiobookshelf  |       agents: [Object],
audiobookshelf  |       auth: null,
audiobookshelf  |       hostname: 'feeds.acast.com',
audiobookshelf  |       port: 0,
audiobookshelf  |       nativeProtocols: [Object],
audiobookshelf  |       pathname: '/public/shows/2158de25-bc3d-4828-b81f-efac999110de',
audiobookshelf  |       host: 'feeds.acast.com',
audiobookshelf  |       href: 'https://feeds.acast.com/public/shows/2158de25-bc3d-4828-b81f-efac999110de',
audiobookshelf  |       query: null,
audiobookshelf  |       search: null,
audiobookshelf  |       hash: null
audiobookshelf  |     },
audiobookshelf  |     _ended: true,
audiobookshelf  |     _ending: true,
audiobookshelf  |     _redirectCount: 1,
audiobookshelf  |     _redirects: [],
audiobookshelf  |     _requestBodyLength: 0,
audiobookshelf  |     _requestBodyBuffers: [],
audiobookshelf  |     _eventsCount: 3,
audiobookshelf  |     _onNativeResponse: [Function (anonymous)],
audiobookshelf  |     _currentRequest: ClientRequest {
audiobookshelf  |       _events: [Object: null prototype],
audiobookshelf  |       _eventsCount: 2,
audiobookshelf  |       _maxListeners: undefined,
audiobookshelf  |       outputData: [],
audiobookshelf  |       outputSize: 0,
audiobookshelf  |       writable: true,
audiobookshelf  |       destroyed: true,
audiobookshelf  |       _last: true,
audiobookshelf  |       chunkedEncoding: false,
audiobookshelf  |       shouldKeepAlive: false,
audiobookshelf  |       maxRequestsOnConnectionReached: false,
audiobookshelf  |       _defaultKeepAlive: true,
audiobookshelf  |       useChunkedEncodingByDefault: false,
audiobookshelf  |       sendDate: false,
audiobookshelf  |       _removedConnection: false,
audiobookshelf  |       _removedContLen: false,
audiobookshelf  |       _removedTE: false,
audiobookshelf  |       strictContentLength: false,
audiobookshelf  |       _contentLength: 0,
audiobookshelf  |       _hasBody: true,
audiobookshelf  |       _trailer: '',
audiobookshelf  |       finished: true,
audiobookshelf  |       _headerSent: true,
audiobookshelf  |       _closed: false,
audiobookshelf  |       socket: [Socket],
audiobookshelf  |       _header: 'GET /danddisfornerds HTTP/1.1\r\n' +
audiobookshelf  |         'Accept: application/rss+xml, application/xhtml+xml, application/xml, */*;q=0.8\r\n' +
audiobookshelf  |         'User-Agent: axios/0.27.2\r\n' +
audiobookshelf  |         'Host: rss.acast.com\r\n' +
audiobookshelf  |         'Connection: close\r\n' +
audiobookshelf  |         '\r\n',
audiobookshelf  |       _keepAliveTimeout: 0,
audiobookshelf  |       _onPendingData: [Function: nop],
audiobookshelf  |       agent: [Agent],
audiobookshelf  |       socketPath: undefined,
audiobookshelf  |       method: 'GET',
audiobookshelf  |       maxHeaderSize: undefined,
audiobookshelf  |       insecureHTTPParser: undefined,
audiobookshelf  |       joinDuplicateHeaders: undefined,
audiobookshelf  |       path: '/danddisfornerds',
audiobookshelf  |       _ended: true,
audiobookshelf  |       res: [IncomingMessage],
audiobookshelf  |       aborted: false,
audiobookshelf  |       timeoutCb: null,
audiobookshelf  |       upgradeOrConnect: false,
audiobookshelf  |       parser: null,
audiobookshelf  |       maxHeadersCount: null,
audiobookshelf  |       reusedSocket: false,
audiobookshelf  |       host: 'rss.acast.com',
audiobookshelf  |       protocol: 'http:',
audiobookshelf  |       _redirectable: [Circular *1],
audiobookshelf  |       [Symbol(shapeMode)]: false,
audiobookshelf  |       [Symbol(kCapture)]: false,
audiobookshelf  |       [Symbol(kBytesWritten)]: 0,
audiobookshelf  |       [Symbol(kNeedDrain)]: false,
audiobookshelf  |       [Symbol(corked)]: 0,
audiobookshelf  |       [Symbol(kOutHeaders)]: [Object: null prototype],
audiobookshelf  |       [Symbol(errored)]: null,
audiobookshelf  |       [Symbol(kHighWaterMark)]: 16384,
audiobookshelf  |       [Symbol(kRejectNonStandardBodyWrites)]: false,
audiobookshelf  |       [Symbol(kUniqueHeaders)]: null,
audiobookshelf  |       [Symbol(kError)]: undefined
audiobookshelf  |     },
audiobookshelf  |     _currentUrl: 'http://rss.acast.com/danddisfornerds',
audiobookshelf  |     _timeout: null,
audiobookshelf  |     _isRedirect: true,
audiobookshelf  |     [Symbol(shapeMode)]: true,
audiobookshelf  |     [Symbol(kCapture)]: false
audiobookshelf  |   }
audiobookshelf  | } (podcastUtils.js:265)


### Additional Notes

_No response_
advplyr commented 3 months ago

That's getting blocked by the SSRF request filter because a cross protocol redirect is happening. https://blog.doyensec.com/2023/03/16/ssrf-remediation-bypass.html

I'm not sure if there is a better way to handle this. There is a way to bypass the SSRF request filter with an env variable DISABLE_SSRF_REQUEST_FILTER=1