All Downloads are FREE. Search and download functionalities are using the official Maven repository.

reactivefeign.jetty.client.JettyReactiveHttpResponse Maven / Gradle / Ivy

The newest version!
package reactivefeign.jetty.client;

import com.fasterxml.jackson.core.async_.JsonFactory;
import com.fasterxml.jackson.databind.ObjectReader;
import org.eclipse.jetty.client.Response;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.io.Content;
import org.eclipse.jetty.io.Retainable;
import org.reactivestreams.Publisher;
import reactivefeign.client.ReactiveHttpRequest;
import reactivefeign.client.ReactiveHttpResponse;
import reactivejson.ReactorObjectReader;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.Optional.ofNullable;
import static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE;

class JettyReactiveHttpResponse implements ReactiveHttpResponse{

	public static final String CHARSET_DELIMITER = ";charset=";
	private ReactiveHttpRequest request;
	private final Response clientResponse;
	private final Publisher contentChunks;
	private final Class returnPublisherType;
	private Class returnActualClass;
	private final ObjectReader objectReader;
	private final JsonFactory jsonFactory;

	JettyReactiveHttpResponse(ReactiveHttpRequest request, Response clientResponse, Publisher contentChunks,
							  Class returnPublisherType, Class returnActualClass,
							  JsonFactory jsonFactory, ObjectReader objectReader) {
		this.request = request;
		this.clientResponse = clientResponse;
		this.contentChunks = contentChunks;
		this.returnPublisherType = returnPublisherType;
		this.returnActualClass = returnActualClass;
		this.objectReader = objectReader;
		this.jsonFactory = jsonFactory;
	}

	@Override
	public ReactiveHttpRequest request() {
		return request;
	}

	@Override
	public int status() {
		return clientResponse.getStatus();
	}

	@Override
	public Map> headers() {
		HttpFields headers = clientResponse.getHeaders();
		Map> headersMap = new HashMap<>(headers.size());
		headers.forEach(httpField ->
				headersMap.compute(httpField.getName(), (oldName, oldValues) -> {
					List values;
					if(oldValues == null){
						values = Arrays.asList(httpField.getValues());
					} else {
						values = new ArrayList<>(oldValues.size() + httpField.getValues().length);
						values.addAll(oldValues);
						values.addAll(Arrays.asList(httpField.getValues()));
					}
					return values;
				}));
		return headersMap;
	}

	@Override
	public Publisher body() {
		ReactorObjectReader reactorObjectReader = new ReactorObjectReader(jsonFactory);

		Flux content = directContent();

		if(returnActualClass == ByteBuffer.class){
			return content;
		} else if(returnActualClass.isAssignableFrom(String.class)
				&& returnPublisherType == Mono.class){
			Charset charset = getCharset();
			return content.map(byteBuffer -> charset.decode(byteBuffer).toString());
		} else {
			if (returnPublisherType == Mono.class) {
				return reactorObjectReader.read(content, objectReader);
			} else if(returnPublisherType == Flux.class){
				return reactorObjectReader.readElements(content, objectReader);
			} else {
				throw new IllegalArgumentException("Unknown returnPublisherType: " + returnPublisherType);
			}
		}
	}

	@Override
	public Mono releaseBody() {
		return Flux.from(contentChunks)
				.doOnNext(Retainable::release)
				.then();
	}

	private Charset getCharset() {
		return ofNullable(clientResponse.getHeaders().get(CONTENT_TYPE.asString()))
				.map(header -> {
					int pos = header.indexOf(CHARSET_DELIMITER);
					if(pos >= 0){
						return header.substring(pos + CHARSET_DELIMITER.length());
					} else {
						return null;
					}
				})
				.map(Charset::forName)
				.orElse(UTF_8);
	}

	private Flux directContent() {
		return Flux.from(contentChunks)
				.map(contentChunk -> contentChunk.getByteBuffer().slice());
	}

	@Override
	public Mono bodyData() {
		return joinChunks();
	}

	private Mono joinChunks() {
		return directContent().reduce(new ByteArrayOutputStream(), (baos, byteBuffer) -> {
			for(int i = byteBuffer.position(), limit = byteBuffer.limit(); i < limit; i++){
				baos.write(byteBuffer.get(i));
			}
			return baos;
		}).map(ByteArrayOutputStream::toByteArray);
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy