/*
* Copyright 2002-2016 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
*
* http://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.reactive;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import io.undertow.connector.PooledByteBuffer;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.Cookie;
import io.undertow.util.HeaderValues;
import org.xnio.ChannelListener;
import org.xnio.channels.StreamSourceChannel;
import reactor.core.publisher.Flux;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
/**
* Adapt {@link ServerHttpRequest} to the Underow {@link HttpServerExchange}.
*
* @author Marek Hawrylczak
* @author Rossen Stoyanchev
*/
public class UndertowServerHttpRequest extends AbstractServerHttpRequest {
private final HttpServerExchange exchange;
private final RequestBodyPublisher body;
public UndertowServerHttpRequest(HttpServerExchange exchange,
DataBufferFactory dataBufferFactory) {
Assert.notNull(exchange, "'exchange' is required.");
this.exchange = exchange;
this.body = new RequestBodyPublisher(exchange, dataBufferFactory);
this.body.registerListener();
}
public HttpServerExchange getUndertowExchange() {
return this.exchange;
}
@Override
public HttpMethod getMethod() {
return HttpMethod.valueOf(this.getUndertowExchange().getRequestMethod().toString());
}
@Override
protected URI initUri() throws URISyntaxException {
return new URI(this.exchange.getRequestScheme(), null,
this.exchange.getHostName(), this.exchange.getHostPort(),
this.exchange.getRequestURI(), this.exchange.getQueryString(), null);
}
@Override
protected HttpHeaders initHeaders() {
HttpHeaders headers = new HttpHeaders();
for (HeaderValues values : this.getUndertowExchange().getRequestHeaders()) {
headers.put(values.getHeaderName().toString(), values);
}
return headers;
}
@Override
protected MultiValueMap<String, HttpCookie> initCookies() {
MultiValueMap<String, HttpCookie> cookies = new LinkedMultiValueMap<>();
for (String name : this.exchange.getRequestCookies().keySet()) {
Cookie cookie = this.exchange.getRequestCookies().get(name);
HttpCookie httpCookie = new HttpCookie(name, cookie.getValue());
cookies.add(name, httpCookie);
}
return cookies;
}
@Override
public Flux<DataBuffer> getBody() {
return Flux.from(this.body);
}
private static class RequestBodyPublisher extends AbstractRequestBodyPublisher {
private final ChannelListener<StreamSourceChannel> readListener =
new ReadListener();
private final ChannelListener<StreamSourceChannel> closeListener =
new CloseListener();
private final StreamSourceChannel requestChannel;
private final DataBufferFactory dataBufferFactory;
private final PooledByteBuffer pooledByteBuffer;
public RequestBodyPublisher(HttpServerExchange exchange,
DataBufferFactory dataBufferFactory) {
this.requestChannel = exchange.getRequestChannel();
this.pooledByteBuffer =
exchange.getConnection().getByteBufferPool().allocate();
this.dataBufferFactory = dataBufferFactory;
}
private void registerListener() {
this.requestChannel.getReadSetter().set(this.readListener);
this.requestChannel.getCloseSetter().set(this.closeListener);
this.requestChannel.resumeReads();
}
@Override
protected void checkOnDataAvailable() {
onDataAvailable();
}
@Override
protected DataBuffer read() throws IOException {
ByteBuffer byteBuffer = this.pooledByteBuffer.getBuffer();
int read = this.requestChannel.read(byteBuffer);
if (logger.isTraceEnabled()) {
logger.trace("read:" + read);
}
if (read > 0) {
byteBuffer.flip();
return this.dataBufferFactory.wrap(byteBuffer);
}
else if (read == -1) {
onAllDataRead();
}
return null;
}
private class ReadListener implements ChannelListener<StreamSourceChannel> {
@Override
public void handleEvent(StreamSourceChannel channel) {
onDataAvailable();
}
}
private class CloseListener implements ChannelListener<StreamSourceChannel> {
@Override
public void handleEvent(StreamSourceChannel channel) {
onAllDataRead();
}
}
}
}