Android - calling Util.reload() causes Image requests to be sent without cookies

I originally posted an issue on Github here (https://github.com/expo/expo/issues/370), but I’ve found more information on this recently that’s clarified what’s really going on and made the issue a bit more urgent for me (now it affects an app that is about to go into production, it’s not just a test environment thing). So, looking for any advice on possible workarounds, for sure!

On Android, I subscribe to the update listener, and then trigger a reload when a new version is detected after restarting the app. My app has a login screen, and a session cookie is set by the response when a user successfully logs in, so all future requests have the cookie. My app also has images secured behind the session ID that are displayed with the stock Image component (think of a chat feed with text and images).

After I publish an update to the JavaScript bundles, I close the Android app, restart it, and the update listener picks up that there’s an update and runs my callback, which shows an alert and then reloads the experience. When I reload the experiences, any new images that were not already cached by the app do not download. When I put them through an HTTP sniffer, a) the cookie is not on the image requests, and b) the image requests appear to be using an entirely different HTTP client (Android/ davlik, not okhttp).

If I close and restart the app again, all is fine again. The images appear, and the network sniffer reports that cookies are attached to the image requests and they’re back to going through okhttp.

Here’s an image request header pre-reload / after closing the app after it reloads and images don’t appear (aka, “the good one”):

GET /4/message/image/497 HTTP/1.1
Host	...
Connection	Keep-Alive
Accept-Encoding	gzip
Cookie	session=...
User-Agent	okhttp/3.6.0

And here’s the image request header after the reload (aka, “the bad one”):

GET /4/message/image/498 HTTP/1.1
User-Agent	Dalvik/2.1.0 (Linux; U; Android 6.0.1; Moto G Play Build/MPI24.241-2.35-1.5)
Host	...
Connection	Keep-Alive
Accept-Encoding	gzip

This is all on Expo 22 (haven’t been able to upgrade to 23 due to a breaking change for me, planning on upgrading to 24 though). This has been around since at least Expo 19 in some form.

Here’s my check for update code:

    // handle updates to Expo JS bundle for Android
    const updateListener = Util.addNewVersionListenerExperimental(() => {
      Alert.alert(
        'An Update is Available',
        'The app will now reload so you can take advantage of the latest improvements.',
        [
          // { text: 'Don\'t reload right now', onPress: () => {}, style: 'cancel' },
          {
            text: 'Reload and update',
            onPress: () => {
              Util.reload();
            },
          },
        ],
        { cancelable: false }
      );
    });

Any ideas on any workarounds for this? Is there another Image component I can use? Am I reloading wrong?

Thanks!

I’ve tried the following workarounds with no luck:

a) reloading again after the first reload - no matter how many times I reload, it’s still showing the the Davlik user agent. Only closing and restarting the app entirely fixes it.

b) Passing a Cookie header with the source for the Image component (see Images · React Native) - this works fine for okhttp, but the Davlik user-agent headers don’t bring the cookie along.

I’ve figured out how to reproduce this without even publishing initially or publishing an update.

It happens even if you’re just live reloading from your computer to the Expo app. Anytime you reload an Android app, the reloaded app sends Image component requests through the Davlik HTTP stack without the cookies.

So, repro steps:

Given an app that requests an image via a URL and displays it in an Image component, and has a button that calls Util.reload(). The image doesn’t have to be secured behind a session in a web app - you should at least be able to see that it’s going to a different HTTP stack, whcih tells you something is wrong.

  1. Set a network sniffer proxy on your Android phone (like Charles or Fiddler).
  2. Run the app in Expo.
  3. Look in the sniffer. You’ll see the request for the image goes through okhttp.
  4. Reload the app with the reload button.
  5. Change the image URL (triggers a refresh - that should be OK). If you don’t do this, you’ll never see the network request because the old image is now cached.
  6. Look in the sniffer. The request for the new image goes through Davlik.

I’m going to try to make a snack to demonstrate this better. Would really appreciate some thoughts or ideas on possible workarounds - we’re trying to avoid actually telling the user to close and reload an app in the task switcher to get an update.

I’ve just built a Snack to reproduce this issue: https://snack.expo.io/Sy6SpHgGz. So, it definitely affects Expo 24.

Repro steps:

  1. Connect an Android phone to a network sniffing proxy (e.g., Charles: https://www.charlesproxy.com - it tells you exactly what to do by going to Help → SSL Proxying).
  2. Go to the Snack.
  3. Copy an image URL in the text box (Google Image Search helps here).
  4. Flip the toggle to on.
  5. The image displays
  6. Watch your HTTP traffic. You’ll see a request for the image with the okhttp user agent.
  7. Press the button to Reload the app.
  8. Copy a DIFFERENT image URL in the text box (so we know it’s not cached).
  9. Flip the toggle on
  10. The image displays
  11. Watch your HTTP traffic. You’ll see a request for the image with the Davlik user agent.

The one difference between this reproduction and my issue is that the image always displays in the reproduction, because it’s not secured behind a session cookie. In my actual scenario, whenever I see the Davlik user agent, my cookies stop going across the wire and my images stop displaying.