Skip to content

Instantly share code, notes, and snippets.

@miyagawa
Created May 16, 2018 15:02
Show Gist options
  • Save miyagawa/652be3a0f1d4721c7bba4c3df3cd5648 to your computer and use it in GitHub Desktop.
Save miyagawa/652be3a0f1d4721c7bba4c3df3cd5648 to your computer and use it in GitHub Desktop.
radar 40288687
Summary:
Apple's Podcast client application (AppleCoreMedia) does not support content revalidation defined in RFC7233. It also has a bug handling 416 (Request Range Not Satisfiable) responses.
Steps to Reproduce:
1. Using Apple's Podcasts app, subscribe to a podcast you own.
2. Upload a new episode with an audio file.
3. Start streaming the episode but pause it.
4. Re-upload a new audio file in a shorter size to the same URL
5. Resume the streaming of the episode
Expected Results:
Apple Podcasts app should support If-Range or If-Match request header and detects that the file has been updated, and redownloads the audio file when necessary.
Actual Results:
1. Apple Podcasts app does not support If-Range nor If-Match header, and fails to detect that the file has been updated and keeps trying to fetch the file with an incorrect Range: headers.
2. Apple Podcasts app does not handle 416 (Request Range Not Satisfiable) responses correctly, and it tries to request the same range repeatedly in a loop.
Version/Build:
I confirmed with AppleCoreMedia/1.0.0.15E302 but happens with multiple versions of the framework.
Configuration:
Details:
Apple Podcasts app seems to use Range: headers to stream the audio content. First, it sends a request with Range: bytes=0-1 to get the initial 2 bytes of the file, presumably to get the correct content-length. Let's say the response is 50MB.
First request:
GET /audio/podcast-ep207.mp3 HTTP/1.1
Host: cdn.rebuild.fm
X-Playback-Session-Id: EC179E4F-2E6D-40BF-B6B8-BAE34E915D3D
Range: bytes=0-1
Accept: */*
User-Agent: AppleCoreMedia/1.0.0.15E216 (iPhone; U; CPU OS 11_3 like Mac OS X; ja_jp)
Accept-Language: ja-jp
Accept-Encoding: identity
Response:
HTTP/1.1 206 Partial Content
Last-Modified: Mon, 14 May 2018 22:47:03 GMT
ETag: "09a983510bf9b9e76fe483899fe7049a-19"
Content-Type: audio/mpeg
Accept-Ranges: bytes
Date: Wed, 16 May 2018 08:38:27 GMT
Content-Range: bytes 0-1/50000000
Content-Length: 2
Apple Podcasts app now remembers the length. Once the streaming is paused and resumed, it always uses the value 49999999 to get the remaining bytes:
GET /audio/podcast-ep207.mp3 HTTP/1.1
Host: cdn.rebuild.fm
X-Playback-Session-Id: EC179E4F-2E6D-40BF-B6B8-BAE34E915D3D
Range: bytes=12345678-49999999
Accept: */*
User-Agent: AppleCoreMedia/1.0.0.15E216 (iPhone; U; CPU OS 11_3 like Mac OS X; ja_jp)
Now, if the file in the URL has been replaced and if the file became smaller, the server can't handle that Range requests and will respond with 416 Request Range Not Satisfiable correctly.
Apple Podcasts app doesn't seem to handle this gracefully, and keeps trying with the same Range: header to the same URL in a loop.
This can be avoided if Apple Podcast and CoreMedia framework supports RFC7233 properly by supporting If-Range or If-Match request header using the ETag or Date values sent from the server, so that if the resource has been updated, it should detect the change and re-download the audio appropriately.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment