/**
* Copyright (C) Zhang,Yuexiang (xfeep)
*
*For reuse some classes from tomcat8 we have to use this package
*/
package org.apache.coyote.http11.upgrade;
import java.io.IOException;
import java.nio.ByteBuffer;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpUpgradeHandler;
import nginx.clojure.MiniConstants;
import org.apache.coyote.http11.NginxChannel;
import org.apache.juli.logging.Log;
import org.apache.tomcat.util.net.SocketWrapper;
public class NginxUpgradeProcessor extends AbstractProcessor<NginxChannel>{
protected NginxChannel channel;
protected WebConnectionInputStream inputStream;
protected WebConnectionOutputStream outputStream;
protected static final org.apache.juli.logging.Log log =
org.apache.juli.logging.LogFactory.getLog( NginxUpgradeProcessor.class );
public NginxUpgradeProcessor(SocketWrapper<NginxChannel> socket,
ByteBuffer leftoverInput,
HttpUpgradeHandler httpUpgradeProcessor) throws IOException {
super(httpUpgradeProcessor, new WebConnectionInputStream(socket.getSocket()),
new WebConnectionOutputStream(socket));
inputStream = (WebConnectionInputStream) getInputStream();
outputStream = (WebConnectionOutputStream) getOutputStream();
channel = socket.getSocket();
// channel.setAsyncTimeout(-1);
ByteBuffer readBuffer = socket.getSocket().getBufHandler().getReadBuffer();
if (leftoverInput != null) {
if (readBuffer.remaining() > 0) {
readBuffer.flip();
} else {
readBuffer.clear();
}
readBuffer.put(leftoverInput);
readBuffer.flip();
}else {
readBuffer.flip();
}
}
@Override
protected Log getLog() {
return log;
}
@Override
public void close() throws Exception {
channel.close();
}
protected static class WebConnectionInputStream extends AbstractServletInputStream {
protected NginxChannel channel;
protected boolean eof = false;
public WebConnectionInputStream(NginxChannel channel) {
this.channel = channel;
}
protected void onClose() {
if (eof) {
return;
}
eof = true;
}
@Override
protected boolean doIsReady() throws IOException {
ByteBuffer buffer = channel.getBufHandler().getReadBuffer();
if (buffer.remaining() > 0) {
return true;
}
buffer.clear();
channel.read(buffer);
boolean isReady = buffer.position() > 0;
buffer.flip();
return isReady;
}
@Override
protected int doRead(boolean block, byte[] b, int off, int len)
throws IOException {
ByteBuffer buffer = channel.getBufHandler().getReadBuffer();
int remaining = buffer.remaining();
// Is there enough data in the read buffer to satisfy this request?
if (remaining >= len) {
buffer.get(b, off, len);
return len;
}
// Copy what data there is in the read buffer to the byte array
int leftToWrite = len;
int newOffset = off;
if (remaining > 0) {
buffer.get(b, off, remaining);
leftToWrite -= remaining;
newOffset += remaining;
}
// Fill the read buffer as best we can
buffer.clear();
int nRead = (int) channel.read(buffer);
// Full as much of the remaining byte array as possible with the data
// that was just read
if (nRead > 0) {
buffer.flip();
if (nRead > leftToWrite) {
buffer.get(b, newOffset, leftToWrite);
leftToWrite = 0;
} else {
buffer.get(b, newOffset, nRead);
leftToWrite -= nRead;
}
} else if (nRead == 0) {
buffer.flip();
} /*nRead == -1 will never happen*/
return len - leftToWrite;
}
@Override
protected void doClose() throws IOException {
eof = true;
channel.close();
}
}
public static class SimpleBufferChain {
public ByteBuffer buffer;
public SimpleBufferChain next;
}
public static class WebConnectionOutputStream extends AbstractServletOutputStream<NginxChannel> {
// protected ByteBuffer buffer;
protected NginxChannel channel;
protected WriteListener listener;
protected boolean eof = false;
protected int maxWrite;
public WebConnectionOutputStream(SocketWrapper<NginxChannel> socket) {
super(socket, MiniConstants.NGINX_CLOJURE_CORE_CLIENT_HEADER_MAX_SIZE);
this.channel = socket.getSocket();
maxWrite = socket.getSocket().getBufHandler().getWriteBuffer().capacity();
}
/**
* before invoke it tomcat will get a write lock. so it need not be thread-safe.
*/
@Override
protected int doWrite(boolean block, byte[] b, int off, int len)
throws IOException {
int leftToWrite = len;
int count = 0;
int offset = off;
while (leftToWrite > 0) {
int writeThisLoop;
int writtenThisLoop;
if (leftToWrite > maxWrite) {
writeThisLoop = maxWrite;
} else {
writeThisLoop = leftToWrite;
}
writtenThisLoop = doWriteInternal(block, b, offset, writeThisLoop);
count += writtenThisLoop;
offset += writtenThisLoop;
leftToWrite -= writtenThisLoop;
if (writtenThisLoop == 0) {
break;
}
}
return count;
}
private int doWriteInternal (boolean block, byte[] b, int off, int len)
throws IOException {
channel.getBufHandler().getWriteBuffer().clear();
channel.getBufHandler().getWriteBuffer().put(b, off, len);
channel.getBufHandler().getWriteBuffer().flip();
return (int)channel.write(channel.getBufHandler().getWriteBuffer());
}
@Override
protected void doClose() throws IOException {
eof = true;
channel.close();
}
@Override
protected void doFlush() throws IOException {
/*nothing to do*/
// channel.flush(true, null, -1);
}
}
}