001/*
002 * Copyright 2012-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 *      http://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.boot.autoconfigure.web.embedded;
018
019import java.time.Duration;
020
021import io.undertow.UndertowOptions;
022
023import org.springframework.boot.autoconfigure.web.ServerProperties;
024import org.springframework.boot.cloud.CloudPlatform;
025import org.springframework.boot.context.properties.PropertyMapper;
026import org.springframework.boot.web.embedded.undertow.ConfigurableUndertowWebServerFactory;
027import org.springframework.boot.web.server.WebServerFactoryCustomizer;
028import org.springframework.core.Ordered;
029import org.springframework.core.env.Environment;
030import org.springframework.util.unit.DataSize;
031
032/**
033 * Customization for Undertow-specific features common for both Servlet and Reactive
034 * servers.
035 *
036 * @author Brian Clozel
037 * @author Yulin Qin
038 * @author Stephane Nicoll
039 * @author Phillip Webb
040 * @since 2.0.0
041 */
042public class UndertowWebServerFactoryCustomizer implements
043                WebServerFactoryCustomizer<ConfigurableUndertowWebServerFactory>, Ordered {
044
045        private final Environment environment;
046
047        private final ServerProperties serverProperties;
048
049        public UndertowWebServerFactoryCustomizer(Environment environment,
050                        ServerProperties serverProperties) {
051                this.environment = environment;
052                this.serverProperties = serverProperties;
053        }
054
055        @Override
056        public int getOrder() {
057                return 0;
058        }
059
060        @Override
061        public void customize(ConfigurableUndertowWebServerFactory factory) {
062                ServerProperties properties = this.serverProperties;
063                ServerProperties.Undertow undertowProperties = properties.getUndertow();
064                ServerProperties.Undertow.Accesslog accesslogProperties = undertowProperties
065                                .getAccesslog();
066                PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
067                propertyMapper.from(undertowProperties::getBufferSize).whenNonNull()
068                                .asInt(DataSize::toBytes).to(factory::setBufferSize);
069                propertyMapper.from(undertowProperties::getIoThreads).to(factory::setIoThreads);
070                propertyMapper.from(undertowProperties::getWorkerThreads)
071                                .to(factory::setWorkerThreads);
072                propertyMapper.from(undertowProperties::getDirectBuffers)
073                                .to(factory::setUseDirectBuffers);
074                propertyMapper.from(accesslogProperties::isEnabled)
075                                .to(factory::setAccessLogEnabled);
076                propertyMapper.from(accesslogProperties::getDir)
077                                .to(factory::setAccessLogDirectory);
078                propertyMapper.from(accesslogProperties::getPattern)
079                                .to(factory::setAccessLogPattern);
080                propertyMapper.from(accesslogProperties::getPrefix)
081                                .to(factory::setAccessLogPrefix);
082                propertyMapper.from(accesslogProperties::getSuffix)
083                                .to(factory::setAccessLogSuffix);
084                propertyMapper.from(accesslogProperties::isRotate)
085                                .to(factory::setAccessLogRotate);
086                propertyMapper.from(this::getOrDeduceUseForwardHeaders)
087                                .to(factory::setUseForwardHeaders);
088                propertyMapper.from(properties::getMaxHttpHeaderSize).whenNonNull()
089                                .asInt(DataSize::toBytes).when(this::isPositive)
090                                .to((maxHttpHeaderSize) -> customizeMaxHttpHeaderSize(factory,
091                                                maxHttpHeaderSize));
092                propertyMapper.from(undertowProperties::getMaxHttpPostSize)
093                                .asInt(DataSize::toBytes).when(this::isPositive)
094                                .to((maxHttpPostSize) -> customizeMaxHttpPostSize(factory,
095                                                maxHttpPostSize));
096                propertyMapper.from(properties::getConnectionTimeout)
097                                .to((connectionTimeout) -> customizeConnectionTimeout(factory,
098                                                connectionTimeout));
099                factory.addDeploymentInfoCustomizers((deploymentInfo) -> deploymentInfo
100                                .setEagerFilterInit(undertowProperties.isEagerFilterInit()));
101        }
102
103        private boolean isPositive(Number value) {
104                return value.longValue() > 0;
105        }
106
107        private void customizeConnectionTimeout(ConfigurableUndertowWebServerFactory factory,
108                        Duration connectionTimeout) {
109                factory.addBuilderCustomizers((builder) -> builder.setSocketOption(
110                                UndertowOptions.NO_REQUEST_TIMEOUT, (int) connectionTimeout.toMillis()));
111        }
112
113        private void customizeMaxHttpHeaderSize(ConfigurableUndertowWebServerFactory factory,
114                        int maxHttpHeaderSize) {
115                factory.addBuilderCustomizers((builder) -> builder
116                                .setServerOption(UndertowOptions.MAX_HEADER_SIZE, maxHttpHeaderSize));
117        }
118
119        private void customizeMaxHttpPostSize(ConfigurableUndertowWebServerFactory factory,
120                        long maxHttpPostSize) {
121                factory.addBuilderCustomizers((builder) -> builder
122                                .setServerOption(UndertowOptions.MAX_ENTITY_SIZE, maxHttpPostSize));
123        }
124
125        private boolean getOrDeduceUseForwardHeaders() {
126                if (this.serverProperties.isUseForwardHeaders() != null) {
127                        return this.serverProperties.isUseForwardHeaders();
128                }
129                CloudPlatform platform = CloudPlatform.getActive(this.environment);
130                return platform != null && platform.isUsingForwardHeaders();
131        }
132
133}