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;
021
022import io.undertow.server.HttpServerExchange;
023import org.apache.commons.logging.Log;
024import org.reactivestreams.Subscriber;
025import org.reactivestreams.Subscription;
026
027import org.springframework.core.io.buffer.DataBufferFactory;
028import org.springframework.core.io.buffer.DefaultDataBufferFactory;
029import org.springframework.http.HttpLogging;
030import org.springframework.http.HttpMethod;
031import org.springframework.util.Assert;
032
033/**
034 * Adapt {@link HttpHandler} to the Undertow {@link io.undertow.server.HttpHandler}.
035 *
036 * @author Marek Hawrylczak
037 * @author Rossen Stoyanchev
038 * @author Arjen Poutsma
039 * @since 5.0
040 */
041public class UndertowHttpHandlerAdapter implements io.undertow.server.HttpHandler {
042
043        private static final Log logger = HttpLogging.forLogName(UndertowHttpHandlerAdapter.class);
044
045
046        private final HttpHandler httpHandler;
047
048        private DataBufferFactory bufferFactory = new DefaultDataBufferFactory(false);
049
050
051        public UndertowHttpHandlerAdapter(HttpHandler httpHandler) {
052                Assert.notNull(httpHandler, "HttpHandler must not be null");
053                this.httpHandler = httpHandler;
054        }
055
056
057        public void setDataBufferFactory(DataBufferFactory bufferFactory) {
058                Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
059                this.bufferFactory = bufferFactory;
060        }
061
062        public DataBufferFactory getDataBufferFactory() {
063                return this.bufferFactory;
064        }
065
066
067        @Override
068        public void handleRequest(HttpServerExchange exchange) {
069                UndertowServerHttpRequest request = null;
070                try {
071                        request = new UndertowServerHttpRequest(exchange, getDataBufferFactory());
072                }
073                catch (URISyntaxException ex) {
074                        if (logger.isWarnEnabled()) {
075                                logger.debug("Failed to get request URI: " + ex.getMessage());
076                        }
077                        exchange.setStatusCode(400);
078                        return;
079                }
080                ServerHttpResponse response = new UndertowServerHttpResponse(exchange, getDataBufferFactory(), request);
081
082                if (request.getMethod() == HttpMethod.HEAD) {
083                        response = new HttpHeadResponseDecorator(response);
084                }
085
086                HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(exchange, request);
087                this.httpHandler.handle(request, response).subscribe(resultSubscriber);
088        }
089
090
091        private class HandlerResultSubscriber implements Subscriber<Void> {
092
093                private final HttpServerExchange exchange;
094
095                private final String logPrefix;
096
097
098                public HandlerResultSubscriber(HttpServerExchange exchange, UndertowServerHttpRequest request) {
099                        this.exchange = exchange;
100                        this.logPrefix = request.getLogPrefix();
101                }
102
103                @Override
104                public void onSubscribe(Subscription subscription) {
105                        subscription.request(Long.MAX_VALUE);
106                }
107
108                @Override
109                public void onNext(Void aVoid) {
110                        // no-op
111                }
112
113                @Override
114                public void onError(Throwable ex) {
115                        logger.trace(this.logPrefix + "Failed to complete: " + ex.getMessage());
116                        if (this.exchange.isResponseStarted()) {
117                                try {
118                                        logger.debug(this.logPrefix + "Closing connection");
119                                        this.exchange.getConnection().close();
120                                }
121                                catch (IOException ex2) {
122                                        // ignore
123                                }
124                        }
125                        else {
126                                logger.debug(this.logPrefix + "Setting HttpServerExchange status to 500 Server Error");
127                                this.exchange.setStatusCode(500);
128                                this.exchange.endExchange();
129                        }
130                }
131
132                @Override
133                public void onComplete() {
134                        logger.trace(this.logPrefix + "Handling completed");
135                        this.exchange.endExchange();
136                }
137        }
138
139}