Skip to content

Commit

Permalink
Add multipart support to HttpServer and WebApplicationInitializer
Browse files Browse the repository at this point in the history
Issue: SPR-14546
  • Loading branch information
sdeleuze committed Oct 13, 2016
1 parent f59370e commit a15cc85
Show file tree
Hide file tree
Showing 15 changed files with 280 additions and 20 deletions.
Expand Up @@ -22,12 +22,14 @@
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.util.Assert;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.multipart.reactive.MultipartResolver;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.adapter.HttpWebHandlerAdapter;
Expand Down Expand Up @@ -91,7 +93,9 @@ protected void registerDispatcherHandler(ServletContext servletContext) {
Assert.notNull(dispatcherHandler,
"createDispatcherHandler() did not return a WebHandler for servlet [" + servletName + "]");

ServletHttpHandlerAdapter handlerAdapter = createHandlerAdapter(dispatcherHandler);
MultipartResolver multipartResolver = createMultipartResolver(applicationContext);

ServletHttpHandlerAdapter handlerAdapter = createHandlerAdapter(dispatcherHandler, multipartResolver);
Assert.notNull(handlerAdapter,
"createHttpHandler() did not return a ServletHttpHandlerAdapter for servlet [" + servletName + "]");

Expand Down Expand Up @@ -145,14 +149,26 @@ protected WebHandler createDispatcherHandler(ApplicationContext applicationConte
return new DispatcherHandler(applicationContext);
}

/**
* Create a {@link MultipartResolver} with the specified {@link ApplicationContext}.
*/
protected MultipartResolver createMultipartResolver(ApplicationContext applicationContext) {
try {
return applicationContext.getBean(MultipartResolver.class);
}
catch (NoSuchBeanDefinitionException nsbe) {
}
return null;
}

/**
* Create a {@link ServletHttpHandlerAdapter}.
* <p>Default implementation returns a {@code ServletHttpHandlerAdapter} with the provided
* {@code webHandler}.
*/
protected ServletHttpHandlerAdapter createHandlerAdapter(WebHandler webHandler) {
protected ServletHttpHandlerAdapter createHandlerAdapter(WebHandler webHandler, MultipartResolver multipartResolver) {
HttpHandler httpHandler = new HttpWebHandlerAdapter(webHandler);
return new ServletHttpHandlerAdapter(httpHandler);
return new ServletHttpHandlerAdapter(httpHandler, multipartResolver);
}

/**
Expand Down
Expand Up @@ -24,6 +24,7 @@
import org.springframework.http.server.reactive.ServletHttpHandlerAdapter;
import org.springframework.util.Assert;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.multipart.reactive.MultipartResolver;

/**
* Base class for {@link org.springframework.web.WebApplicationInitializer}
Expand Down Expand Up @@ -59,7 +60,7 @@ public void onStartup(ServletContext servletContext) throws ServletException {
* returned from {@link #getServletMappings()}.
* <p>Further customization can be achieved by overriding {@link
* #customizeRegistration(ServletRegistration.Dynamic)} or
* {@link #createServlet(HttpHandler)}.
* {@link #createServlet(HttpHandler, MultipartResolver)}.
* @param servletContext the context to register the servlet against
*/
protected void registerHandlerAdapter(ServletContext servletContext) {
Expand All @@ -70,7 +71,9 @@ protected void registerHandlerAdapter(ServletContext servletContext) {
Assert.notNull(httpHandler,
"createHttpHandler() did not return a HttpHandler for servlet [" + servletName + "]");

ServletHttpHandlerAdapter servlet = createServlet(httpHandler);
MultipartResolver multipartResolver = createMultipartResolver();

ServletHttpHandlerAdapter servlet = createServlet(httpHandler, multipartResolver);
Assert.notNull(servlet,
"createHttpHandler() did not return a ServletHttpHandlerAdapter for servlet [" + servletName + "]");

Expand Down Expand Up @@ -100,13 +103,18 @@ protected String getServletName() {
*/
protected abstract HttpHandler createHttpHandler();

/**
* Create the {@link MultipartResolver}.
*/
protected abstract MultipartResolver createMultipartResolver();

/**
* Create a {@link ServletHttpHandlerAdapter} with the specified .
* <p>Default implementation returns a {@code ServletHttpHandlerAdapter} with the provided
* {@code httpHandler}.
*/
protected ServletHttpHandlerAdapter createServlet(HttpHandler httpHandler) {
return new ServletHttpHandlerAdapter(httpHandler);
protected ServletHttpHandlerAdapter createServlet(HttpHandler httpHandler, MultipartResolver multipartResolver) {
return new ServletHttpHandlerAdapter(httpHandler, multipartResolver);
}

/**
Expand Down
Expand Up @@ -26,6 +26,7 @@

import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.reactive.MultipartResolver;

/**
* Adapt {@link HttpHandler} to the Reactor Netty channel handling function.
Expand All @@ -39,17 +40,24 @@ public class ReactorHttpHandlerAdapter implements Function<HttpChannel, Mono<Voi

private final HttpHandler delegate;

private final MultipartResolver multipartResolver;


public ReactorHttpHandlerAdapter(HttpHandler delegate) {
this(delegate, null);
}

public ReactorHttpHandlerAdapter(HttpHandler delegate, MultipartResolver multipartResolver) {
Assert.notNull(delegate, "HttpHandler delegate is required");
this.delegate = delegate;
this.multipartResolver = multipartResolver;
}


@Override
public Mono<Void> apply(HttpChannel channel) {
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(channel.delegate().alloc());
ReactorServerHttpRequest adaptedRequest = new ReactorServerHttpRequest(channel, bufferFactory);
ReactorServerHttpRequest adaptedRequest = new ReactorServerHttpRequest(channel, bufferFactory, this.multipartResolver);
ReactorServerHttpResponse adaptedResponse = new ReactorServerHttpResponse(channel, bufferFactory);

return this.delegate.handle(adaptedRequest, adaptedResponse)
Expand Down
Expand Up @@ -30,6 +30,7 @@

import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.reactive.MultipartResolver;

/**
* Adapt {@link HttpHandler} to the RxNetty {@link RequestHandler}.
Expand All @@ -43,17 +44,25 @@ public class RxNettyHttpHandlerAdapter implements RequestHandler<ByteBuf, ByteBu

private final HttpHandler delegate;

private final MultipartResolver multipartResolver;


public RxNettyHttpHandlerAdapter(HttpHandler delegate) {
this(delegate, null);

}

public RxNettyHttpHandlerAdapter(HttpHandler delegate, MultipartResolver multipartResolver) {
Assert.notNull(delegate, "HttpHandler delegate is required");
this.delegate = delegate;
this.multipartResolver = multipartResolver;
}


@Override
public Observable<Void> handle(HttpServerRequest<ByteBuf> request, HttpServerResponse<ByteBuf> response) {
NettyDataBufferFactory bufferFactory = new NettyDataBufferFactory(response.unsafeNettyChannel().alloc());
RxNettyServerHttpRequest adaptedRequest = new RxNettyServerHttpRequest(request, bufferFactory);
RxNettyServerHttpRequest adaptedRequest = new RxNettyServerHttpRequest(request, bufferFactory, this.multipartResolver);
RxNettyServerHttpResponse adaptedResponse = new RxNettyServerHttpResponse(response, bufferFactory);

Publisher<Void> result = this.delegate.handle(adaptedRequest, adaptedResponse)
Expand Down
Expand Up @@ -32,6 +32,7 @@
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.reactive.MultipartResolver;

/**
* Adapt {@link HttpHandler} to an {@link HttpServlet} using Servlet Async
Expand All @@ -52,6 +53,8 @@ public class ServletHttpHandlerAdapter extends HttpServlet {

private final HttpHandler handler;

private final MultipartResolver multipartResolver;

// Servlet is based on blocking I/O, hence the usage of non-direct, heap-based buffers
// (i.e. 'false' as constructor argument)
private DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(false);
Expand All @@ -62,10 +65,21 @@ public class ServletHttpHandlerAdapter extends HttpServlet {
/**
* Create a new {@code ServletHttpHandlerAdapter} with the given HTTP handler.
* @param handler the handler
*/
*/
public ServletHttpHandlerAdapter(HttpHandler handler) {
this(handler, null);
}

/**
* Create a new {@code ServletHttpHandlerAdapter} with the given HTTP handler and multipart
* resolver.
* @param handler the handler
* @param multipartResolver the multipart resolver
*/
public ServletHttpHandlerAdapter(HttpHandler handler, MultipartResolver multipartResolver) {
Assert.notNull(handler, "HttpHandler must not be null");
this.handler = handler;
this.multipartResolver = multipartResolver;
}


Expand All @@ -86,7 +100,7 @@ protected void service(HttpServletRequest servletRequest, HttpServletResponse se

AsyncContext asyncContext = servletRequest.startAsync();
ServletServerHttpRequest request = new ServletServerHttpRequest(
servletRequest, this.dataBufferFactory, this.bufferSize);
servletRequest, this.dataBufferFactory, this.bufferSize, this.multipartResolver);
ServletServerHttpResponse response = new ServletServerHttpResponse(
servletResponse, this.dataBufferFactory, this.bufferSize);
HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(asyncContext);
Expand Down
Expand Up @@ -25,6 +25,7 @@
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.util.Assert;
import org.springframework.web.multipart.reactive.MultipartResolver;

/**
* Adapt {@link HttpHandler} to the Undertow {@link io.undertow.server.HttpHandler}.
Expand All @@ -41,12 +42,15 @@ public class UndertowHttpHandlerAdapter implements io.undertow.server.HttpHandle

private final HttpHandler delegate;

private final MultipartResolver multipartResolver;

private DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory(false);


public UndertowHttpHandlerAdapter(HttpHandler delegate) {
public UndertowHttpHandlerAdapter(HttpHandler delegate, MultipartResolver multipartResolver) {
Assert.notNull(delegate, "HttpHandler delegate is required");
this.delegate = delegate;
this.multipartResolver = multipartResolver;
}


Expand All @@ -58,7 +62,7 @@ public void setDataBufferFactory(DataBufferFactory dataBufferFactory) {

@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
ServerHttpRequest request = new UndertowServerHttpRequest(exchange, this.dataBufferFactory);
ServerHttpRequest request = new UndertowServerHttpRequest(exchange, this.dataBufferFactory, this.multipartResolver);
ServerHttpResponse response = new UndertowServerHttpResponse(exchange, this.dataBufferFactory);

this.delegate.handle(request, response).subscribe(new Subscriber<Void>() {
Expand Down
Expand Up @@ -30,7 +30,7 @@
import org.springframework.http.server.reactive.bootstrap.TomcatHttpServer;
import org.springframework.http.server.reactive.bootstrap.UndertowHttpServer;
import org.springframework.util.SocketUtils;

import org.springframework.web.multipart.reactive.MultipartResolver;

@RunWith(Parameterized.class)
public abstract class AbstractHttpHandlerIntegrationTests {
Expand Down Expand Up @@ -59,12 +59,17 @@ public void setup() throws Exception {
this.port = SocketUtils.findAvailableTcpPort();
this.server.setPort(this.port);
this.server.setHandler(createHttpHandler());
this.server.setMultipartResolver(createMultipartResolver());
this.server.afterPropertiesSet();
this.server.start();
}

protected abstract HttpHandler createHttpHandler();

protected MultipartResolver createMultipartResolver() {
return null;
}

@After
public void tearDown() throws Exception {
this.server.stop();
Expand Down

0 comments on commit a15cc85

Please sign in to comment.