001/* 002 * Copyright 2002-2020 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.http.server.reactive; 018 019import org.reactivestreams.Publisher; 020import reactor.core.publisher.Flux; 021import reactor.core.publisher.Mono; 022 023import org.springframework.core.io.buffer.DataBuffer; 024import org.springframework.core.io.buffer.DataBufferUtils; 025import org.springframework.http.HttpHeaders; 026 027/** 028 * {@link ServerHttpResponse} decorator for HTTP HEAD requests. 029 * 030 * @author Rossen Stoyanchev 031 * @since 5.0 032 */ 033public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator { 034 035 036 public HttpHeadResponseDecorator(ServerHttpResponse delegate) { 037 super(delegate); 038 } 039 040 041 /** 042 * Consume and release the body without writing. 043 * <p>If the headers contain neither Content-Length nor Transfer-Encoding, 044 * count the bytes and set Content-Length. 045 */ 046 @Override 047 public final Mono<Void> writeWith(Publisher<? extends DataBuffer> body) { 048 if (shouldSetContentLength()) { 049 return Flux.from(body) 050 .reduce(0, (current, buffer) -> { 051 int next = current + buffer.readableByteCount(); 052 DataBufferUtils.release(buffer); 053 return next; 054 }) 055 .doOnNext(length -> getHeaders().setContentLength(length)) 056 .then(); 057 } 058 else { 059 return Flux.from(body) 060 .doOnNext(DataBufferUtils::release) 061 .then(); 062 } 063 } 064 065 private boolean shouldSetContentLength() { 066 return (getHeaders().getFirst(HttpHeaders.CONTENT_LENGTH) == null && 067 getHeaders().getFirst(HttpHeaders.TRANSFER_ENCODING) == null); 068 } 069 070 /** 071 * Invoke {@link #setComplete()} without writing. 072 * <p>RFC 7302 allows HTTP HEAD response without content-length and it's not 073 * something that can be computed on a streaming response. 074 */ 075 @Override 076 public final Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) { 077 // Not feasible to count bytes on potentially streaming response. 078 // RFC 7302 allows HEAD without content-length. 079 return setComplete(); 080 } 081 082}