001/*
002 * Copyright 2002-2018 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 java.io.IOException;
020import java.net.URISyntaxException;
021import java.nio.ByteBuffer;
022import java.nio.charset.Charset;
023
024import javax.servlet.AsyncContext;
025import javax.servlet.ServletResponse;
026import javax.servlet.http.HttpServletRequest;
027import javax.servlet.http.HttpServletResponse;
028
029import org.eclipse.jetty.http.HttpFields;
030import org.eclipse.jetty.server.HttpOutput;
031import org.eclipse.jetty.server.Request;
032import org.eclipse.jetty.server.Response;
033
034import org.springframework.core.io.buffer.DataBuffer;
035import org.springframework.core.io.buffer.DataBufferFactory;
036import org.springframework.http.HttpHeaders;
037import org.springframework.http.MediaType;
038import org.springframework.util.Assert;
039
040/**
041 * {@link ServletHttpHandlerAdapter} extension that uses Jetty APIs for writing
042 * to the response with {@link ByteBuffer}.
043 *
044 * @author Violeta Georgieva
045 * @author Brian Clozel
046 * @since 5.0
047 * @see org.springframework.web.server.adapter.AbstractReactiveWebInitializer
048 */
049public class JettyHttpHandlerAdapter extends ServletHttpHandlerAdapter {
050
051        public JettyHttpHandlerAdapter(HttpHandler httpHandler) {
052                super(httpHandler);
053        }
054
055
056        @Override
057        protected ServletServerHttpRequest createRequest(HttpServletRequest request, AsyncContext context)
058                        throws IOException, URISyntaxException {
059
060                Assert.notNull(getServletPath(), "Servlet path is not initialized");
061                return new JettyServerHttpRequest(request, context, getServletPath(), getDataBufferFactory(), getBufferSize());
062        }
063
064        @Override
065        protected ServletServerHttpResponse createResponse(HttpServletResponse response,
066                        AsyncContext context, ServletServerHttpRequest request) throws IOException {
067
068                return new JettyServerHttpResponse(
069                                response, context, getDataBufferFactory(), getBufferSize(), request);
070        }
071
072
073        private static final class JettyServerHttpRequest extends ServletServerHttpRequest {
074
075                JettyServerHttpRequest(HttpServletRequest request, AsyncContext asyncContext,
076                                String servletPath, DataBufferFactory bufferFactory, int bufferSize)
077                                throws IOException, URISyntaxException {
078
079                        super(createHeaders(request), request, asyncContext, servletPath, bufferFactory, bufferSize);
080                }
081
082                private static HttpHeaders createHeaders(HttpServletRequest request) {
083                        HttpFields fields = ((Request) request).getMetaData().getFields();
084                        return new HttpHeaders(new JettyHeadersAdapter(fields));
085                }
086        }
087
088
089        private static final class JettyServerHttpResponse extends ServletServerHttpResponse {
090
091                JettyServerHttpResponse(HttpServletResponse response, AsyncContext asyncContext,
092                                DataBufferFactory bufferFactory, int bufferSize, ServletServerHttpRequest request)
093                                throws IOException {
094
095                        super(createHeaders(response), response, asyncContext, bufferFactory, bufferSize, request);
096                }
097
098                private static HttpHeaders createHeaders(HttpServletResponse response) {
099                        HttpFields fields = ((Response) response).getHttpFields();
100                        return new HttpHeaders(new JettyHeadersAdapter(fields));
101                }
102
103                @Override
104                protected void applyHeaders() {
105                        HttpServletResponse response = getNativeResponse();
106                        MediaType contentType = null;
107                        try {
108                                contentType = getHeaders().getContentType();
109                        }
110                        catch (Exception ex) {
111                                String rawContentType = getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
112                                response.setContentType(rawContentType);
113                        }
114                        if (response.getContentType() == null && contentType != null) {
115                                response.setContentType(contentType.toString());
116                        }
117                        Charset charset = (contentType != null ? contentType.getCharset() : null);
118                        if (response.getCharacterEncoding() == null && charset != null) {
119                                response.setCharacterEncoding(charset.name());
120                        }
121                        long contentLength = getHeaders().getContentLength();
122                        if (contentLength != -1) {
123                                response.setContentLengthLong(contentLength);
124                        }
125                }
126
127                @Override
128                protected int writeToOutputStream(DataBuffer dataBuffer) throws IOException {
129                        ByteBuffer input = dataBuffer.asByteBuffer();
130                        int len = input.remaining();
131                        ServletResponse response = getNativeResponse();
132                        ((HttpOutput) response.getOutputStream()).write(input);
133                        return len;
134                }
135        }
136
137}