private static Writer getWriter(HttpOutputMessage outputMessage) throws IOException { return new OutputStreamWriter(outputMessage.getBody(), getCharset(outputMessage.getHeaders())); }
/** * Set the "X-Protobuf-*" HTTP headers when responding with a message of * content type "application/x-protobuf" * <p><b>Note:</b> <code>outputMessage.getBody()</code> should not have been called * before because it writes HTTP headers (making them read only).</p> */ private void setProtoHeader(HttpOutputMessage response, Message message) { response.getHeaders().set(X_PROTOBUF_SCHEMA_HEADER, message.getDescriptorForType().getFile().getName()); response.getHeaders().set(X_PROTOBUF_MESSAGE_HEADER, message.getDescriptorForType().getFullName()); }
@Override protected final void writeInternal(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { try { writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody())); } catch (IOException | HttpMessageConversionException ex) { throw ex; } catch (Exception ex) { throw new HttpMessageNotWritableException("Could not marshal [" + t + "]: " + ex.getMessage(), ex); } }
private void writeForm(MultiValueMap<String, Object> formData, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException { contentType = getMediaType(contentType); outputMessage.getHeaders().setContentType(contentType); Charset charset = contentType.getCharset(); Assert.notNull(charset, "No charset"); // should never occur final byte[] bytes = serializeForm(formData, charset).getBytes(charset); outputMessage.getHeaders().setContentLength(bytes.length); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(bytes, outputStream)); } else { StreamUtils.copy(bytes, outputMessage.getBody()); } }
@Override protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException { if (this.writeAcceptCharset) { outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets()); } Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType()); StreamUtils.copy(str, charset, outputMessage.getBody()); }
@Override public void write(Object o, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { outputMessage.getHeaders().setContentType(contentType); outputMessage.getBody(); // force a header write } }
@Override public void write(final BufferedImage image, @Nullable final MediaType contentType, final HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final MediaType selectedContentType = getContentType(contentType); outputMessage.getHeaders().setContentType(selectedContentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(image, selectedContentType, outputStream)); } else { writeInternal(image, selectedContentType, outputMessage.getBody()); } }
/** * Set the "X-Protobuf-*" HTTP headers when responding with a message of * content type "application/x-protobuf" * <p><b>Note:</b> <code>outputMessage.getBody()</code> should not have been called * before because it writes HTTP headers (making them read only).</p> */ private void setProtoHeader(HttpOutputMessage response, Message message) { response.getHeaders().set(X_PROTOBUF_SCHEMA_HEADER, message.getDescriptorForType().getFile().getName()); response.getHeaders().set(X_PROTOBUF_MESSAGE_HEADER, message.getDescriptorForType().getFullName()); }
@Override protected final void writeInternal(T t, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { try { writeToResult(t, outputMessage.getHeaders(), new StreamResult(outputMessage.getBody())); } catch (IOException | HttpMessageConversionException ex) { throw ex; } catch (Exception ex) { throw new HttpMessageNotWritableException("Could not marshal [" + t + "]: " + ex.getMessage(), ex); } }
/** * This implementation sets the default headers by calling {@link #addDefaultHeaders}, * and then calls {@link #writeInternal}. */ public final void write(final T t, @Nullable final Type type, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, type, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { writeInternal(t, type, outputMessage); outputMessage.getBody().flush(); } }
@Override protected void writeInternal(T wireFeed, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { Charset charset = (StringUtils.hasLength(wireFeed.getEncoding()) ? Charset.forName(wireFeed.getEncoding()) : DEFAULT_CHARSET); MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType != null) { contentType = new MediaType(contentType.getType(), contentType.getSubtype(), charset); outputMessage.getHeaders().setContentType(contentType); } WireFeedOutput feedOutput = new WireFeedOutput(); try { Writer writer = new OutputStreamWriter(outputMessage.getBody(), charset); feedOutput.output(wireFeed, writer); } catch (FeedException ex) { throw new HttpMessageNotWritableException("Could not write WireFeed: " + ex.getMessage(), ex); } }
protected void writeResourceRegion(ResourceRegion region, HttpOutputMessage outputMessage) throws IOException { Assert.notNull(region, "ResourceRegion must not be null"); HttpHeaders responseHeaders = outputMessage.getHeaders(); long start = region.getPosition(); long end = start + region.getCount() - 1; Long resourceLength = region.getResource().contentLength(); end = Math.min(end, resourceLength - 1); long rangeLength = end - start + 1; responseHeaders.add("Content-Range", "bytes " + start + '-' + end + '/' + resourceLength); responseHeaders.setContentLength(rangeLength); InputStream in = region.getResource().getInputStream(); try { StreamUtils.copyRange(in, outputMessage.getBody(), start, end); } finally { try { in.close(); } catch (IOException ex) { // ignore } } }
/** * This implementation sets the default headers by calling {@link #addDefaultHeaders}, * and then calls {@link #writeInternal}. */ @Override public final void write(final T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { final HttpHeaders headers = outputMessage.getHeaders(); addDefaultHeaders(headers, t, contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> writeInternal(t, new HttpOutputMessage() { @Override public OutputStream getBody() { return outputStream; } @Override public HttpHeaders getHeaders() { return headers; } })); } else { writeInternal(t, outputMessage); outputMessage.getBody().flush(); } }
private void writeMultipart(final MultiValueMap<String, Object> parts, HttpOutputMessage outputMessage) throws IOException { final byte[] boundary = generateMultipartBoundary(); Map<String, String> parameters = new LinkedHashMap<>(2); if (!isFilenameCharsetSet()) { parameters.put("charset", this.charset.name()); } parameters.put("boundary", new String(boundary, StandardCharsets.US_ASCII)); MediaType contentType = new MediaType(MediaType.MULTIPART_FORM_DATA, parameters); HttpHeaders headers = outputMessage.getHeaders(); headers.setContentType(contentType); if (outputMessage instanceof StreamingHttpOutputMessage) { StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) outputMessage; streamingOutputMessage.setBody(outputStream -> { writeParts(outputStream, parts, boundary); writeEnd(outputStream, boundary); }); } else { writeParts(outputMessage.getBody(), parts, boundary); writeEnd(outputMessage.getBody(), boundary); } }
@Override protected void writeInternal(String str, HttpOutputMessage outputMessage) throws IOException { if (this.writeAcceptCharset) { outputMessage.getHeaders().setAcceptCharset(getAcceptedCharsets()); } Charset charset = getContentTypeCharset(outputMessage.getHeaders().getContentType()); StreamUtils.copy(str, charset, outputMessage.getBody()); }
@SuppressWarnings("unchecked") private void writePart(String name, HttpEntity<?> partEntity, OutputStream os) throws IOException { Object partBody = partEntity.getBody(); if (partBody == null) { throw new IllegalStateException("Empty body for part '" + name + "': " + partEntity); } Class<?> partType = partBody.getClass(); HttpHeaders partHeaders = partEntity.getHeaders(); MediaType partContentType = partHeaders.getContentType(); for (HttpMessageConverter<?> messageConverter : this.partConverters) { if (messageConverter.canWrite(partType, partContentType)) { Charset charset = isFilenameCharsetSet() ? StandardCharsets.US_ASCII : this.charset; HttpOutputMessage multipartMessage = new MultipartHttpOutputMessage(os, charset); multipartMessage.getHeaders().setContentDispositionFormData(name, getFilename(partBody)); if (!partHeaders.isEmpty()) { multipartMessage.getHeaders().putAll(partHeaders); } ((HttpMessageConverter<Object>) messageConverter).write(partBody, partContentType, multipartMessage); return; } } throw new HttpMessageNotWritableException("Could not write request: no suitable HttpMessageConverter " + "found for request type [" + partType.getName() + "]"); }
@Test public void shouldHandleResponseHeaderAndBody() throws Exception { HttpHeaders responseHeaders = new HttpHeaders(); responseHeaders.set("header", "headerValue"); ResponseEntity<String> returnValue = new ResponseEntity<>("body", responseHeaders, HttpStatus.ACCEPTED); initStringMessageConversion(TEXT_PLAIN); processor.handleReturnValue(returnValue, returnTypeResponseEntity, mavContainer, webRequest); ArgumentCaptor<HttpOutputMessage> outputMessage = ArgumentCaptor.forClass(HttpOutputMessage.class); verify(stringHttpMessageConverter).write(eq("body"), eq(TEXT_PLAIN), outputMessage.capture()); assertTrue(mavContainer.isRequestHandled()); assertEquals("headerValue", outputMessage.getValue().getHeaders().get("header").get(0)); }
@Override protected void writeInternal(Message message, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { MediaType contentType = outputMessage.getHeaders().getContentType(); if (contentType == null) { contentType = getDefaultContentType(message); Assert.state(contentType != null, "No content type"); } Charset charset = contentType.getCharset(); if (charset == null) { charset = DEFAULT_CHARSET; } if (PROTOBUF.isCompatibleWith(contentType)) { setProtoHeader(outputMessage, message); CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputMessage.getBody()); message.writeTo(codedOutputStream); codedOutputStream.flush(); } else if (TEXT_PLAIN.isCompatibleWith(contentType)) { OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputMessage.getBody(), charset); TextFormat.print(message, outputStreamWriter); outputStreamWriter.flush(); outputMessage.getBody().flush(); } else if (this.protobufFormatSupport != null) { this.protobufFormatSupport.print(message, outputMessage.getBody(), contentType, charset); outputMessage.getBody().flush(); } }
@Test public void shouldHandleResourceByteRange() throws Exception { ResponseEntity<Resource> returnValue = ResponseEntity .ok(new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8))); servletRequest.addHeader("Range", "bytes=0-5"); given(resourceRegionMessageConverter.canWrite(any(), eq(null))).willReturn(true); given(resourceRegionMessageConverter.canWrite(any(), eq(APPLICATION_OCTET_STREAM))).willReturn(true); processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest); then(resourceRegionMessageConverter).should(times(1)).write( anyCollection(), eq(APPLICATION_OCTET_STREAM), argThat(outputMessage -> "bytes".equals(outputMessage.getHeaders().getFirst(HttpHeaders.ACCEPT_RANGES)))); assertEquals(206, servletResponse.getStatus()); }
@Test public void handleReturnTypeResourceByteRange() throws Exception { Resource returnValue = new ByteArrayResource("Content".getBytes(StandardCharsets.UTF_8)); servletRequest.addHeader("Range", "bytes=0-5"); given(resourceRegionMessageConverter.canWrite(any(), eq(null))).willReturn(true); given(resourceRegionMessageConverter.canWrite(any(), eq(MediaType.APPLICATION_OCTET_STREAM))).willReturn(true); processor.handleReturnValue(returnValue, returnTypeResource, mavContainer, webRequest); then(resourceRegionMessageConverter).should(times(1)).write( anyCollection(), eq(MediaType.APPLICATION_OCTET_STREAM), argThat(outputMessage -> "bytes".equals(outputMessage.getHeaders().getFirst(HttpHeaders.ACCEPT_RANGES)))); assertEquals(206, servletResponse.getStatus()); }