/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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 io.undertow.io;
import io.undertow.UndertowLogger;
import io.undertow.UndertowMessages;
import io.undertow.connector.PooledByteBuffer;
import io.undertow.server.Connectors;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.Headers;
import io.undertow.util.StatusCodes;
import org.xnio.ChannelListener;
import org.xnio.channels.StreamSourceChannel;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.StandardCharsets;
/**
* @author Stuart Douglas
*/
public class AsyncReceiverImpl implements Receiver {
private static final ErrorCallback END_EXCHANGE = new ErrorCallback() {
@Override
public void error(HttpServerExchange exchange, IOException e) {
e.printStackTrace();
exchange.setStatusCode(StatusCodes.INTERNAL_SERVER_ERROR);
UndertowLogger.REQUEST_IO_LOGGER.ioException(e);
exchange.endExchange();
}
};
public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private final HttpServerExchange exchange;
private final StreamSourceChannel channel;
private int maxBufferSize = -1;
private boolean paused = false;
private boolean done = false;
public AsyncReceiverImpl(HttpServerExchange exchange) {
this.exchange = exchange;
this.channel = exchange.getRequestChannel();
if (channel == null) {
throw UndertowMessages.MESSAGES.requestChannelAlreadyProvided();
}
}
@Override
public void setMaxBufferSize(int maxBufferSize) {
this.maxBufferSize = maxBufferSize;
}
@Override
public void receiveFullString(final FullStringCallback callback, ErrorCallback errorCallback) {
receiveFullString(callback, errorCallback, StandardCharsets.ISO_8859_1);
}
@Override
public void receiveFullString(FullStringCallback callback) {
receiveFullString(callback, END_EXCHANGE, StandardCharsets.ISO_8859_1);
}
@Override
public void receivePartialString(PartialStringCallback callback, ErrorCallback errorCallback) {
receivePartialString(callback, errorCallback, StandardCharsets.ISO_8859_1);
}
@Override
public void receivePartialString(PartialStringCallback callback) {
receivePartialString(callback, END_EXCHANGE, StandardCharsets.ISO_8859_1);
}
@Override
public void receiveFullString(final FullStringCallback callback, final ErrorCallback errorCallback, final Charset charset) {
if(done) {
throw UndertowMessages.MESSAGES.requestBodyAlreadyRead();
}
final ErrorCallback error = errorCallback == null ? END_EXCHANGE : errorCallback;
if (callback == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("callback");
}
if (exchange.isRequestComplete()) {
callback.handle(exchange, "");
return;
}
String contentLengthString = exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH);
long contentLength;
final ByteArrayOutputStream sb;
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
if (contentLength > Integer.MAX_VALUE) {
error.error(exchange, new RequestToLargeException());
return;
}
sb = new ByteArrayOutputStream((int) contentLength);
} else {
contentLength = -1;
sb = new ByteArrayOutputStream();
}
if (maxBufferSize > 0) {
if (contentLength > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
callback.handle(exchange, sb.toString(charset.name()));
return;
} else if (res == 0) {
channel.getReadSetter().set(new ChannelListener<StreamSourceChannel>() {
@Override
public void handleEvent(StreamSourceChannel channel) {
if(done) {
return;
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, sb.toString(charset.name()));
}
}, exchange);
return;
} else if (res == 0) {
return;
} else {
buffer.flip();
while (buffer.hasRemaining()) {
sb.write(buffer.get());
}
if (maxBufferSize > 0 && sb.size() > maxBufferSize) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, new RequestToLargeException());
}
}, exchange);
return;
}
}
} catch (final IOException e) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, e);
}
}, exchange);
return;
}
} while (true);
} finally {
pooled.close();
}
}
});
channel.resumeReads();
return;
} else {
buffer.flip();
while (buffer.hasRemaining()) {
sb.write(buffer.get());
}
if (maxBufferSize > 0 && sb.size() > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
} catch (IOException e) {
error.error(exchange, e);
return;
}
} while (true);
} finally {
pooled.close();
}
}
@Override
public void receiveFullString(FullStringCallback callback, Charset charset) {
receiveFullString(callback, END_EXCHANGE, charset);
}
@Override
public void receivePartialString(final PartialStringCallback callback, final ErrorCallback errorCallback, Charset charset) {
if(done) {
throw UndertowMessages.MESSAGES.requestBodyAlreadyRead();
}
final ErrorCallback error = errorCallback == null ? END_EXCHANGE : errorCallback;
if (callback == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("callback");
}
if (exchange.isRequestComplete()) {
callback.handle(exchange, "", true);
return;
}
String contentLengthString = exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH);
long contentLength;
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
if (contentLength > Integer.MAX_VALUE) {
error.error(exchange, new RequestToLargeException());
return;
}
} else {
contentLength = -1;
}
if (maxBufferSize > 0) {
if (contentLength > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
final CharsetDecoder decoder = charset.newDecoder();
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
channel.getReadSetter().set(new ChannelListener<StreamSourceChannel>() {
@Override
public void handleEvent(final StreamSourceChannel channel) {
if(done || paused) {
return;
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
if(paused) {
return;
}
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, "", true);
}
}, exchange);
return;
} else if (res == 0) {
return;
} else {
buffer.flip();
final CharBuffer cb = decoder.decode(buffer);
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, cb.toString(), false);
if (!paused) {
channel.resumeReads();
} else {
System.out.println("paused");
}
}
}, exchange);
}
} catch (final IOException e) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, e);
}
}, exchange);
return;
}
} while (true);
} finally {
pooled.close();
}
}
});
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
callback.handle(exchange, "", true);
return;
} else if (res == 0) {
channel.resumeReads();
return;
} else {
buffer.flip();
CharBuffer cb = decoder.decode(buffer);
callback.handle(exchange, cb.toString(), false);
if(paused) {
return;
}
}
} catch (IOException e) {
error.error(exchange, e);
return;
}
} while (true);
} finally {
pooled.close();
}
}
@Override
public void receivePartialString(PartialStringCallback callback, Charset charset) {
receivePartialString(callback, END_EXCHANGE, charset);
}
@Override
public void receiveFullBytes(final FullBytesCallback callback, final ErrorCallback errorCallback) {
if(done) {
throw UndertowMessages.MESSAGES.requestBodyAlreadyRead();
}
final ErrorCallback error = errorCallback == null ? END_EXCHANGE : errorCallback;
if (callback == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("callback");
}
if (exchange.isRequestComplete()) {
callback.handle(exchange, EMPTY_BYTE_ARRAY);
return;
}
String contentLengthString = exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH);
long contentLength;
final ByteArrayOutputStream sb;
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
if (contentLength > Integer.MAX_VALUE) {
error.error(exchange, new RequestToLargeException());
return;
}
sb = new ByteArrayOutputStream((int) contentLength);
} else {
contentLength = -1;
sb = new ByteArrayOutputStream();
}
if (maxBufferSize > 0) {
if (contentLength > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
callback.handle(exchange, sb.toByteArray());
return;
} else if (res == 0) {
channel.getReadSetter().set(new ChannelListener<StreamSourceChannel>() {
@Override
public void handleEvent(StreamSourceChannel channel) {
if(done) {
return;
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, sb.toByteArray());
}
}, exchange);
return;
} else if (res == 0) {
return;
} else {
buffer.flip();
while (buffer.hasRemaining()) {
sb.write(buffer.get());
}
if (maxBufferSize > 0 && sb.size() > maxBufferSize) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, new RequestToLargeException());
}
}, exchange);
return;
}
}
} catch (final Exception e) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, new IOException(e));
}
}, exchange);
return;
}
} while (true);
} finally {
pooled.close();
}
}
});
channel.resumeReads();
return;
} else {
buffer.flip();
while (buffer.hasRemaining()) {
sb.write(buffer.get());
}
if (maxBufferSize > 0 && sb.size() > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
} catch (IOException e) {
error.error(exchange, e);
return;
}
} while (true);
} finally {
pooled.close();
}
}
@Override
public void receiveFullBytes(FullBytesCallback callback) {
receiveFullBytes(callback, END_EXCHANGE);
}
@Override
public void receivePartialBytes(final PartialBytesCallback callback, final ErrorCallback errorCallback) {
if(done) {
throw UndertowMessages.MESSAGES.requestBodyAlreadyRead();
}
final ErrorCallback error = errorCallback == null ? END_EXCHANGE : errorCallback;
if (callback == null) {
throw UndertowMessages.MESSAGES.argumentCannotBeNull("callback");
}
if (exchange.isRequestComplete()) {
callback.handle(exchange, EMPTY_BYTE_ARRAY, true);
return;
}
String contentLengthString = exchange.getRequestHeaders().getFirst(Headers.CONTENT_LENGTH);
long contentLength;
if (contentLengthString != null) {
contentLength = Long.parseLong(contentLengthString);
if (contentLength > Integer.MAX_VALUE) {
error.error(exchange, new RequestToLargeException());
return;
}
} else {
contentLength = -1;
}
if (maxBufferSize > 0) {
if (contentLength > maxBufferSize) {
error.error(exchange, new RequestToLargeException());
return;
}
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
channel.getReadSetter().set(new ChannelListener<StreamSourceChannel>() {
@Override
public void handleEvent(final StreamSourceChannel channel) {
if(done || paused) {
return;
}
PooledByteBuffer pooled = exchange.getConnection().getByteBufferPool().allocate();
final ByteBuffer buffer = pooled.getBuffer();
try {
int res;
do {
if(paused) {
return;
}
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, EMPTY_BYTE_ARRAY, true);
}
}, exchange);
return;
} else if (res == 0) {
return;
} else {
buffer.flip();
final byte[] data = new byte[buffer.remaining()];
buffer.get(data);
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
callback.handle(exchange, data, false);
if (!paused) {
channel.resumeReads();
}
}
}, exchange);
}
} catch (final IOException e) {
Connectors.executeRootHandler(new HttpHandler() {
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
error.error(exchange, e);
}
}, exchange);
return;
}
} while (true);
} finally {
pooled.close();
}
}
});
try {
int res;
do {
try {
buffer.clear();
res = channel.read(buffer);
if (res == -1) {
done = true;
callback.handle(exchange, EMPTY_BYTE_ARRAY, true);
return;
} else if (res == 0) {
channel.resumeReads();
return;
} else {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
callback.handle(exchange, data, false);
if(paused) {
return;
}
}
} catch (IOException e) {
error.error(exchange, e);
return;
}
} while (true);
} finally {
pooled.close();
}
}
@Override
public void receivePartialBytes(PartialBytesCallback callback) {
receivePartialBytes(callback, END_EXCHANGE);
}
@Override
public void pause() {
this.paused = true;
channel.suspendReads();
}
@Override
public void resume() {
this.paused = false;
channel.wakeupReads();
}
}