Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split OkHttpNetworkFetcher's fetch method to make part of it reusable #1459

Closed
wants to merge 2 commits into from

Conversation

rigdern
Copy link

@rigdern rigdern commented Sep 7, 2016

Subclasses overriding the fetch method might want to reuse some of its functionality. This change splits the fetch method into 2 methods to make this reusability possible: fetch and fetchWithRequest.

This Fresco change will be consumed by React Native PR facebook/react-native#7791 which enables React Native apps to specify headers to include in the HTTP request when fetching a remote image. For example, one might leverage this when fetching an image from an endpoint that requires authentication.

Adam Comella
Microsoft Corp.

@@ -256,6 +262,18 @@ public ImageRequestBuilder setPostprocessor(Postprocessor postprocessor) {
return mPostprocessor;
}

/** Sets the HTTP headers.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @rigdern.

We just need to add to this doc (and maybe where you declare mHeaders too) to make it clear that support for these added headers is dependent on the NetworkFetcher being used.

Ideally we'd add support to the basic HttpUrlConnectionNetworkFetcher too but I can live without that as long as this is well enough documented.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kriwan, thanks for the feedback! I added some comments to document this.

@ghost ghost added the CLA Signed label Sep 8, 2016
@ghost
Copy link

ghost commented Sep 8, 2016

@rigdern updated the pull request - view changes

@rigdern rigdern force-pushed the rigdern/image-headers branch from 9c5ca1d to 5eaf11d Compare September 8, 2016 20:12
@ghost
Copy link

ghost commented Sep 8, 2016

@rigdern updated the pull request - view changes

@kirwan
Copy link
Contributor

kirwan commented Sep 9, 2016

The doc you've added wasn't quite what I meant, which was that the headers are only supported if you use a NetworkFetcher implementation which consumes them. This PR only adds support for one of the two NetworkFetchers in the open source library and anyone using their own would have to add support.

That said I don't want to delay this over a doc which I can fix myself - particularly as we have a new release coming which this could be included in - so I'm going to import it then make the slight change myself.

@facebook-github-bot import

@ghost
Copy link

ghost commented Sep 9, 2016

Thanks for importing. If you are an FB employee go to Phabricator to review internal test results.

@ghost ghost added the CLA Signed label Sep 9, 2016
@kirwan kirwan mentioned this pull request Sep 9, 2016
@rigdern
Copy link
Author

rigdern commented Sep 9, 2016

Thanks, @kirwan. Sorry, I wrote this change a while ago and I forgot that NetworkFetcher was just an interface 😞.

@npomfret
Copy link

Any chance of getting this into the project? It would be very useful.

@lambdapioneer
Copy link
Contributor

Hi @rigdern and @npomfret,

Thanks again for the pull request. While the changes certainly would unblock the React Native feature, I do not feel too comfortable adding HTTP headers to the ImageRequest class. There are three arguments that I have for this:

First, the HTTP headers would only apply to a certain URI scheme (http) and is not relevant e.g. for local file requests. After adding headers, we might also want to add support for auth tokens in cookies and so on leading to some confusing parts in the core api.

Second, sending tokens from a higher level seems to be a specific use case due to the nature of React Native. Most clients would use an OkHttp interceptor (see code below [1]) to solve this. Passing down auth tokens from the UI would look like an anti-pattern there.

Finally, so far the URI was able to fully describe the source of the content. If the image might change depending on the used headers, this would require us to rethink cache keys.

Proposals

Nevertheless, I see that this is crucial for writing applications in React Native that require auth headers. I'd like to propose the following alternatives:

(1) We find a way to leverage the OkHttp3 interceptor in ReactNative. Frankly, I don't know how feasible this is to do. However, the big benefit would be that one can properly do the rewriting of all request parameters. In addition, client logic could handle a 401 itself, re-auth and then re-issue the original request. Completely transparent to the image and UI code.

(2) In the React Native Android code, we create a custom ReactNativeImageRequest extends ImageRequest in those cases where one wants to pass down HTTP modifiers. React Native would also implement its own NetworkFetcher down in the stack and there check for instanceof ReactNativeImageRequest for modifying the request on the lower level where necessary. This would come close to the current proposal with a lot of freedom, without modifying the ImageRequest class.

(3) Refactor the Fresco code to have a NetworkImageRequestWithHeaders extends ImageRequest. This would not change much for other clients, but make it available for users who really need this.

I'm looking forward to hear what you think about these ideas :)

Maybe @oprisnik or @kirwan also chime in. Personally, I feel that (2) would be something that we can get working quite soon. I'm more than happy to help you working on those so that we get them in soon.

Cheers,
Daniel

Code attachments

[1] Best-practice of adding auth headers using interceptor of okhttp

final OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder()
    .addInterceptor(new Interceptor() {
        @Override
        public Response intercept(Chain chain) throws IOException {
            if (!chain.request().url().host().equals("myhost.com")) {
                return chain.proceed(chain.request());
            }
            final Request.Builder requestBuilder = chain.request().newBuilder();
            requestBuilder.addHeader("authtoken", "foobar42");
            return chain.proceed(requestBuilder.build());
        }
    });
final ImagePipelineConfig imagePipelineConfig = OkHttpImagePipelineConfigFactory.newBuilder(
        getApplicationContext(),
        okHttpClientBuilder.build())
    .build();

@ghost ghost added the CLA Signed label Sep 19, 2016
@npomfret
Copy link

Thanks for the details. I agree that option 2 sounds best. Are you considering that the user could provide an optional "NetworkFetcher" from their JS code in the form of a fetch request? From there they are free to provide custom headers and cache keys.

I don't fully understand option 3 - how would it manifest in the client code?

@rigdern
Copy link
Author

rigdern commented Sep 26, 2016

@lambdapioneer, thanks for the feedback. Option (2) sounds very similar to the change I originally submitted to the react-native repo (rigdern/react-native@2743ba2) but in this comment @dmmiller suggested this alternative approach of putting part of the change into Fresco.

@dmmiller do you have any thoughts on @lambdapioneer proposals?

@ghost ghost added the CLA Signed label Sep 26, 2016
@dmmiller
Copy link

dmmiller commented Oct 4, 2016

I am fine with (2). The fresco team certainly knows better if this doesn't really belong in Fresco and if there is a better way to put it in. I suggested it since it looked like just a copy/paste + a new method in our code. If we can do it through inheritance that might be better.

@rigdern
Copy link
Author

rigdern commented Oct 4, 2016

Based on @dmmiller's response, I will continue with (2). @lambdapioneer, can you provide feedback on my implementation of (2) at rigdern/react-native@2743ba2?

@lambdapioneer
Copy link
Contributor

lambdapioneer commented Oct 5, 2016

@rigdern, I've added my comments to the implementation at rigdern/react-native@2743ba2. Looks very good and happy to support from Fresco side

Subclasses overriding the fetch method might want to reuse some of its functionality. This change splits the fetch method to make this reusability possible.
@facebook-github-bot
Copy link
Contributor

@rigdern updated the pull request - view changes - changes since last import

@rigdern
Copy link
Author

rigdern commented Oct 7, 2016

@lambdapioneer Thank you for your suggestions. I applied them and updated the Fresco and React Native PRs.

Copy link
Contributor

@lambdapioneer lambdapioneer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome, just two small style remarks :)

Will already import and then update with when the code style is updated

.url(uri.toString())
.get()
.build();
protected void fetchWithRequest(final OkHttpNetworkFetchState fetchState, final Callback callback,
Copy link
Contributor

@lambdapioneer lambdapioneer Oct 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move this method below the other public methods and re-format:

protected void fetchWithRequest(
    final OkHttpNetworkFetchState fetchState,
    final Callback callback,
    final Request request) {

call,
new IOException("Unexpected HTTP code " + response),
callback);
call,
Copy link
Contributor

@lambdapioneer lambdapioneer Oct 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

- Moved protected method fetchWithRequest to be below the public methods
- Reformated fetchWithRequest to put each argument in its signature on a separate line.
@facebook-github-bot
Copy link
Contributor

@rigdern updated the pull request - view changes - changes since last import

@rigdern rigdern changed the title Add support for setting HTTP headers in ImageRequest Split OkHttpNetworkFetcher's fetch method to make part of it reusable Oct 7, 2016
@rigdern
Copy link
Author

rigdern commented Oct 7, 2016

@lambdapioneer I made the styling tweaks you requested. I also retitled the PR to better match the approach we're now taking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants