aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java')
-rw-r--r--container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java164
1 files changed, 164 insertions, 0 deletions
diff --git a/container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java b/container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java
new file mode 100644
index 00000000000..efca279cd38
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/processing/handler/ProcessingResponse.java
@@ -0,0 +1,164 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.processing.handler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+
+import com.google.common.collect.ImmutableList;
+import com.yahoo.container.jdisc.AsyncHttpResponse;
+import com.yahoo.container.jdisc.HttpRequest;
+import com.yahoo.container.jdisc.VespaHeaders;
+import com.yahoo.container.logging.AccessLogEntry;
+import com.yahoo.jdisc.handler.CompletionHandler;
+import com.yahoo.jdisc.handler.ContentChannel;
+import com.yahoo.processing.Request;
+import com.yahoo.processing.Response;
+import com.yahoo.processing.execution.Execution;
+import com.yahoo.processing.execution.Execution.Trace.LogValue;
+import com.yahoo.processing.rendering.AsynchronousRenderer;
+import com.yahoo.processing.rendering.Renderer;
+import com.yahoo.processing.request.ErrorMessage;
+import com.yahoo.processing.response.Data;
+import com.yahoo.processing.response.DataList;
+
+/**
+ * A response from running a request through processing. This response is just a
+ * wrapper of the knowhow needed to render the Response from processing.
+ *
+ * @author <a href="mailto:bratseth@yahoo-inc.com">Jon Bratseth</a>
+ * @author <a href="mailto:steinar@yahoo-inc.com">Steinar Knutsen</a>
+ * @since 5.1.12
+ */
+public class ProcessingResponse extends AsyncHttpResponse {
+
+ private final com.yahoo.processing.Request processingRequest;
+ private final com.yahoo.processing.Response processingResponse;
+ private final Executor renderingExecutor;
+ private final Execution execution;
+ private final Renderer renderer;
+
+ /** True if the return status has been set explicitly and should not be further changed */
+ private boolean explicitStatusSet = false;
+
+ @SuppressWarnings("unchecked")
+ public ProcessingResponse(
+ int status,
+ final com.yahoo.processing.Request processingRequest,
+ final com.yahoo.processing.Response processingResponse,
+ final Renderer renderer,
+ final Executor renderingExecutor, final Execution execution) {
+ super(status);
+ this.processingRequest = processingRequest;
+ this.processingResponse = processingResponse;
+ this.renderingExecutor = renderingExecutor;
+ this.execution = execution;
+ this.renderer = renderer;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void render(final OutputStream stream, final ContentChannel channel,
+ final CompletionHandler completionHandler) throws IOException {
+ if (renderer instanceof AsynchronousRenderer) {
+ AsynchronousRenderer asyncRenderer = (AsynchronousRenderer)renderer;
+ asyncRenderer.setNetworkWiring(channel, completionHandler);
+ }
+ renderer.render(stream, processingResponse, execution, processingRequest);
+ // the stream is closed in AsynchronousSectionedRenderer, after all data
+ // has arrived
+ }
+
+ @Override
+ public String getContentType() {
+ return renderer.getMimeType();
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ return renderer.getEncoding();
+ }
+
+ @Override
+ public void complete() {
+ // Add headers
+ addHeadersAndStatusFrom(processingResponse.data());
+
+ if ( ! explicitStatusSet) {
+ // Set status from errors TODO: This could be decomplicated a bit
+ List<ErrorMessage> errors = flattenErrors(processingResponse);
+ boolean isSuccess = !(processingResponse.data().asList().isEmpty() && !errors.isEmpty()); // NOT success if ( no data AND are errors )
+ setStatus(getHttpResponseStatus(isSuccess, processingRequest, errors.size() == 0 ? null : errors.get(0), errors));
+ }
+ }
+
+ /**
+ * This sets header and status from special Data items used for the purpose.
+ * Do both at once to avoid traversing the data tree twice.
+ */
+ @SuppressWarnings("unchecked")
+ private void addHeadersAndStatusFrom(DataList<Data> dataList) {
+ for (Data data : dataList.asList()) {
+ if (data instanceof ResponseHeaders) {
+ headers().addAll(((ResponseHeaders) data).headers());
+ }
+ else if ( ! explicitStatusSet && (data instanceof ResponseStatus)) {
+ setStatus(((ResponseStatus)data).code());
+ explicitStatusSet = true;
+ }
+ else if (data instanceof DataList) {
+ addHeadersAndStatusFrom((DataList) data);
+ }
+ }
+ }
+
+ private List<ErrorMessage> flattenErrors(Response processingResponse) {
+ Set<ErrorMessage> errors = flattenErrors(null, processingResponse.data());
+ if (errors == null) return Collections.emptyList();
+ return ImmutableList.copyOf(errors);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Set<ErrorMessage> flattenErrors(Set<ErrorMessage> errors, Data data) {
+ if (data.request() == null) return Collections.EMPTY_SET; // Not allowed, but handle anyway
+ errors = addTo(errors, data.request().errors());
+
+ if (data instanceof DataList) {
+ for (Data item : ((DataList<Data>) data).asList())
+ errors = flattenErrors(errors, item);
+ }
+
+ return errors;
+ }
+
+ private Set<ErrorMessage> addTo(Set<ErrorMessage> allErrors, List<ErrorMessage> errors) {
+ if (errors.isEmpty()) return allErrors;
+
+ if (allErrors == null)
+ allErrors = new LinkedHashSet<>();
+ allErrors.addAll(errors);
+ return allErrors;
+ }
+
+ private int getHttpResponseStatus(boolean isSuccess, Request request,
+ ErrorMessage mainError, List<ErrorMessage> errors) {
+ if (isBenchmarking(request)) return VespaHeaders.getEagerErrorStatus(mainError,errors.iterator());
+ return VespaHeaders.getStatus(isSuccess, mainError, errors.iterator());
+ }
+
+ private boolean isBenchmarking(Request request) {
+ com.yahoo.container.jdisc.HttpRequest httpRequest = (com.yahoo.container.jdisc.HttpRequest)request.properties().get(Request.JDISC_REQUEST);
+ if (httpRequest == null) return false;
+ return VespaHeaders.benchmarkOutput(httpRequest);
+ }
+
+ @Override
+ public Iterable<LogValue> getLogValues() {
+ return execution.trace()::logValueIterator;
+ }
+
+}