/* * Copyright 2014 Netflix, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.netflix.ribbon.http; import com.netflix.hystrix.HystrixObservableCommand; import com.netflix.ribbon.CacheProvider; import com.netflix.ribbon.RequestWithMetaData; import com.netflix.ribbon.RibbonRequest; import com.netflix.ribbon.hystrix.CacheObservableCommand; import com.netflix.ribbon.hystrix.HystrixObservableCommandChain; import com.netflix.ribbon.template.TemplateParser; import com.netflix.ribbon.template.TemplateParsingException; import io.netty.buffer.ByteBuf; import io.reactivex.netty.protocol.http.client.HttpClient; import io.reactivex.netty.protocol.http.client.HttpClientRequest; import rx.Observable; import rx.functions.Func1; import rx.subjects.ReplaySubject; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.Future; class HttpRequest<T> implements RibbonRequest<T> { static class CacheProviderWithKey<T> { CacheProvider<T> cacheProvider; String key; public CacheProviderWithKey(CacheProvider<T> cacheProvider, String key) { this.cacheProvider = cacheProvider; this.key = key; } public final CacheProvider<T> getCacheProvider() { return cacheProvider; } public final String getKey() { return key; } } private static final Func1<ByteBuf, ByteBuf> refCountIncrementer = new Func1<ByteBuf, ByteBuf>() { @Override public ByteBuf call(ByteBuf t1) { t1.retain(); return t1; } }; private final HttpClientRequest<ByteBuf> httpRequest; private final String hystrixCacheKey; private final String cacheHystrixCacheKey; private final CacheProviderWithKey<T> cacheProvider; private final Map<String, Object> requestProperties; private final HttpClient<ByteBuf, ByteBuf> client; /* package private for HttpMetaRequest */ final HttpRequestTemplate<T> template; HttpRequest(HttpRequestBuilder<T> requestBuilder) throws TemplateParsingException { client = requestBuilder.template().getClient(); httpRequest = requestBuilder.createClientRequest(); hystrixCacheKey = requestBuilder.hystrixCacheKey(); cacheHystrixCacheKey = hystrixCacheKey == null ? null : hystrixCacheKey + HttpRequestTemplate.CACHE_HYSTRIX_COMMAND_SUFFIX; requestProperties = new HashMap<String, Object>(requestBuilder.requestProperties()); if (requestBuilder.cacheProvider() != null) { CacheProvider<T> provider = requestBuilder.cacheProvider().getProvider(); String key = TemplateParser.toData(this.requestProperties, requestBuilder.cacheProvider().getKeyTemplate()); cacheProvider = new CacheProviderWithKey<T>(provider, key); } else { cacheProvider = null; } template = requestBuilder.template(); if (!ByteBuf.class.isAssignableFrom(template.getClassType())) { throw new IllegalArgumentException("Return type other than ByteBuf is not currently supported as serialization functionality is still work in progress"); } } HystrixObservableCommandChain<T> createHystrixCommandChain() { List<HystrixObservableCommand<T>> commands = new ArrayList<HystrixObservableCommand<T>>(2); if (cacheProvider != null) { commands.add(new CacheObservableCommand<T>(cacheProvider.getCacheProvider(), cacheProvider.getKey(), cacheHystrixCacheKey, requestProperties, template.cacheHystrixProperties())); } commands.add(new HttpResourceObservableCommand<T>(client, httpRequest, hystrixCacheKey, requestProperties, template.fallbackHandler(), template.responseValidator(), template.getClassType(), template.hystrixProperties())); return new HystrixObservableCommandChain<T>(commands); } @Override public Observable<T> toObservable() { return createHystrixCommandChain().toObservable(); } @Override public T execute() { return getObservable().toBlocking().last(); } @Override public Future<T> queue() { return getObservable().toBlocking().toFuture(); } @Override public Observable<T> observe() { ReplaySubject<T> subject = ReplaySubject.create(); getObservable().subscribe(subject); return subject; } @Override public RequestWithMetaData<T> withMetadata() { return new HttpMetaRequest<T>(this); } boolean isByteBufResponse() { return ByteBuf.class.isAssignableFrom(template.getClassType()); } Observable<T> getObservable() { if (isByteBufResponse()) { return ((Observable) toObservable()).map(refCountIncrementer); } else { return toObservable(); } } }