340265 Improve handling of io shutdown in SSL
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2906 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
903dd14656
commit
a6483bfa6f
|
@ -6,6 +6,7 @@ jetty-7.3.2-SNAPSHOT
|
|||
+ 339150 Validate client certificate when it is used for authentication
|
||||
+ 339187 In the OSGi manifest of the jetty-all-server aggregate, mark javax.annotation as optional
|
||||
+ 339543 Add configuration options for Certificate Revocation checking
|
||||
+ 340265 Improve handling of io shutdown in SSL
|
||||
+ Ensure generated fragment names are unique
|
||||
+ JETTY-1245 Pooled Buffers implementation
|
||||
|
||||
|
|
|
@ -145,10 +145,10 @@ public class Ajp13Parser implements Parser
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public long parseAvailable() throws IOException
|
||||
public int parseAvailable() throws IOException
|
||||
{
|
||||
long len = parseNext();
|
||||
long total = len > 0 ? len : 0;
|
||||
int len = parseNext();
|
||||
int total = len > 0 ? len : 0;
|
||||
|
||||
// continue parsing
|
||||
while (!isComplete() && _buffer != null && _buffer.length() > 0)
|
||||
|
@ -228,9 +228,9 @@ public class Ajp13Parser implements Parser
|
|||
|
||||
volatile int _seq=0;
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public long parseNext() throws IOException
|
||||
public int parseNext() throws IOException
|
||||
{
|
||||
long total_filled = -1;
|
||||
int total_filled = 0;
|
||||
|
||||
if (_buffer == null)
|
||||
{
|
||||
|
@ -250,7 +250,7 @@ public class Ajp13Parser implements Parser
|
|||
{
|
||||
_state = STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (_state < 0)
|
||||
|
|
|
@ -87,10 +87,11 @@ public class HttpExchangeTest extends TestCase
|
|||
|
||||
public void testPerf() throws Exception
|
||||
{
|
||||
sender(1,false);
|
||||
sender(1,true);
|
||||
|
||||
if (Stress.isEnabled())
|
||||
{
|
||||
sender(1,false);
|
||||
sender(1,true);
|
||||
sender(100,false);
|
||||
sender(100,true);
|
||||
sender(10000,false);
|
||||
|
@ -98,8 +99,6 @@ public class HttpExchangeTest extends TestCase
|
|||
}
|
||||
else
|
||||
{
|
||||
sender(1,false);
|
||||
sender(1,true);
|
||||
sender(10,false);
|
||||
sender(10,true);
|
||||
}
|
||||
|
@ -168,7 +167,9 @@ public class HttpExchangeTest extends TestCase
|
|||
if (len==2009)
|
||||
latch.countDown();
|
||||
else
|
||||
{
|
||||
System.err.println(n+" ONLY "+len);
|
||||
}
|
||||
complete.countDown();
|
||||
}
|
||||
|
||||
|
@ -223,7 +224,7 @@ public class HttpExchangeTest extends TestCase
|
|||
if(elapsed>0)
|
||||
System.err.println(nb+"/"+_count+" c="+close+" rate="+(nb*1000/elapsed));
|
||||
*/
|
||||
|
||||
|
||||
assertEquals("nb="+nb+" close="+close,0,latch.getCount());
|
||||
}
|
||||
|
||||
|
|
|
@ -31,8 +31,6 @@ import org.eclipse.jetty.util.log.Log;
|
|||
* two optional writer to byte conversions. buffer.writers=true will probably be
|
||||
* faster, but will consume more memory. This option is just for testing and tuning.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractGenerator implements Generator
|
||||
{
|
||||
|
|
|
@ -198,7 +198,8 @@ public class HttpParser implements Parser
|
|||
|
||||
// continue parsing
|
||||
while (_state != STATE_END)
|
||||
parseNext();
|
||||
if (parseNext()<0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
@ -209,34 +210,33 @@ public class HttpParser implements Parser
|
|||
* @see #parse
|
||||
* @see #parseNext
|
||||
*/
|
||||
public long parseAvailable() throws IOException
|
||||
public int parseAvailable() throws IOException
|
||||
{
|
||||
long len = parseNext();
|
||||
long total=len>0?len:0;
|
||||
int progress = parseNext();
|
||||
int total=progress>0?1:0;
|
||||
|
||||
// continue parsing
|
||||
while (!isComplete() && _buffer!=null && _buffer.length()>0)
|
||||
{
|
||||
len = parseNext();
|
||||
if (len>0)
|
||||
total+=len;
|
||||
progress = parseNext();
|
||||
if (progress>0)
|
||||
total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Parse until next Event.
|
||||
* @return number of bytes filled from endpoint or -1 if fill never called.
|
||||
* @return an indication of progress <0 EOF, 0 no progress, >0 progress.
|
||||
*/
|
||||
public long parseNext() throws IOException
|
||||
public int parseNext() throws IOException
|
||||
{
|
||||
long total_filled=-1;
|
||||
int progress=0;
|
||||
|
||||
if (_state == STATE_END)
|
||||
return -1;
|
||||
return 0;
|
||||
|
||||
if (_buffer==null)
|
||||
{
|
||||
|
@ -256,7 +256,7 @@ public class HttpParser implements Parser
|
|||
{
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int length=_buffer.length();
|
||||
|
@ -287,11 +287,9 @@ public class HttpParser implements Parser
|
|||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
try
|
||||
{
|
||||
if (total_filled<0)
|
||||
total_filled=0;
|
||||
filled=_endp.fill(_buffer);
|
||||
if (filled>0)
|
||||
total_filled+=filled;
|
||||
progress++;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
@ -303,6 +301,12 @@ public class HttpParser implements Parser
|
|||
|
||||
if (filled < 0)
|
||||
{
|
||||
if (_headResponse && _state>STATE_END)
|
||||
{
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return 1;
|
||||
}
|
||||
if ( _state == STATE_EOF_CONTENT)
|
||||
{
|
||||
if (_buffer.length()>0)
|
||||
|
@ -315,10 +319,10 @@ public class HttpParser implements Parser
|
|||
}
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
reset(true);
|
||||
throw new EofException(ioex);
|
||||
|
||||
return -1;
|
||||
}
|
||||
length=_buffer.length();
|
||||
}
|
||||
|
@ -327,9 +331,15 @@ public class HttpParser implements Parser
|
|||
// EventHandler header
|
||||
byte ch;
|
||||
byte[] array=_buffer.array();
|
||||
|
||||
int last=_state;
|
||||
while (_state<STATE_END && length-->0)
|
||||
{
|
||||
if (last!=_state)
|
||||
{
|
||||
progress++;
|
||||
last=_state;
|
||||
}
|
||||
|
||||
ch=_buffer.get();
|
||||
|
||||
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
|
||||
|
@ -425,7 +435,7 @@ public class HttpParser implements Parser
|
|||
_state=STATE_END;
|
||||
_handler.headerComplete();
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -453,7 +463,7 @@ public class HttpParser implements Parser
|
|||
_state=STATE_END;
|
||||
_handler.headerComplete();
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -601,7 +611,7 @@ public class HttpParser implements Parser
|
|||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
}
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -765,8 +775,15 @@ public class HttpParser implements Parser
|
|||
// Handle _content
|
||||
length=_buffer.length();
|
||||
Buffer chunk;
|
||||
last=_state;
|
||||
while (_state > STATE_END && length > 0)
|
||||
{
|
||||
if (last!=_state)
|
||||
{
|
||||
progress++;
|
||||
last=_state;
|
||||
}
|
||||
|
||||
if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
|
||||
{
|
||||
_eol=_buffer.get();
|
||||
|
@ -782,7 +799,7 @@ public class HttpParser implements Parser
|
|||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return total_filled;
|
||||
return 1;
|
||||
|
||||
case STATE_CONTENT:
|
||||
{
|
||||
|
@ -791,7 +808,7 @@ public class HttpParser implements Parser
|
|||
{
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (length > remaining)
|
||||
|
@ -812,7 +829,7 @@ public class HttpParser implements Parser
|
|||
_handler.messageComplete(_contentPosition);
|
||||
}
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
|
||||
case STATE_CHUNKED_CONTENT:
|
||||
|
@ -844,7 +861,7 @@ public class HttpParser implements Parser
|
|||
_eol=_buffer.get();
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
_state=STATE_CHUNK;
|
||||
|
@ -874,7 +891,7 @@ public class HttpParser implements Parser
|
|||
_eol=_buffer.get();
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
_state=STATE_CHUNK;
|
||||
|
@ -898,13 +915,14 @@ public class HttpParser implements Parser
|
|||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return total_filled;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
length=_buffer.length();
|
||||
}
|
||||
return total_filled;
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
|
|
|
@ -17,17 +17,18 @@ import java.io.IOException;
|
|||
|
||||
/**
|
||||
* Abstract interface for a connection Parser for use by Jetty.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public interface Parser
|
||||
{
|
||||
|
||||
void reset(boolean returnBuffers);
|
||||
|
||||
boolean isComplete();
|
||||
|
||||
long parseAvailable() throws IOException;
|
||||
/**
|
||||
* @return An indication of progress, typically the number of bytes filled plus the events parsed: -1 means EOF read, 0 no progress, >0 progress
|
||||
* @throws IOException
|
||||
*/
|
||||
int parseAvailable() throws IOException;
|
||||
|
||||
boolean isMoreInBuffer() throws IOException;
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ public class HttpGeneratorTest
|
|||
Handler handler = new Handler();
|
||||
HttpParser parser=null;
|
||||
|
||||
|
||||
|
||||
// For HTTP version
|
||||
for (int v=9;v<=11;v++)
|
||||
{
|
||||
|
@ -81,8 +83,7 @@ public class HttpGeneratorTest
|
|||
// For none, keep-alive, close
|
||||
for (int c=0;c<(v==11?connect.length:(connect.length-1));c++)
|
||||
{
|
||||
|
||||
String t="v="+v+",r="+r+",chunks="+chunks+",connect="+connect[c]+",tr="+tr[r];
|
||||
String t="v="+v+",r="+r+",chunks="+chunks+",connect="+c+",tr="+tr[r];
|
||||
// System.err.println(t);
|
||||
|
||||
hb.reset(true);
|
||||
|
@ -91,39 +92,45 @@ public class HttpGeneratorTest
|
|||
|
||||
tr[r].build(v,hb,"OK\r\nTest",connect[c],null,chunks, fields);
|
||||
String response=endp.getOut().toString();
|
||||
// System.out.println("RESPONSE: "+t+"\n"+response+(hb.isPersistent()?"...\n":"---\n"));
|
||||
//System.out.println("RESPONSE: "+t+"\n"+response+(hb.isPersistent()?"...\n":"---\n"));
|
||||
|
||||
if (v==9)
|
||||
{
|
||||
assertFalse(t,hb.isPersistent());
|
||||
if (tr[r].body!=null)
|
||||
assertEquals(t,tr[r].body, response);
|
||||
if (tr[r]._body!=null)
|
||||
assertEquals(t,tr[r]._body, response);
|
||||
continue;
|
||||
}
|
||||
|
||||
parser=new HttpParser(new ByteArrayBuffer(response.getBytes()), handler);
|
||||
parser.setHeadResponse(tr[r]._head);
|
||||
|
||||
try
|
||||
{
|
||||
parser.parse();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
if (tr[r].body!=null)
|
||||
if (tr[r]._body!=null)
|
||||
throw new Exception(t,e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tr[r].body!=null)
|
||||
assertEquals(t,tr[r].body, this.content);
|
||||
if (tr[r]._body!=null)
|
||||
assertEquals(t,tr[r]._body, this.content);
|
||||
|
||||
if (v==10)
|
||||
assertTrue(t,hb.isPersistent() || tr[r].values[1]==null || c==2 || c==0);
|
||||
assertTrue(t,hb.isPersistent() || tr[r]._contentLength==null || c==2 || c==0);
|
||||
else
|
||||
assertTrue(t,hb.isPersistent() || c==2 || c==3);
|
||||
|
||||
if (v>9)
|
||||
assertEquals("OK Test",f2);
|
||||
|
||||
assertTrue(t,tr[r].values[1]==null || content.length()==Integer.parseInt(tr[r].values[1]));
|
||||
if (content==null)
|
||||
assertTrue(t,tr[r]._body==null);
|
||||
else
|
||||
assertTrue(t,tr[r]._contentLength==null || content.length()==Integer.parseInt(tr[r]._contentLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,37 +140,48 @@ public class HttpGeneratorTest
|
|||
private static final String[] headers= { "Content-Type","Content-Length","Connection","Transfer-Encoding","Other"};
|
||||
private class TR
|
||||
{
|
||||
private int code;
|
||||
private String[] values=new String[headers.length];
|
||||
private String body;
|
||||
private int _code;
|
||||
private String _body;
|
||||
private boolean _head;
|
||||
String _contentType;
|
||||
String _contentLength;
|
||||
String _connection;
|
||||
String _te;
|
||||
String _other;
|
||||
|
||||
private TR(int code,String ct, String cl ,String content)
|
||||
private TR(int code,String contentType, String contentLength ,String content,boolean head)
|
||||
{
|
||||
this.code=code;
|
||||
values[0]=ct;
|
||||
values[1]=cl;
|
||||
values[4]="value";
|
||||
this.body=content;
|
||||
_code=code;
|
||||
_contentType=contentType;
|
||||
_contentLength=contentLength;
|
||||
_other="value";
|
||||
_body=content;
|
||||
_head=head;
|
||||
}
|
||||
|
||||
private void build(int version,HttpGenerator hb,String reason, String connection, String te, int chunks, HttpFields fields) throws Exception
|
||||
{
|
||||
values[2]=connection;
|
||||
values[3]=te;
|
||||
_connection=connection;
|
||||
_te=te;
|
||||
hb.setVersion(version);
|
||||
hb.setResponse(code,reason);
|
||||
|
||||
for (int i=0;i<headers.length;i++)
|
||||
hb.setResponse(_code,reason);
|
||||
hb.setHead(_head);
|
||||
|
||||
if (_contentType!=null)
|
||||
fields.put(new ByteArrayBuffer("Content-Type"),new ByteArrayBuffer(_contentType));
|
||||
if (_contentLength!=null)
|
||||
fields.put(new ByteArrayBuffer("Content-Length"),new ByteArrayBuffer(_contentLength));
|
||||
if (_connection!=null)
|
||||
fields.put(new ByteArrayBuffer("Connection"),new ByteArrayBuffer(_connection));
|
||||
if (_te!=null)
|
||||
fields.put(new ByteArrayBuffer("Transfer-Encoding"),new ByteArrayBuffer(_te));
|
||||
if (_other!=null)
|
||||
fields.put(new ByteArrayBuffer("Other"),new ByteArrayBuffer(_other));
|
||||
|
||||
if (_body!=null)
|
||||
{
|
||||
if (values[i]==null)
|
||||
continue;
|
||||
fields.put(new ByteArrayBuffer(headers[i]),new ByteArrayBuffer(values[i]));
|
||||
}
|
||||
|
||||
if (body!=null)
|
||||
{
|
||||
int inc=1+body.length()/chunks;
|
||||
Buffer buf=new ByteArrayBuffer(body);
|
||||
int inc=1+_body.length()/chunks;
|
||||
Buffer buf=new ByteArrayBuffer(_body);
|
||||
View view = new View(buf);
|
||||
for (int i=1;i<chunks;i++)
|
||||
{
|
||||
|
@ -198,20 +216,20 @@ public class HttpGeneratorTest
|
|||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "["+code+","+values[0]+","+values[1]+","+(body==null?"none":"_content")+"]";
|
||||
return "["+_code+","+_contentType+","+_contentLength+","+(_body==null?"null":"content")+"]";
|
||||
}
|
||||
}
|
||||
|
||||
private final TR[] tr =
|
||||
{
|
||||
/* 0 */ new TR(200,null,null,null),
|
||||
/* 1 */ new TR(200,null,null,CONTENT),
|
||||
/* 2 */ new TR(200,null,""+CONTENT.length(),null),
|
||||
/* 3 */ new TR(200,null,""+CONTENT.length(),CONTENT),
|
||||
/* 4 */ new TR(200,"text/html",null,null),
|
||||
/* 5 */ new TR(200,"text/html",null,CONTENT),
|
||||
/* 6 */ new TR(200,"text/html",""+CONTENT.length(),null),
|
||||
/* 7 */ new TR(200,"text/html",""+CONTENT.length(),CONTENT),
|
||||
/* 0 */ new TR(200,null,null,null,false),
|
||||
/* 1 */ new TR(200,null,null,CONTENT,false),
|
||||
/* 2 */ new TR(200,null,""+CONTENT.length(),null,true),
|
||||
/* 3 */ new TR(200,null,""+CONTENT.length(),CONTENT,false),
|
||||
/* 4 */ new TR(200,"text/html",null,null,true),
|
||||
/* 5 */ new TR(200,"text/html",null,CONTENT,false),
|
||||
/* 6 */ new TR(200,"text/html",""+CONTENT.length(),null,true),
|
||||
/* 7 */ new TR(200,"text/html",""+CONTENT.length(),CONTENT,false),
|
||||
};
|
||||
|
||||
private String content;
|
||||
|
|
|
@ -121,6 +121,7 @@ public class ByteArrayEndPoint implements ConnectedEndPoint
|
|||
{
|
||||
_out = out;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.io.EndPoint#isOpen()
|
||||
|
@ -130,6 +131,24 @@ public class ByteArrayEndPoint implements ConnectedEndPoint
|
|||
return !_closed;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.jetty.io.EndPoint#isInputShutdown()
|
||||
*/
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return _closed;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.jetty.io.EndPoint#isOutputShutdown()
|
||||
*/
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return _closed;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.io.EndPoint#isBlocking()
|
||||
|
@ -157,6 +176,16 @@ public class ByteArrayEndPoint implements ConnectedEndPoint
|
|||
*/
|
||||
public void shutdownOutput() throws IOException
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.io.EndPoint#shutdownInput()
|
||||
*/
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -176,13 +205,19 @@ public class ByteArrayEndPoint implements ConnectedEndPoint
|
|||
{
|
||||
if (_closed)
|
||||
throw new IOException("CLOSED");
|
||||
if (_in==null)
|
||||
return -1;
|
||||
if (_in.length()<=0)
|
||||
return _nonBlocking?0:-1;
|
||||
int len = buffer.put(_in);
|
||||
_in.skip(len);
|
||||
return len;
|
||||
|
||||
if (_in!=null && _in.length()>0)
|
||||
{
|
||||
int len = buffer.put(_in);
|
||||
_in.skip(len);
|
||||
return len;
|
||||
}
|
||||
|
||||
if (_in!=null && _in.length()==0 && _nonBlocking)
|
||||
return 0;
|
||||
|
||||
close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -26,6 +26,15 @@ public interface EndPoint
|
|||
* Shutdown any backing output stream associated with the endpoint
|
||||
*/
|
||||
void shutdownOutput() throws IOException;
|
||||
|
||||
boolean isOutputShutdown();
|
||||
|
||||
/**
|
||||
* Shutdown any backing input stream associated with the endpoint
|
||||
*/
|
||||
void shutdownInput() throws IOException;
|
||||
|
||||
boolean isInputShutdown();
|
||||
|
||||
/**
|
||||
* Close any backing stream associated with the endpoint
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.net.InetSocketAddress;
|
|||
import java.net.Socket;
|
||||
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -67,7 +68,21 @@ public class SocketEndPoint extends StreamEndPoint
|
|||
@Override
|
||||
public boolean isOpen()
|
||||
{
|
||||
return super.isOpen() && _socket!=null && !_socket.isClosed() && !_socket.isInputShutdown() && !_socket.isOutputShutdown();
|
||||
return super.isOpen() && _socket!=null && !_socket.isClosed();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return !super.isOpen() || _socket!=null && _socket.isInputShutdown();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return !super.isOpen() || _socket!=null && _socket.isOutputShutdown();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -80,6 +95,18 @@ public class SocketEndPoint extends StreamEndPoint
|
|||
if (!_socket.isClosed() && !_socket.isOutputShutdown())
|
||||
_socket.shutdownOutput();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/*
|
||||
* @see org.eclipse.jetty.io.bio.StreamEndPoint#shutdownOutput()
|
||||
*/
|
||||
@Override
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
if (!_socket.isClosed() && !_socket.isInputShutdown())
|
||||
_socket.shutdownInput();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* (non-Javadoc)
|
||||
|
@ -190,6 +217,22 @@ public class SocketEndPoint extends StreamEndPoint
|
|||
_socket.setSoTimeout(timeMs>0?timeMs:0);
|
||||
super.setMaxIdleTime(timeMs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
protected void idleExpired() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!isInputShutdown())
|
||||
shutdownInput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
_socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.eclipse.jetty.io.bio;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
|
@ -77,6 +78,20 @@ public class StreamEndPoint implements EndPoint
|
|||
{
|
||||
}
|
||||
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return !isOpen();
|
||||
}
|
||||
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
}
|
||||
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return !isOpen();
|
||||
}
|
||||
|
||||
/*
|
||||
* @see org.eclipse.io.BufferIO#close()
|
||||
*/
|
||||
|
@ -90,6 +105,11 @@ public class StreamEndPoint implements EndPoint
|
|||
_out=null;
|
||||
}
|
||||
|
||||
protected void idleExpired() throws IOException
|
||||
{
|
||||
_in.close();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.io.BufferIO#fill(org.eclipse.io.Buffer)
|
||||
*/
|
||||
|
@ -107,7 +127,15 @@ public class StreamEndPoint implements EndPoint
|
|||
throw new IOException("FULL");
|
||||
}
|
||||
|
||||
return buffer.readFrom(_in,space);
|
||||
try
|
||||
{
|
||||
return buffer.readFrom(_in,space);
|
||||
}
|
||||
catch(SocketTimeoutException e)
|
||||
{
|
||||
idleExpired();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
|
|
|
@ -98,6 +98,19 @@ public class ChannelEndPoint implements EndPoint
|
|||
return _channel.isOpen();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.io.EndPoint#close()
|
||||
*/
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
if (_channel.isOpen() && _channel instanceof SocketChannel)
|
||||
{
|
||||
Socket socket= ((SocketChannel)_channel).socket();
|
||||
if (!socket.isClosed()&&!socket.isInputShutdown())
|
||||
socket.shutdownInput();
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.io.EndPoint#close()
|
||||
*/
|
||||
|
@ -110,6 +123,16 @@ public class ChannelEndPoint implements EndPoint
|
|||
socket.shutdownOutput();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return _channel.isOpen() && _socket!=null && _socket.isOutputShutdown();
|
||||
}
|
||||
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return _channel.isOpen() && _socket!=null && _socket.isInputShutdown();
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.eclipse.io.EndPoint#close()
|
||||
|
@ -148,8 +171,25 @@ public class ChannelEndPoint implements EndPoint
|
|||
{
|
||||
bbuf.position(buffer.putIndex());
|
||||
len=_channel.read(bbuf);
|
||||
if (len<0)
|
||||
_channel.close(); // Don't call this.close() as that may recurse in SSL land and call fill again
|
||||
if (len<0 && isOpen() && !isInputShutdown())
|
||||
{
|
||||
try
|
||||
{
|
||||
shutdownInput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
try
|
||||
{
|
||||
close();
|
||||
}
|
||||
catch(IOException e2)
|
||||
{
|
||||
Log.ignore(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -318,6 +358,7 @@ public class ChannelEndPoint implements EndPoint
|
|||
return length;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return Returns the channel.
|
||||
*/
|
||||
|
|
|
@ -250,12 +250,14 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
protected void idleExpired()
|
||||
{
|
||||
if (_connection instanceof Idleable)
|
||||
{
|
||||
((Idleable)_connection).idleExpired();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
close();
|
||||
shutdownOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
|
@ -415,8 +417,8 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
if (getChannel().isOpen())
|
||||
{
|
||||
_interestOps =
|
||||
((!_dispatched || _readBlocked) ? SelectionKey.OP_READ : 0)
|
||||
| ((!_writable || _writeBlocked) ? SelectionKey.OP_WRITE : 0);
|
||||
((!_socket.isInputShutdown() && (!_dispatched || _readBlocked)) ? SelectionKey.OP_READ : 0)
|
||||
| ((!_socket.isOutputShutdown()&& (!_writable || _writeBlocked)) ? SelectionKey.OP_WRITE : 0);
|
||||
try
|
||||
{
|
||||
ops = ((_key!=null && _key.isValid())?_key.interestOps():-1);
|
||||
|
|
|
@ -172,12 +172,39 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
{
|
||||
Log.info(""+_result);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isOutputShutdown()
|
||||
{
|
||||
return _engine!=null && _engine.isOutboundDone();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public boolean isInputShutdown()
|
||||
{
|
||||
return _engine!=null && _engine.isInboundDone();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
// Nothing to do here. If the socket is open, let the SSL close handshake work, else the socket is closed anyway.
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void shutdownOutput() throws IOException
|
||||
{
|
||||
if (_closing)
|
||||
return;
|
||||
_closing=true;
|
||||
|
||||
// TODO - this really should not be done in a loop here - but with async callbacks.
|
||||
long end=System.currentTimeMillis()+((SocketChannel)_channel).socket().getSoTimeout();
|
||||
long end=System.currentTimeMillis()+getMaxIdleTime();
|
||||
try
|
||||
{
|
||||
while (isOpen() && isBufferingOutput()&& System.currentTimeMillis()<end)
|
||||
|
@ -185,7 +212,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
flush();
|
||||
if (isBufferingOutput())
|
||||
{
|
||||
Thread.sleep(100); // TODO non blocking
|
||||
Thread.sleep(50); // TODO non blocking
|
||||
flush();
|
||||
}
|
||||
}
|
||||
|
@ -198,7 +225,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
{
|
||||
flush();
|
||||
if (isBufferingOutput())
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(50);
|
||||
}
|
||||
|
||||
if (_debug) __log.debug(_session+" closing "+_engine.getHandshakeStatus());
|
||||
|
@ -273,27 +300,12 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void close() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
_closing=true;
|
||||
shutdownOutput();
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
Log.ignore(e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
super.close();
|
||||
}
|
||||
super.close();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Fill the buffer with unencrypted bytes.
|
||||
|
@ -617,6 +629,10 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
flushed=super.flush(_outNIOBuffer);
|
||||
if (_debug) __log.debug(_session+" flushed "+flushed+"/"+len);
|
||||
}
|
||||
else if (_closing && !_engine.isOutboundDone())
|
||||
{
|
||||
_engine.closeOutbound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,7 +642,7 @@ public class SslSelectChannelEndPoint extends SelectChannelEndPoint
|
|||
if (_handshook && !_allowRenegotiate && _channel!=null && _channel.isOpen())
|
||||
{
|
||||
Log.warn("SSL renegotiate denied: "+_channel);
|
||||
close();
|
||||
super.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -395,8 +395,9 @@ public class HttpConnection /* TODO extends AbstractConnection*/ implements Conn
|
|||
handleRequest();
|
||||
else if (!_parser.isComplete())
|
||||
{
|
||||
long parsed=_parser.parseAvailable();
|
||||
progress|=parsed>0;
|
||||
int parsed=_parser.parseAvailable();
|
||||
if (parsed>0)
|
||||
progress=true;
|
||||
}
|
||||
|
||||
if (_generator.isCommitted() && !_generator.isComplete())
|
||||
|
@ -408,7 +409,11 @@ public class HttpConnection /* TODO extends AbstractConnection*/ implements Conn
|
|||
{
|
||||
// If we are not ended then parse available
|
||||
if (!_parser.isComplete())
|
||||
progress|=_parser.parseAvailable()>0;
|
||||
{
|
||||
int parsed=_parser.parseAvailable();
|
||||
if (parsed>0)
|
||||
progress=true;
|
||||
}
|
||||
|
||||
// Do we have more generating to do?
|
||||
// Loop here because some writes may take multiple steps and
|
||||
|
@ -453,7 +458,7 @@ public class HttpConnection /* TODO extends AbstractConnection*/ implements Conn
|
|||
finally
|
||||
{
|
||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||
|
||||
|
||||
// Is this request/response round complete?
|
||||
if (_parser.isComplete() && _generator.isComplete() && !_endp.isBufferingOutput())
|
||||
{
|
||||
|
@ -471,7 +476,7 @@ public class HttpConnection /* TODO extends AbstractConnection*/ implements Conn
|
|||
else
|
||||
{
|
||||
// No switch, so cleanup and reset
|
||||
if (!_generator.isPersistent())
|
||||
if (!_generator.isPersistent() || _endp.isInputShutdown())
|
||||
{
|
||||
_parser.reset(true);
|
||||
more_in_buffer=false;
|
||||
|
@ -488,6 +493,11 @@ public class HttpConnection /* TODO extends AbstractConnection*/ implements Conn
|
|||
progress=true;
|
||||
}
|
||||
}
|
||||
else if (_parser.isIdle() && _endp.isInputShutdown())
|
||||
{
|
||||
more_in_buffer=false;
|
||||
_endp.close();
|
||||
}
|
||||
|
||||
if (_request.isAsyncStarted())
|
||||
{
|
||||
|
|
|
@ -622,6 +622,12 @@ public class SslSocketConnector extends SocketConnector implements SslConnector
|
|||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdownInput() throws IOException
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
|
|
|
@ -125,7 +125,7 @@ public class AbstractConnectorTest
|
|||
doInit(connections);
|
||||
|
||||
sendRequest(1, 1);
|
||||
|
||||
|
||||
doClose(connections);
|
||||
|
||||
assertEquals(connections, _connector.getConnections());
|
||||
|
@ -216,12 +216,6 @@ public class AbstractConnectorTest
|
|||
{
|
||||
for (int idx=0; idx < count; idx++)
|
||||
{
|
||||
if (_out[idx] != null)
|
||||
_out[idx].close();
|
||||
|
||||
if (_in[idx] != null)
|
||||
_in[idx].close();
|
||||
|
||||
if (_socket[idx] != null)
|
||||
_socket[idx].close();
|
||||
}
|
||||
|
|
|
@ -301,10 +301,9 @@ public class RFC2616Test
|
|||
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"\n"+
|
||||
|
||||
"GET /R2 HTTP/1.1\n"+"Host: localhost\n"+"Connection: close\n"+"\n"+
|
||||
|
||||
"GET /R3 HTTP/1.1\n"+"Host: localhost\n"+"Connection: close\n"+"\n");
|
||||
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200 OK\015\012","8.1.2 default")+1;
|
||||
offset=checkContains(response,offset,"/R1","8.1.2 default")+1;
|
||||
|
||||
|
@ -322,82 +321,84 @@ public class RFC2616Test
|
|||
}
|
||||
|
||||
@Test
|
||||
public void test8_2()
|
||||
public void test8_2() throws Exception
|
||||
{
|
||||
try
|
||||
{
|
||||
String response;
|
||||
int offset=0;
|
||||
// No Expect 100
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
String response;
|
||||
int offset=0;
|
||||
// No Expect 100
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
|
||||
assertTrue("8.2.3 no expect 100",response==null || response.length()==0);
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n"+
|
||||
"AbCdEf\015\012",true);
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200","8.2.3 no expect 100")+1;
|
||||
offset=checkContains(response,offset,"AbCdEf","8.2.3 no expect 100")+1;
|
||||
assertTrue("8.2.3 no expect 100",response==null || response.length()==0);
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n"+
|
||||
"AbCdEf\015\012",true);
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200","8.2.3 no expect 100")+1;
|
||||
offset=checkContains(response,offset,"AbCdEf","8.2.3 no expect 100")+1;
|
||||
|
||||
// Expect Failure
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Expect: unknown\n"+"Content-Type: text/plain\n"+"Content-Length: 8\n"
|
||||
+"\n");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 417","8.2.3 expect failure")+1;
|
||||
// Expect Failure
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Expect: unknown\n"+"Content-Type: text/plain\n"+"Content-Length: 8\n"
|
||||
+"\n");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 417","8.2.3 expect failure")+1;
|
||||
|
||||
// Expect with body
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Expect: 100-continue\n"+"Content-Type: text/plain\n"
|
||||
+"Content-Length: 8\n"+"Connection: close\n"+"\n"+"123456\015\012");
|
||||
checkNotContained(response,offset,"HTTP/1.1 100 ","8.2.3 expect 100");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200 OK","8.2.3 expect with body")+1;
|
||||
// Expect with body
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+"Host: localhost\n"+"Expect: 100-continue\n"+"Content-Type: text/plain\n"
|
||||
+"Content-Length: 8\n"+"Connection: close\n"+"\n"+"123456\015\012");
|
||||
checkNotContained(response,offset,"HTTP/1.1 100 ","8.2.3 expect 100");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200 OK","8.2.3 expect with body")+1;
|
||||
}
|
||||
|
||||
// Expect 100
|
||||
((StdErrLog)Log.getLog()).setHideStacks(true);
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Connection: close\n"+
|
||||
"Expect: 100-continue\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
offset=checkContains(response,offset,"HTTP/1.1 100 ","8.2.3 expect 100")+1;
|
||||
checkNotContained(response,offset,"HTTP/1.1 200","8.2.3 expect 100");
|
||||
/* can't test this with localconnector.
|
||||
@Test
|
||||
public void test8_2_3() throws Exception
|
||||
{
|
||||
String response;
|
||||
int offset=0;
|
||||
// Expect 100
|
||||
response=connector.getResponses("GET /R1 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Connection: close\n"+
|
||||
"Expect: 100-continue\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
offset=checkContains(response,offset,"HTTP/1.1 100 ","8.2.3 expect 100")+1;
|
||||
checkNotContained(response,offset,"HTTP/1.1 200","8.2.3 expect 100");
|
||||
/* can't test this with localconnector.
|
||||
response=connector.getResponses("654321\015\012");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 200","8.2.3 expect 100")+1;
|
||||
offset=checkContains(response,offset,"654321","8.2.3 expect 100")+1;
|
||||
*/
|
||||
*/
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test8_2_4() throws Exception
|
||||
{
|
||||
String response;
|
||||
int offset=0;
|
||||
// Expect 100 not sent
|
||||
((StdErrLog)Log.getLog()).setHideStacks(true);
|
||||
offset=0;
|
||||
|
||||
// Expect 100 not sent
|
||||
((StdErrLog)Log.getLog()).setHideStacks(true);
|
||||
offset=0;
|
||||
response=connector.getResponses("GET /R1?error=401 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Expect: 100-continue\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
checkNotContained(response,offset,"HTTP/1.1 100","8.2.3 expect 100");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 401 ","8.2.3 expect 100")+1;
|
||||
offset=checkContains(response,offset,"Connection: close","8.2.3 expect 100")+1;
|
||||
|
||||
response=connector.getResponses("GET /R1?error=401 HTTP/1.1\n"+
|
||||
"Host: localhost\n"+
|
||||
"Expect: 100-continue\n"+
|
||||
"Content-Type: text/plain\n"+
|
||||
"Content-Length: 8\n"+
|
||||
"\n",true);
|
||||
checkNotContained(response,offset,"HTTP/1.1 100","8.2.3 expect 100");
|
||||
offset=checkContains(response,offset,"HTTP/1.1 401 ","8.2.3 expect 100")+1;
|
||||
offset=checkContains(response,offset,"Connection: close","8.2.3 expect 100")+1;
|
||||
|
||||
((StdErrLog)Log.getLog()).setHideStacks(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
assertTrue(false);
|
||||
}
|
||||
((StdErrLog)Log.getLog()).setHideStacks(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -247,21 +247,13 @@ public class WebSocketGeneratorD06 implements WebSocketGenerator
|
|||
|
||||
private synchronized int flushBuffer() throws IOException
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_endp.isOpen())
|
||||
throw new EofException();
|
||||
if (!_endp.isOpen())
|
||||
throw new EofException();
|
||||
|
||||
if (_buffer!=null)
|
||||
return _endp.flush(_buffer);
|
||||
if (_buffer!=null)
|
||||
return _endp.flush(_buffer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
System.err.println("FAILED to flush: "+_buffer.toDetailString());
|
||||
throw e;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private synchronized int expelBuffer(long blockFor) throws IOException
|
||||
|
|
|
@ -120,7 +120,7 @@ public class WebSocketParserD01 implements WebSocketParser
|
|||
{
|
||||
int filled=_endp.isOpen()?_endp.fill(_buffer):-1;
|
||||
if (filled<=0)
|
||||
return total_filled;
|
||||
return total_filled>0?total_filled:-1;
|
||||
total_filled+=filled;
|
||||
available=_buffer.length();
|
||||
}
|
||||
|
|
|
@ -80,6 +80,7 @@ public class WebSocketParserD06Test
|
|||
{
|
||||
WebSocketBuffers buffers = new WebSocketBuffers(1024);
|
||||
ByteArrayEndPoint endPoint = new ByteArrayEndPoint();
|
||||
endPoint.setNonBlocking(true);
|
||||
_handler = new Handler();
|
||||
_parser=new WebSocketParserD06(buffers, endPoint,_handler,true);
|
||||
_in = new MaskedByteArrayBuffer();
|
||||
|
@ -243,6 +244,8 @@ public class WebSocketParserD06Test
|
|||
@Test
|
||||
public void testFrameTooLarge() throws Exception
|
||||
{
|
||||
// Buffers are only 1024, so this frame is too large
|
||||
|
||||
_in.sendMask();
|
||||
_in.put((byte)0x84);
|
||||
_in.put((byte)0x7E);
|
||||
|
|
Loading…
Reference in New Issue