spring ServletServerHttpResponse 源码

  • 2022-08-08
  • 浏览 (393)

spring ServletServerHttpResponse 代码

文件路径:/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpResponse.java

/*
 * Copyright 2002-2020 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.http.server;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import jakarta.servlet.http.HttpServletResponse;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatusCode;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

/**
 * {@link ServerHttpResponse} implementation that is based on a {@link HttpServletResponse}.
 *
 * @author Arjen Poutsma
 * @author Rossen Stoyanchev
 * @since 3.0
 */
public class ServletServerHttpResponse implements ServerHttpResponse {

	private final HttpServletResponse servletResponse;

	private final HttpHeaders headers;

	private boolean headersWritten = false;

	private boolean bodyUsed = false;

	@Nullable
	private HttpHeaders readOnlyHeaders;


	/**
	 * Construct a new instance of the ServletServerHttpResponse based on the given {@link HttpServletResponse}.
	 * @param servletResponse the servlet response
	 */
	public ServletServerHttpResponse(HttpServletResponse servletResponse) {
		Assert.notNull(servletResponse, "HttpServletResponse must not be null");
		this.servletResponse = servletResponse;
		this.headers = new ServletResponseHttpHeaders();
	}


	/**
	 * Return the {@code HttpServletResponse} this object is based on.
	 */
	public HttpServletResponse getServletResponse() {
		return this.servletResponse;
	}

	@Override
	public void setStatusCode(HttpStatusCode status) {
		Assert.notNull(status, "HttpStatus must not be null");
		this.servletResponse.setStatus(status.value());
	}

	@Override
	public HttpHeaders getHeaders() {
		if (this.readOnlyHeaders != null) {
			return this.readOnlyHeaders;
		}
		else if (this.headersWritten) {
			this.readOnlyHeaders = HttpHeaders.readOnlyHttpHeaders(this.headers);
			return this.readOnlyHeaders;
		}
		else {
			return this.headers;
		}
	}

	@Override
	public OutputStream getBody() throws IOException {
		this.bodyUsed = true;
		writeHeaders();
		return this.servletResponse.getOutputStream();
	}

	@Override
	public void flush() throws IOException {
		writeHeaders();
		if (this.bodyUsed) {
			this.servletResponse.flushBuffer();
		}
	}

	@Override
	public void close() {
		writeHeaders();
	}

	private void writeHeaders() {
		if (!this.headersWritten) {
			getHeaders().forEach((headerName, headerValues) -> {
				for (String headerValue : headerValues) {
					this.servletResponse.addHeader(headerName, headerValue);
				}
			});
			// HttpServletResponse exposes some headers as properties: we should include those if not already present
			if (this.servletResponse.getContentType() == null && this.headers.getContentType() != null) {
				this.servletResponse.setContentType(this.headers.getContentType().toString());
			}
			if (this.servletResponse.getCharacterEncoding() == null && this.headers.getContentType() != null &&
					this.headers.getContentType().getCharset() != null) {
				this.servletResponse.setCharacterEncoding(this.headers.getContentType().getCharset().name());
			}
			long contentLength = getHeaders().getContentLength();
			if (contentLength != -1) {
				this.servletResponse.setContentLengthLong(contentLength);
			}
			this.headersWritten = true;
		}
	}


	/**
	 * Extends HttpHeaders with the ability to look up headers already present in
	 * the underlying HttpServletResponse.
	 *
	 * <p>The intent is merely to expose what is available through the HttpServletResponse
	 * i.e. the ability to look up specific header values by name. All other
	 * map-related operations (e.g. iteration, removal, etc) apply only to values
	 * added directly through HttpHeaders methods.
	 *
	 * @since 4.0.3
	 */
	private class ServletResponseHttpHeaders extends HttpHeaders {

		private static final long serialVersionUID = 3410708522401046302L;

		@Override
		public boolean containsKey(Object key) {
			return (super.containsKey(key) || (get(key) != null));
		}

		@Override
		@Nullable
		public String getFirst(String headerName) {
			if (headerName.equalsIgnoreCase(CONTENT_TYPE)) {
				// Content-Type is written as an override so check super first
				String value = super.getFirst(headerName);
				return (value != null ? value : servletResponse.getHeader(headerName));
			}
			else {
				String value = servletResponse.getHeader(headerName);
				return (value != null ? value : super.getFirst(headerName));
			}
		}

		@Override
		public List<String> get(Object key) {
			Assert.isInstanceOf(String.class, key, "Key must be a String-based header name");

			String headerName = (String) key;
			if (headerName.equalsIgnoreCase(CONTENT_TYPE)) {
				// Content-Type is written as an override so don't merge
				return Collections.singletonList(getFirst(headerName));
			}

			Collection<String> values1 = servletResponse.getHeaders(headerName);
			if (headersWritten) {
				return new ArrayList<>(values1);
			}
			boolean isEmpty1 = CollectionUtils.isEmpty(values1);

			List<String> values2 = super.get(key);
			boolean isEmpty2 = CollectionUtils.isEmpty(values2);

			if (isEmpty1 && isEmpty2) {
				return null;
			}

			List<String> values = new ArrayList<>();
			if (!isEmpty1) {
				values.addAll(values1);
			}
			if (!isEmpty2) {
				values.addAll(values2);
			}
			return values;
		}
	}

}

相关信息

spring 源码目录

相关文章

spring DefaultPathContainer 源码

spring DefaultRequestPath 源码

spring DelegatingServerHttpResponse 源码

spring PathContainer 源码

spring RequestPath 源码

spring ServerHttpAsyncRequestControl 源码

spring ServerHttpRequest 源码

spring ServerHttpResponse 源码

spring ServletServerHttpAsyncRequestControl 源码

spring ServletServerHttpRequest 源码

0  赞