/** * create a function invocation exception * * @param message private message for this exception - * @param target the underlying user exception that triggered this failure */ public InternalFunctionInvocationException(String message, Throwable target) { super(message); this.cause = target; this.event = OutputEvent.fromBytes(new byte[0], OutputEvent.Status.FunctionError, null); }
@Override public Optional<String> getContentType() { return from.getContentType(); }
@Override public Headers getHeaders() { return from.getHeaders(); }
private void writeEvent(OutputEvent evt, HttpResponse response) { evt.getHeaders().asMap() .entrySet() .stream() .filter(e -> !stripOutputHeaders.contains(e.getKey())) .flatMap(e -> e.getValue().stream().map((v) -> new BasicHeader(e.getKey(), v))) .forEachOrdered(response::addHeader); ContentType contentType = evt.getContentType().map(c -> { try { return ContentType.parse(c); } catch (ParseException e) { return ContentType.DEFAULT_BINARY; } }).orElse(ContentType.DEFAULT_BINARY); response.setHeader("Content-Type", contentType.toString()); response.setStatusLine(new BasicStatusLine(HttpVersion.HTTP_1_1, evt.getStatus().getCode(), evt.getStatus().name())); ByteArrayOutputStream bos = new ByteArrayOutputStream(); // TODO remove output buffering here - possibly change OutputEvent contract to support providing an InputStream? try { evt.writeToOutput(bos); } catch (IOException e) { throw new FunctionOutputHandlingException("Error writing output", e); } byte[] data = bos.toByteArray(); response.setEntity(new ByteArrayEntity(data, contentType)); }
throw new FunctionInputHandlingException("No invoker found for input event"); if (output.isSuccess()) { lastStatus.set(0); fic.fireOnSuccessfulInvocation(); return output.withHeaders(output.getHeaders().setHeaders(fic.getAdditionalResponseHeaders())); lastStatus.set(fie.toOutput().isSuccess() ? 0 : 1); return fie.toOutput();
@Override public void writeToOutput(OutputStream out) throws IOException { a.writeToOutput(out); } };
/** * Report the boolean success of this event. * For default-format functions, this is used to map the HTTP status code into a straight success/failure. * * @return true if the output event results from a successful invocation. */ default boolean isSuccess() { return getStatus() == Status.Success; }
/** * Upon request failure an empty output event will be returned to the functions platform as part of * the WrappedOutput. */ @Override protected WrappedOutput onRequestFailure(Exception e, WrappedInput wrappedInput, @Nullable JRestlessContainerRequest jRestlessContainerRequest) { LOG.error("Request failed", e); OutputEvent outputEvent = OutputEvent.emptyResult(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); return new WrappedOutput(outputEvent, null, Response.Status.INTERNAL_SERVER_ERROR); }
@Test public void testOnRequestFailure_ShouldReturnEmptyOutputEvent() { FnRequestHandler.WrappedInput wrappedInput = new FnRequestHandler.WrappedInput( new DefaultInputEvent().getInputEvent(), new ByteArrayInputStream(new byte[]{})); RuntimeException exception = new RuntimeException("Testing that onRequestFailure works"); FnRequestHandler.WrappedOutput output = requestHandler.onRequestFailure(exception, wrappedInput, null); assertFalse(output.getOutputEvent().isSuccess()); }
evt.writeToOutput(bos); if (evt.isSuccess()) { response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), evt.getStatus().getCode(), evt.getStatus().name())); } else { response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), evt.getStatus().getCode(), evt.getStatus().name())); evt.getHeaders().asMap().forEach((k, vs) -> vs.forEach(v -> response.addHeader(k, v))); evt.getContentType().ifPresent((ct) -> response.setHeader(CONTENT_TYPE_HEADER, ct)); response.setHeader("Content-length", String.valueOf(data.length));
private TestOutput(OutputEvent from) throws IOException { this.from = Objects.requireNonNull(from, "from"); ByteArrayOutputStream bos = new ByteArrayOutputStream(); from.writeToOutput(bos); body = bos.toByteArray(); }
@Override public Status getStatus() { return from.getStatus(); }
@Override public Optional<OutputEvent> wrapFunctionResult(InvocationContext ctx, MethodWrapper method, Object value) { if (method.getReturnType().getParameterClass().equals(Void.class)) { return Optional.of(OutputEvent.emptyResult(OutputEvent.Status.Success)); } else { return Optional.empty(); } } }
@Override public Optional<OutputEvent> wrapFunctionResult(InvocationContext ctx, MethodWrapper method, Object value) { try { return Optional.of(OutputEvent.fromBytes(objectMapper(ctx).writeValueAsBytes(value), OutputEvent.Status.Success, "application/json")); } catch (JsonProcessingException e) { throw new RuntimeException("Failed to render response to JSON", e); } }
void writeEvent(OutputEvent evt) { try { evt.writeToOutput(out); } catch (IOException e) { throw new FunctionOutputHandlingException("error writing event", e); } }
@Override public Status getStatus() { return a.getStatus(); }
@Test public void testResponseWriter_WithContentHeader_ShouldUseContentHeaderGiven() throws IOException { Map<String, List<String>> headers = new HashMap<>(); headers.put("Content-Type", Collections.singletonList(MediaType.TEXT_HTML)); SimpleRequestHandler.SimpleResponseWriter<FnRequestHandler.WrappedOutput> responseWriter = requestHandler.createResponseWriter(null); responseWriter.writeResponse(Response.Status.OK, headers, new ByteArrayOutputStream()); Assert.assertTrue(responseWriter.getResponse().getOutputEvent().getContentType().get().equals(MediaType.TEXT_HTML)); }
/** * The content type of the response. * <p> * * @return The name of the content type. */ default Optional<String> getContentType(){ return getHeaders().get(CONTENT_TYPE_HEADER); }
/** * Create an output event from a byte array * * @param bytes the byte array to write to the output * @param status the status code to report * @param contentType the content type to present on HTTP responses * @return a new output event */ static OutputEvent fromBytes(byte[] bytes, Status status, String contentType) { return fromBytes(bytes, status, contentType, Headers.emptyHeaders()); }
@Test public void testResponseWriter_WithoutContentHeader_ShouldDefaultToApplicationJson() throws IOException { Map<String, List<String>> headers = new HashMap<>(); SimpleRequestHandler.SimpleResponseWriter<FnRequestHandler.WrappedOutput> responseWriter = requestHandler.createResponseWriter(null); responseWriter.writeResponse(Response.Status.OK, headers, new ByteArrayOutputStream()); Assert.assertTrue(responseWriter.getResponse().getOutputEvent().getContentType().get().equals(MediaType.APPLICATION_JSON)); }