Merge remote-tracking branch 'origin/master' into jetty-8
Conflicts: jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java
This commit is contained in:
commit
d862348b4c
|
@ -360,26 +360,29 @@ public class HttpConnection extends AbstractConnection implements Dumpable
|
|||
// we wont be called again so let the parser see the close
|
||||
complete=true;
|
||||
_parser.parseAvailable();
|
||||
// TODO should not need this
|
||||
if (!(_parser.isComplete()||_parser.isIdle()))
|
||||
{
|
||||
LOG.warn("Incomplete {} {}",_parser,_endp);
|
||||
if (_exchange!=null)
|
||||
_exchange.cancel();
|
||||
if (_exchange!=null && !_exchange.isDone())
|
||||
{
|
||||
_exchange.setStatus(HttpExchange.STATUS_EXCEPTED);
|
||||
_exchange.getEventListener().onException(new EOFException("Incomplete"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO - is this needed ?
|
||||
if (_generator.isComplete() && !_parser.isComplete())
|
||||
// TODO should not need this
|
||||
if (_endp.isInputShutdown() && !_parser.isComplete())
|
||||
{
|
||||
if (!_endp.isOpen() || _endp.isInputShutdown())
|
||||
if (_exchange!=null && !_exchange.isDone())
|
||||
{
|
||||
complete=true;
|
||||
close=true;
|
||||
close();
|
||||
_exchange.setStatus(HttpExchange.STATUS_EXCEPTED);
|
||||
_exchange.getEventListener().onException(new EOFException("Incomplete"));
|
||||
}
|
||||
_endp.close();
|
||||
}
|
||||
*/
|
||||
|
||||
if (complete || failed)
|
||||
{
|
||||
|
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
package org.eclipse.jetty.client;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
@ -25,6 +25,9 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
|
@ -37,7 +40,7 @@ public abstract class AbstractConnectionTest
|
|||
// httpClient.setConnectorType(HttpClient.CONNECTOR_SOCKET);
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testServerClosedConnection() throws Exception
|
||||
{
|
||||
|
@ -57,6 +60,19 @@ public abstract class AbstractConnectionTest
|
|||
httpClient.send(exchange);
|
||||
|
||||
Socket remote = serverSocket.accept();
|
||||
|
||||
// HttpClient.send() above is async, so if we write the response immediately
|
||||
// there is a chance that it arrives before the request is being sent, so we
|
||||
// read the request before sending the response to avoid the race
|
||||
InputStream input = remote.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if (line.length() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
OutputStream output = remote.getOutputStream();
|
||||
output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8"));
|
||||
output.write("Content-Length: 0\r\n".getBytes("UTF-8"));
|
||||
|
@ -80,6 +96,15 @@ public abstract class AbstractConnectionTest
|
|||
httpClient.send(exchange);
|
||||
|
||||
remote = serverSocket.accept();
|
||||
|
||||
input = remote.getInputStream();
|
||||
reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if (line.length() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
output = remote.getOutputStream();
|
||||
output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8"));
|
||||
output.write("Content-Length: 0\r\n".getBytes("UTF-8"));
|
||||
|
@ -94,6 +119,105 @@ public abstract class AbstractConnectionTest
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerClosedIncomplete() throws Exception
|
||||
{
|
||||
ServerSocket serverSocket = new ServerSocket();
|
||||
serverSocket.bind(null);
|
||||
int port=serverSocket.getLocalPort();
|
||||
|
||||
HttpClient httpClient = newHttpClient();
|
||||
httpClient.setMaxConnectionsPerAddress(1);
|
||||
httpClient.start();
|
||||
try
|
||||
{
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
HttpExchange exchange = new ConnectionExchange(latch);
|
||||
exchange.setAddress(new Address("localhost", port));
|
||||
exchange.setRequestURI("/");
|
||||
httpClient.send(exchange);
|
||||
|
||||
Socket remote = serverSocket.accept();
|
||||
|
||||
// HttpClient.send() above is async, so if we write the response immediately
|
||||
// there is a chance that it arrives before the request is being sent, so we
|
||||
// read the request before sending the response to avoid the race
|
||||
InputStream input = remote.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if (line.length() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
OutputStream output = remote.getOutputStream();
|
||||
output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8"));
|
||||
output.write("Content-Length: 10\r\n".getBytes("UTF-8"));
|
||||
output.write("\r\n".getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
remote.close();
|
||||
|
||||
assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerHalfClosedIncomplete() throws Exception
|
||||
{
|
||||
ServerSocket serverSocket = new ServerSocket();
|
||||
serverSocket.bind(null);
|
||||
int port=serverSocket.getLocalPort();
|
||||
|
||||
HttpClient httpClient = newHttpClient();
|
||||
httpClient.setIdleTimeout(10000);
|
||||
httpClient.setMaxConnectionsPerAddress(1);
|
||||
httpClient.start();
|
||||
try
|
||||
{
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
HttpExchange exchange = new ConnectionExchange(latch);
|
||||
exchange.setAddress(new Address("localhost", port));
|
||||
exchange.setRequestURI("/");
|
||||
httpClient.send(exchange);
|
||||
|
||||
Socket remote = serverSocket.accept();
|
||||
|
||||
// HttpClient.send() above is async, so if we write the response immediately
|
||||
// there is a chance that it arrives before the request is being sent, so we
|
||||
// read the request before sending the response to avoid the race
|
||||
InputStream input = remote.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if (line.length() == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
OutputStream output = remote.getOutputStream();
|
||||
output.write("HTTP/1.1 200 OK\r\n".getBytes("UTF-8"));
|
||||
output.write("Content-Length: 10\r\n".getBytes("UTF-8"));
|
||||
output.write("\r\n".getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
remote.shutdownOutput();
|
||||
|
||||
assertEquals(HttpExchange.STATUS_EXCEPTED, exchange.waitForDone());
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
httpClient.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConnectionFailed() throws Exception
|
||||
{
|
||||
|
|
|
@ -22,4 +22,12 @@ public class AsyncSelectConnectionTest extends AbstractConnectionTest
|
|||
httpClient.setConnectBlocking(false);
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testServerHalfClosedIncomplete() throws Exception
|
||||
{
|
||||
super.testServerHalfClosedIncomplete();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -32,12 +32,4 @@ public class AsyncSslHttpExchangeTest extends SslHttpExchangeTest
|
|||
_port = _server.getConnectors()[0].getLocalPort();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPerf1() throws Exception
|
||||
{
|
||||
sender(10,true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -483,8 +483,13 @@ public abstract class AbstractGenerator implements Generator
|
|||
{
|
||||
if (close)
|
||||
_persistent=false;
|
||||
if (!isCommitted())
|
||||
if (isCommitted())
|
||||
{
|
||||
LOG.debug("sendError on committed: {} {}",code,reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("sendError: {} {}",code,reason);
|
||||
setResponse(code, reason);
|
||||
if (content != null)
|
||||
{
|
||||
|
|
|
@ -4,15 +4,16 @@
|
|||
// All rights reserved. This program and the accompanying materials
|
||||
// are made available under the terms of the Eclipse Public License v1.0
|
||||
// and Apache License v2.0 which accompanies this distribution.
|
||||
// The Eclipse Public License is available at
|
||||
// The Eclipse Public License is available at
|
||||
// http://www.eclipse.org/legal/epl-v10.html
|
||||
// The Apache License v2.0 is available at
|
||||
// http://www.opensource.org/licenses/apache2.0.php
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// You may elect to redistribute this code under either of these licenses.
|
||||
// ========================================================================
|
||||
|
||||
package org.eclipse.jetty.http;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.jetty.io.Buffer;
|
||||
|
@ -67,7 +68,7 @@ public class HttpParser implements Parser
|
|||
private String _multiLineValue;
|
||||
private int _responseStatus; // If >0 then we are parsing a response
|
||||
private boolean _forceContentBuffer;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
protected final View _contentView=new View(); // View of the content in the buffer for {@link Input}
|
||||
protected int _state=STATE_START;
|
||||
|
@ -78,7 +79,7 @@ public class HttpParser implements Parser
|
|||
protected int _chunkLength;
|
||||
protected int _chunkPosition;
|
||||
private boolean _headResponse;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -103,7 +104,7 @@ public class HttpParser implements Parser
|
|||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Constructor.
|
||||
* @param buffers the buffers to use
|
||||
* @param buffers the buffers to use
|
||||
* @param endp the endpoint
|
||||
* @param handler the even handler
|
||||
*/
|
||||
|
@ -134,7 +135,7 @@ public class HttpParser implements Parser
|
|||
{
|
||||
_headResponse=head;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public int getState()
|
||||
{
|
||||
|
@ -170,7 +171,7 @@ public class HttpParser implements Parser
|
|||
{
|
||||
return isState(STATE_END);
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isMoreInBuffer()
|
||||
throws IOException
|
||||
|
@ -203,11 +204,11 @@ public class HttpParser implements Parser
|
|||
if (parseNext()<0)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/**
|
||||
* Parse until END state.
|
||||
* This method will parse any remaining content in the current buffer. It does not care about the
|
||||
* This method will parse any remaining content in the current buffer. It does not care about the
|
||||
* {@link #getState current state} of the parser.
|
||||
* @see #parse
|
||||
* @see #parseNext
|
||||
|
@ -216,7 +217,7 @@ public class HttpParser implements Parser
|
|||
{
|
||||
int progress = parseNext();
|
||||
int total=progress>0?1:0;
|
||||
|
||||
|
||||
// continue parsing
|
||||
while (!isComplete() && _buffer!=null && _buffer.length()>0)
|
||||
{
|
||||
|
@ -237,9 +238,9 @@ public class HttpParser implements Parser
|
|||
{
|
||||
int progress=0;
|
||||
|
||||
if (_state == STATE_END)
|
||||
if (_state == STATE_END)
|
||||
return 0;
|
||||
|
||||
|
||||
if (_buffer==null)
|
||||
{
|
||||
if (_header == null)
|
||||
|
@ -252,24 +253,24 @@ public class HttpParser implements Parser
|
|||
_tok0.setPutIndex(_tok0.getIndex());
|
||||
_tok1.setPutIndex(_tok1.getIndex());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (_state == STATE_CONTENT && _contentPosition == _contentLength)
|
||||
{
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int length=_buffer.length();
|
||||
|
||||
|
||||
// Fill buffer if we can
|
||||
if (length == 0)
|
||||
{
|
||||
long filled=-1;
|
||||
IOException ex=null;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
filled=fill();
|
||||
}
|
||||
catch(IOException e)
|
||||
|
@ -277,8 +278,8 @@ public class HttpParser implements Parser
|
|||
LOG.debug(this.toString(),e);
|
||||
ex=e;
|
||||
}
|
||||
|
||||
if (filled < 0 || _endp.isInputShutdown())
|
||||
|
||||
if (filled < 0 || _endp.isInputShutdown())
|
||||
{
|
||||
if (_headResponse && _state>STATE_END)
|
||||
{
|
||||
|
@ -294,21 +295,25 @@ public class HttpParser implements Parser
|
|||
Buffer chunk=_buffer.get(_buffer.length());
|
||||
_contentPosition += chunk.length();
|
||||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
_handler.content(chunk); // May recurse here
|
||||
}
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if (ex!=null)
|
||||
throw ex;
|
||||
|
||||
if (!isComplete() && !isIdle())
|
||||
throw new EOFException();
|
||||
|
||||
return -1;
|
||||
}
|
||||
length=_buffer.length();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// EventHandler header
|
||||
byte ch;
|
||||
byte[] array=_buffer.array();
|
||||
|
@ -320,16 +325,16 @@ public class HttpParser implements Parser
|
|||
progress++;
|
||||
last=_state;
|
||||
}
|
||||
|
||||
|
||||
ch=_buffer.get();
|
||||
|
||||
|
||||
if (_eol == HttpTokens.CARRIAGE_RETURN && ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
_eol=HttpTokens.LINE_FEED;
|
||||
continue;
|
||||
}
|
||||
_eol=0;
|
||||
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case STATE_START:
|
||||
|
@ -476,22 +481,22 @@ public class HttpParser implements Parser
|
|||
_state=STATE_HEADER_VALUE;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
{
|
||||
// handler last header if any
|
||||
if (_cached!=null || _tok0.length() > 0 || _tok1.length() > 0 || _multiLineValue != null)
|
||||
{
|
||||
|
||||
|
||||
Buffer header=_cached!=null?_cached:HttpHeaders.CACHE.lookup(_tok0);
|
||||
_cached=null;
|
||||
Buffer value=_multiLineValue == null ? _tok1 : new ByteArrayBuffer(_multiLineValue);
|
||||
|
||||
|
||||
int ho=HttpHeaders.CACHE.getOrdinal(header);
|
||||
if (ho >= 0)
|
||||
{
|
||||
int vo;
|
||||
|
||||
int vo;
|
||||
|
||||
switch (ho)
|
||||
{
|
||||
case HttpHeaders.CONTENT_LENGTH_ORDINAL:
|
||||
|
@ -510,7 +515,7 @@ public class HttpParser implements Parser
|
|||
_contentLength=HttpTokens.NO_CONTENT;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case HttpHeaders.TRANSFER_ENCODING_ORDINAL:
|
||||
value=HttpHeaderValues.CACHE.lookup(value);
|
||||
vo=HttpHeaderValues.CACHE.getOrdinal(value);
|
||||
|
@ -521,22 +526,22 @@ public class HttpParser implements Parser
|
|||
String c=value.toString(StringUtil.__ISO_8859_1);
|
||||
if (c.endsWith(HttpHeaderValues.CHUNKED))
|
||||
_contentLength=HttpTokens.CHUNKED_CONTENT;
|
||||
|
||||
|
||||
else if (c.indexOf(HttpHeaderValues.CHUNKED) >= 0)
|
||||
throw new HttpException(400,null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_handler.parsedHeader(header, value);
|
||||
_tok0.setPutIndex(_tok0.getIndex());
|
||||
_tok1.setPutIndex(_tok1.getIndex());
|
||||
_multiLineValue=null;
|
||||
}
|
||||
_buffer.setMarkIndex(-1);
|
||||
|
||||
|
||||
|
||||
|
||||
// now handle ch
|
||||
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
|
@ -556,7 +561,7 @@ public class HttpParser implements Parser
|
|||
_eol=ch;
|
||||
if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
|
||||
_eol=_buffer.get();
|
||||
|
||||
|
||||
// We convert _contentLength to an int for this switch statement because
|
||||
// we don't care about the amount of data available just whether there is some.
|
||||
switch (_contentLength > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) _contentLength)
|
||||
|
@ -565,19 +570,19 @@ public class HttpParser implements Parser
|
|||
_state=STATE_EOF_CONTENT;
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
|
||||
|
||||
case HttpTokens.CHUNKED_CONTENT:
|
||||
_state=STATE_CHUNKED_CONTENT;
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
break;
|
||||
|
||||
|
||||
case HttpTokens.NO_CONTENT:
|
||||
_state=STATE_END;
|
||||
returnBuffers();
|
||||
_handler.headerComplete();
|
||||
_handler.headerComplete();
|
||||
_handler.messageComplete(_contentPosition);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
_state=STATE_CONTENT;
|
||||
_handler.headerComplete(); // May recurse here !
|
||||
|
@ -591,7 +596,7 @@ public class HttpParser implements Parser
|
|||
_length=1;
|
||||
_buffer.mark();
|
||||
_state=STATE_HEADER_NAME;
|
||||
|
||||
|
||||
// try cached name!
|
||||
if (array!=null)
|
||||
{
|
||||
|
@ -604,10 +609,10 @@ public class HttpParser implements Parser
|
|||
length=_buffer.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case STATE_HEADER_NAME:
|
||||
|
@ -629,16 +634,16 @@ public class HttpParser implements Parser
|
|||
case HttpTokens.SPACE:
|
||||
case HttpTokens.TAB:
|
||||
break;
|
||||
default:
|
||||
default:
|
||||
{
|
||||
_cached=null;
|
||||
if (_length == -1)
|
||||
if (_length == -1)
|
||||
_buffer.mark();
|
||||
_length=_buffer.getIndex() - _buffer.markIndex();
|
||||
_state=STATE_HEADER_IN_NAME;
|
||||
_state=STATE_HEADER_IN_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case STATE_HEADER_IN_NAME:
|
||||
|
@ -694,11 +699,11 @@ public class HttpParser implements Parser
|
|||
break;
|
||||
default:
|
||||
{
|
||||
if (_length == -1)
|
||||
if (_length == -1)
|
||||
_buffer.mark();
|
||||
_length=_buffer.getIndex() - _buffer.markIndex();
|
||||
_state=STATE_HEADER_IN_VALUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -732,9 +737,9 @@ public class HttpParser implements Parser
|
|||
break;
|
||||
}
|
||||
} // end of HEADER states loop
|
||||
|
||||
|
||||
// ==========================
|
||||
|
||||
|
||||
// Handle HEAD response
|
||||
if (_responseStatus>0 && _headResponse)
|
||||
{
|
||||
|
@ -743,10 +748,10 @@ public class HttpParser implements Parser
|
|||
}
|
||||
|
||||
// ==========================
|
||||
|
||||
|
||||
// Handle _content
|
||||
length=_buffer.length();
|
||||
Buffer chunk;
|
||||
Buffer chunk;
|
||||
last=_state;
|
||||
while (_state > STATE_END && length > 0)
|
||||
{
|
||||
|
@ -755,7 +760,7 @@ public class HttpParser implements Parser
|
|||
progress++;
|
||||
last=_state;
|
||||
}
|
||||
|
||||
|
||||
if (_eol == HttpTokens.CARRIAGE_RETURN && _buffer.peek() == HttpTokens.LINE_FEED)
|
||||
{
|
||||
_eol=_buffer.get();
|
||||
|
@ -769,11 +774,11 @@ public class HttpParser implements Parser
|
|||
chunk=_buffer.get(_buffer.length());
|
||||
_contentPosition += chunk.length();
|
||||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
_handler.content(chunk); // May recurse here
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return 1;
|
||||
|
||||
case STATE_CONTENT:
|
||||
case STATE_CONTENT:
|
||||
{
|
||||
long remaining=_contentLength - _contentPosition;
|
||||
if (remaining == 0)
|
||||
|
@ -782,24 +787,24 @@ public class HttpParser implements Parser
|
|||
_handler.messageComplete(_contentPosition);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (length > remaining)
|
||||
|
||||
if (length > remaining)
|
||||
{
|
||||
// We can cast reamining to an int as we know that it is smaller than
|
||||
// or equal to length which is already an int.
|
||||
// or equal to length which is already an int.
|
||||
length=(int)remaining;
|
||||
}
|
||||
|
||||
|
||||
chunk=_buffer.get(length);
|
||||
_contentPosition += chunk.length();
|
||||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
|
||||
_handler.content(chunk); // May recurse here
|
||||
|
||||
if(_contentPosition == _contentLength)
|
||||
{
|
||||
_state=STATE_END;
|
||||
_handler.messageComplete(_contentPosition);
|
||||
}
|
||||
}
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return 1;
|
||||
}
|
||||
|
@ -826,7 +831,7 @@ public class HttpParser implements Parser
|
|||
if (ch == HttpTokens.CARRIAGE_RETURN || ch == HttpTokens.LINE_FEED)
|
||||
{
|
||||
_eol=ch;
|
||||
|
||||
|
||||
if (_chunkLength == 0)
|
||||
{
|
||||
if (_eol==HttpTokens.CARRIAGE_RETURN && _buffer.hasContent() && _buffer.peek()==HttpTokens.LINE_FEED)
|
||||
|
@ -870,8 +875,8 @@ public class HttpParser implements Parser
|
|||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_CHUNK:
|
||||
|
||||
case STATE_CHUNK:
|
||||
{
|
||||
int remaining=_chunkLength - _chunkPosition;
|
||||
if (remaining == 0)
|
||||
|
@ -879,13 +884,13 @@ public class HttpParser implements Parser
|
|||
_state=STATE_CHUNKED_CONTENT;
|
||||
break;
|
||||
}
|
||||
else if (length > remaining)
|
||||
else if (length > remaining)
|
||||
length=remaining;
|
||||
chunk=_buffer.get(length);
|
||||
_contentPosition += chunk.length();
|
||||
_chunkPosition += chunk.length();
|
||||
_contentView.update(chunk);
|
||||
_handler.content(chunk); // May recurse here
|
||||
_handler.content(chunk); // May recurse here
|
||||
// TODO adjust the _buffer to keep unconsumed content
|
||||
return 1;
|
||||
}
|
||||
|
@ -893,13 +898,13 @@ public class HttpParser implements Parser
|
|||
|
||||
length=_buffer.length();
|
||||
}
|
||||
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/** fill the buffers from the endpoint
|
||||
*
|
||||
*
|
||||
*/
|
||||
public long fill() throws IOException
|
||||
{
|
||||
|
@ -910,14 +915,14 @@ public class HttpParser implements Parser
|
|||
_tok0=new View.CaseInsensitive(_buffer);
|
||||
_tok1=new View.CaseInsensitive(_buffer);
|
||||
}
|
||||
|
||||
|
||||
// Is there unconsumed content in body buffer
|
||||
if (_state>STATE_END && _buffer==_header && _header!=null && !_header.hasContent() && _body!=null && _body.hasContent())
|
||||
{
|
||||
_buffer=_body;
|
||||
return _buffer.length();
|
||||
}
|
||||
|
||||
|
||||
// Shall we switch to a body buffer?
|
||||
if (_buffer==_header && _state>STATE_END && _header.length()==0 && (_forceContentBuffer || (_contentLength-_contentPosition)>_header.capacity()) && (_body!=null||_buffers!=null))
|
||||
{
|
||||
|
@ -925,20 +930,20 @@ public class HttpParser implements Parser
|
|||
_body=_buffers.getBuffer();
|
||||
_buffer=_body;
|
||||
}
|
||||
|
||||
|
||||
// Do we have somewhere to fill from?
|
||||
if (_endp != null )
|
||||
{
|
||||
// Shall we compact the body?
|
||||
if (_buffer==_body || _state>STATE_END)
|
||||
if (_buffer==_body || _state>STATE_END)
|
||||
{
|
||||
_buffer.compact();
|
||||
}
|
||||
|
||||
|
||||
// Are we full?
|
||||
if (_buffer.space() == 0)
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
|
||||
if (_buffer.space() == 0)
|
||||
throw new HttpException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413, "FULL "+(_buffer==_body?"body":"head"));
|
||||
|
||||
try
|
||||
{
|
||||
return _endp.fill(_buffer);
|
||||
|
@ -955,7 +960,7 @@ public class HttpParser implements Parser
|
|||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
/** Skip any CRLFs in buffers
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void skipCRLF()
|
||||
{
|
||||
|
@ -983,12 +988,12 @@ public class HttpParser implements Parser
|
|||
else
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void reset()
|
||||
{
|
||||
{
|
||||
// reset state
|
||||
_contentView.setGetIndex(_contentView.putIndex());
|
||||
_state=STATE_START;
|
||||
|
@ -1038,12 +1043,12 @@ public class HttpParser implements Parser
|
|||
public void returnBuffers()
|
||||
{
|
||||
if (_body!=null && !_body.hasContent() && _body.markIndex()==-1 && _buffers!=null)
|
||||
{
|
||||
{
|
||||
if (_buffer==_body)
|
||||
_buffer=_header;
|
||||
if (_buffers!=null)
|
||||
_buffers.returnBuffer(_body);
|
||||
_body=null;
|
||||
_body=null;
|
||||
}
|
||||
|
||||
if (_header!=null && !_header.hasContent() && _header.markIndex()==-1 && _buffers!=null)
|
||||
|
@ -1054,7 +1059,7 @@ public class HttpParser implements Parser
|
|||
_header=null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
public void setState(int state)
|
||||
{
|
||||
|
@ -1067,13 +1072,13 @@ public class HttpParser implements Parser
|
|||
{
|
||||
return "state=" + _state + " length=" + _length + " buf=" + buf.hashCode();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "state=" + _state + " length=" + _length + " len=" + _contentLength;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getHeaderBuffer()
|
||||
|
@ -1084,7 +1089,7 @@ public class HttpParser implements Parser
|
|||
}
|
||||
return _header;
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer getBodyBuffer()
|
||||
{
|
||||
|
@ -1098,20 +1103,20 @@ public class HttpParser implements Parser
|
|||
public void setForceContentBuffer(boolean force)
|
||||
{
|
||||
_forceContentBuffer=force;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Buffer blockForContent(long maxIdleTime) throws IOException
|
||||
{
|
||||
if (_contentView.length()>0)
|
||||
return _contentView;
|
||||
if (getState() <= HttpParser.STATE_END)
|
||||
if (getState() <= HttpParser.STATE_END)
|
||||
return null;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
parseNext();
|
||||
|
||||
|
||||
// parse until some progress is made (or IOException thrown for timeout)
|
||||
while(_contentView.length() == 0 && !isState(HttpParser.STATE_END) && _endp!=null && _endp.isOpen())
|
||||
{
|
||||
|
@ -1135,9 +1140,9 @@ public class HttpParser implements Parser
|
|||
_endp.close();
|
||||
throw e;
|
||||
}
|
||||
|
||||
return _contentView.length()>0?_contentView:null;
|
||||
}
|
||||
|
||||
return _contentView.length()>0?_contentView:null;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* (non-Javadoc)
|
||||
|
@ -1155,11 +1160,11 @@ public class HttpParser implements Parser
|
|||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
parseNext();
|
||||
return _contentView==null?0:_contentView.length();
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
|
@ -1187,7 +1192,7 @@ public class HttpParser implements Parser
|
|||
*/
|
||||
public abstract void startRequest(Buffer method, Buffer url, Buffer version)
|
||||
throws IOException;
|
||||
|
||||
|
||||
/**
|
||||
* This is the method called by parser when the HTTP request line is parsed
|
||||
*/
|
||||
|
@ -1197,5 +1202,5 @@ public class HttpParser implements Parser
|
|||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ public interface EndPoint
|
|||
* The buffer may chose to do a compact before filling.
|
||||
* @return an <code>int</code> value indicating the number of bytes
|
||||
* filled or -1 if EOF is reached.
|
||||
* @throws EofException If input is shutdown or the endpoint is closed.
|
||||
*/
|
||||
int fill(Buffer buffer) throws IOException;
|
||||
|
||||
|
@ -59,6 +60,7 @@ public interface EndPoint
|
|||
*
|
||||
* @param buffer The buffer to flush. This buffers getIndex is updated.
|
||||
* @return the number of bytes written
|
||||
* @throws EofException If the endpoint is closed or output is shutdown.
|
||||
*/
|
||||
int flush(Buffer buffer) throws IOException;
|
||||
|
||||
|
@ -157,7 +159,7 @@ public interface EndPoint
|
|||
/* ------------------------------------------------------------ */
|
||||
/** Flush any buffered output.
|
||||
* May fail to write all data if endpoint is non-blocking
|
||||
* @throws IOException
|
||||
* @throws EofException If the endpoint is closed or output is shutdown.
|
||||
*/
|
||||
public void flush() throws IOException;
|
||||
|
||||
|
|
|
@ -38,21 +38,42 @@ public class SelectChannelEndPoint extends ChannelEndPoint implements AsyncEndPo
|
|||
|
||||
private final SelectorManager.SelectSet _selectSet;
|
||||
private final SelectorManager _manager;
|
||||
private SelectionKey _key;
|
||||
private final Runnable _handler = new Runnable()
|
||||
{
|
||||
public void run() { handle(); }
|
||||
};
|
||||
|
||||
/** The desired value for {@link SelectionKey#interestOps()} */
|
||||
private int _interestOps;
|
||||
|
||||
/**
|
||||
* The connection instance is the handler for any IO activity on the endpoint.
|
||||
* There is a different type of connection for HTTP, AJP, WebSocket and
|
||||
* ProxyConnect. The connection may change for an SCEP as it is upgraded
|
||||
* from HTTP to proxy connect or websocket.
|
||||
*/
|
||||
private volatile Connection _connection;
|
||||
|
||||
/** true if a thread has been dispatched to handle this endpoint */
|
||||
private boolean _dispatched = false;
|
||||
|
||||
/** true if a non IO dispatch (eg async resume) is outstanding */
|
||||
private boolean _redispatched = false;
|
||||
|
||||
/** true if the last write operation succeed and wrote all offered bytes */
|
||||
private volatile boolean _writable = true;
|
||||
|
||||
private SelectionKey _key;
|
||||
private int _interestOps;
|
||||
|
||||
/** True if a thread has is blocked in {@link #blockReadable(long)} */
|
||||
private boolean _readBlocked;
|
||||
|
||||
/** True if a thread has is blocked in {@link #blockWritable(long)} */
|
||||
private boolean _writeBlocked;
|
||||
|
||||
/** true if {@link SelectSet#destroyEndPoint(SelectChannelEndPoint)} has not been called */
|
||||
private boolean _open;
|
||||
|
||||
private volatile long _idleTimestamp;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -14,9 +14,15 @@
|
|||
package org.eclipse.jetty.io;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.Reader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.junit.Test;
|
||||
|
@ -42,4 +48,88 @@ public class IOTest
|
|||
out.toString(),
|
||||
"The quick brown fox jumped over the lazy dog");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHalfCloses() throws Exception
|
||||
{
|
||||
ServerSocket connector = new ServerSocket(0);
|
||||
|
||||
Socket client = new Socket("localhost",connector.getLocalPort());
|
||||
System.err.println(client);
|
||||
Socket server = connector.accept();
|
||||
System.err.println(server);
|
||||
|
||||
// we can write both ways
|
||||
client.getOutputStream().write(1);
|
||||
assertEquals(1,server.getInputStream().read());
|
||||
server.getOutputStream().write(1);
|
||||
assertEquals(1,client.getInputStream().read());
|
||||
|
||||
// shutdown output results in read -1
|
||||
client.shutdownOutput();
|
||||
assertEquals(-1,server.getInputStream().read());
|
||||
|
||||
// Even though EOF has been read, the server input is not seen as shutdown
|
||||
assertFalse(server.isInputShutdown());
|
||||
|
||||
// and we can read -1 again
|
||||
assertEquals(-1,server.getInputStream().read());
|
||||
|
||||
// but cannot write
|
||||
try { client.getOutputStream().write(1); assertTrue(false); } catch (SocketException e) {}
|
||||
|
||||
// but can still write in opposite direction.
|
||||
server.getOutputStream().write(1);
|
||||
assertEquals(1,client.getInputStream().read());
|
||||
|
||||
|
||||
// server can shutdown input to match the shutdown out of client
|
||||
server.shutdownInput();
|
||||
|
||||
// now we EOF instead of reading -1
|
||||
try { server.getInputStream().read(); assertTrue(false); } catch (SocketException e) {}
|
||||
|
||||
|
||||
// but can still write in opposite direction.
|
||||
server.getOutputStream().write(1);
|
||||
assertEquals(1,client.getInputStream().read());
|
||||
|
||||
// client can shutdown input
|
||||
client.shutdownInput();
|
||||
|
||||
// now we EOF instead of reading -1
|
||||
try { client.getInputStream().read(); assertTrue(false); } catch (SocketException e) {}
|
||||
|
||||
// But we can still write at the server (data which will never be read)
|
||||
server.getOutputStream().write(1);
|
||||
|
||||
// and the server output is not shutdown
|
||||
assertFalse( server.isOutputShutdown() );
|
||||
|
||||
// until we explictly shut it down
|
||||
server.shutdownOutput();
|
||||
|
||||
// and now we can't write
|
||||
try { server.getOutputStream().write(1); assertTrue(false); } catch (SocketException e) {}
|
||||
|
||||
// but the sockets are still open
|
||||
assertFalse(client.isClosed());
|
||||
assertFalse(server.isClosed());
|
||||
|
||||
// but if we close one end
|
||||
client.close();
|
||||
|
||||
// it is seen as closed.
|
||||
assertTrue(client.isClosed());
|
||||
|
||||
// but not the other end
|
||||
assertFalse(server.isClosed());
|
||||
|
||||
// which has to be closed explictly
|
||||
server.close();
|
||||
assertTrue(server.isClosed());
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ public class AsyncHttpConnection extends HttpConnection
|
|||
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
LOG.debug("handle {}",this);
|
||||
Connection connection = this;
|
||||
boolean some_progress=false;
|
||||
boolean progress=true;
|
||||
|
@ -60,6 +59,11 @@ public class AsyncHttpConnection extends HttpConnection
|
|||
// Flush output from buffering endpoint
|
||||
if (_endp.isBufferingOutput())
|
||||
_endp.flush();
|
||||
|
||||
// Special case close handling.
|
||||
// If we were dispatched and have made no progress, but io is shutdown, then close
|
||||
if (!progress && !some_progress && (_endp.isInputShutdown()||_endp.isOutputShutdown()))
|
||||
_endp.close();
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
|
@ -123,13 +127,12 @@ public class AsyncHttpConnection extends HttpConnection
|
|||
{
|
||||
setCurrentConnection(null);
|
||||
_parser.returnBuffers();
|
||||
_generator.returnBuffers();
|
||||
|
||||
// Are we write blocked
|
||||
if (_generator.isCommitted() && !_generator.isComplete() && _endp.isOpen())
|
||||
((AsyncEndPoint)_endp).scheduleWrite();
|
||||
else
|
||||
_generator.returnBuffers();
|
||||
|
||||
// Check if we are write blocked
|
||||
if (_generator.isCommitted() && !_generator.isComplete() && _endp.isOpen() && !_endp.isOutputShutdown())
|
||||
((AsyncEndPoint)_endp).scheduleWrite(); // TODO. This should not be required
|
||||
|
||||
if (!some_progress)
|
||||
{
|
||||
_total_no_progress++;
|
||||
|
@ -137,11 +140,6 @@ public class AsyncHttpConnection extends HttpConnection
|
|||
if (NO_PROGRESS_INFO>0 && _total_no_progress%NO_PROGRESS_INFO==0 && (NO_PROGRESS_CLOSE<=0 || _total_no_progress< NO_PROGRESS_CLOSE))
|
||||
{
|
||||
LOG.info("EndPoint making no progress: "+_total_no_progress+" "+_endp);
|
||||
|
||||
LOG.setDebugEnabled(true);
|
||||
Log.getLogger("org.eclipse.jetty.io.nio").getLogger("ssl").setDebugEnabled(true);
|
||||
Log.getLogger(ChannelEndPoint.class).setDebugEnabled(true);
|
||||
|
||||
}
|
||||
|
||||
if (NO_PROGRESS_CLOSE>0 && _total_no_progress>NO_PROGRESS_CLOSE)
|
||||
|
@ -154,7 +152,6 @@ public class AsyncHttpConnection extends HttpConnection
|
|||
}
|
||||
}
|
||||
}
|
||||
LOG.debug("unhandle {}",this);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
|
|
@ -455,22 +455,22 @@ public abstract class HttpConnection extends AbstractConnection
|
|||
{
|
||||
async_exception=e;
|
||||
LOG.debug(e);
|
||||
_request.setHandled(true);
|
||||
error=true;
|
||||
_request.setHandled(true);
|
||||
}
|
||||
catch (UncheckedIOException e)
|
||||
{
|
||||
async_exception=e;
|
||||
LOG.debug(e);
|
||||
_request.setHandled(true);
|
||||
error=true;
|
||||
_request.setHandled(true);
|
||||
}
|
||||
catch (HttpException e)
|
||||
{
|
||||
LOG.debug(e);
|
||||
error=true;
|
||||
_request.setHandled(true);
|
||||
_response.sendError(e.getStatus(), e.getReason());
|
||||
error=true;
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
|
@ -478,9 +478,8 @@ public abstract class HttpConnection extends AbstractConnection
|
|||
throw (ThreadDeath)e;
|
||||
|
||||
async_exception=e;
|
||||
|
||||
error=true;
|
||||
LOG.warn(String.valueOf(_uri),e);
|
||||
error=true;
|
||||
_request.setHandled(true);
|
||||
_generator.sendError(info==null?400:500, null, null, true);
|
||||
}
|
||||
|
@ -515,7 +514,12 @@ public abstract class HttpConnection extends AbstractConnection
|
|||
if(_endp.isOpen())
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
_endp.shutdownOutput();
|
||||
_generator.setPersistent(false);
|
||||
if (!_generator.isComplete())
|
||||
_response.complete();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!_response.isCommitted() && !_request.isHandled())
|
||||
|
|
|
@ -45,7 +45,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleWithRequest10() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleWithRequest10");
|
||||
configureServer(new HelloWorldHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
@ -77,7 +76,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleWithRequest11() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleWithRequest11");
|
||||
configureServer(new EchoHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
@ -112,7 +110,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleNoRequest() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleNoRequest");
|
||||
configureServer(new EchoHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
@ -141,7 +138,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleWithSlowRequest() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleWithSlowRequest");
|
||||
configureServer(new EchoHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
@ -182,7 +178,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleWithSlowResponse() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleWithSlowResponse");
|
||||
configureServer(new SlowResponseHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
@ -212,7 +207,6 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
|
|||
@Test
|
||||
public void testMaxIdleWithWait() throws Exception
|
||||
{
|
||||
System.err.println("testMaxIdleWithWait");
|
||||
configureServer(new WaitHandler());
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
client.setSoTimeout(10000);
|
||||
|
|
|
@ -37,8 +37,11 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.StdErrLog;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -106,7 +109,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
* Feed the server the entire request at once.
|
||||
*/
|
||||
@Test
|
||||
public void testRequest1_jetty() throws Exception
|
||||
public void testRequest1() throws Exception
|
||||
{
|
||||
configureServer(new HelloWorldHandler());
|
||||
|
||||
|
@ -164,7 +167,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
* Feed the server fragmentary headers and see how it copes with it.
|
||||
*/
|
||||
@Test
|
||||
public void testRequest1Fragments_jetty() throws Exception, InterruptedException
|
||||
public void testRequest1Fragments() throws Exception, InterruptedException
|
||||
{
|
||||
configureServer(new HelloWorldHandler());
|
||||
|
||||
|
@ -197,7 +200,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequest2_jetty() throws Exception
|
||||
public void testRequest2() throws Exception
|
||||
{
|
||||
configureServer(new EchoHandler());
|
||||
|
||||
|
@ -226,7 +229,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequest2Fragments_jetty() throws Exception
|
||||
public void testRequest2Fragments() throws Exception
|
||||
{
|
||||
configureServer(new EchoHandler());
|
||||
|
||||
|
@ -270,7 +273,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRequest2Iterate_jetty() throws Exception
|
||||
public void testRequest2Iterate() throws Exception
|
||||
{
|
||||
configureServer(new EchoHandler());
|
||||
|
||||
|
@ -309,7 +312,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
* After several iterations, I generated some known bad fragment points.
|
||||
*/
|
||||
@Test
|
||||
public void testRequest2KnownBad_jetty() throws Exception
|
||||
public void testRequest2KnownBad() throws Exception
|
||||
{
|
||||
configureServer(new EchoHandler());
|
||||
|
||||
|
@ -914,6 +917,68 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommittedError() throws Exception
|
||||
{
|
||||
CommittedErrorHandler handler =new CommittedErrorHandler();
|
||||
configureServer(handler);
|
||||
|
||||
Socket client=newSocket(HOST,_connector.getLocalPort());
|
||||
try
|
||||
{
|
||||
((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(true);
|
||||
OutputStream os=client.getOutputStream();
|
||||
InputStream is=client.getInputStream();
|
||||
|
||||
// Send a request
|
||||
os.write((
|
||||
"GET / HTTP/1.1\r\n"+
|
||||
"Host: "+HOST+":"+_connector.getLocalPort()+"\r\n" +
|
||||
"\r\n"
|
||||
).getBytes());
|
||||
os.flush();
|
||||
|
||||
client.setSoTimeout(2000);
|
||||
String in = IO.toString(is);
|
||||
|
||||
assertEquals(-1,is.read()); // Closed by error!
|
||||
|
||||
assertTrue(in.indexOf("HTTP/1.1 200 OK")>=0);
|
||||
assertTrue(in.indexOf("Transfer-Encoding: chunked")>0);
|
||||
assertTrue(in.indexOf("Now is the time for all good men to come to the aid of the party")>0);
|
||||
assertTrue(in.indexOf("\r\n0\r\n")==-1); // chunking is interrupted by error close
|
||||
|
||||
client.close();
|
||||
Thread.sleep(100);
|
||||
assertTrue(!handler._endp.isOpen());
|
||||
}
|
||||
finally
|
||||
{
|
||||
((StdErrLog)Log.getLogger(HttpConnection.class)).setHideStacks(false);
|
||||
|
||||
if (!client.isClosed())
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected static class CommittedErrorHandler extends AbstractHandler
|
||||
{
|
||||
public EndPoint _endp;
|
||||
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
_endp=baseRequest.getConnection().getEndPoint();
|
||||
response.setHeader("test","value");
|
||||
response.setStatus(200);
|
||||
response.setContentType("text/plain");
|
||||
response.getWriter().println("Now is the time for all good men to come to the aid of the party");
|
||||
response.getWriter().flush();
|
||||
response.flushBuffer();
|
||||
|
||||
throw new ServletException(new Exception("exception after commit"));
|
||||
}
|
||||
}
|
||||
|
||||
protected static class AvailableHandler extends AbstractHandler
|
||||
{
|
||||
public Exchanger<Object> _ex = new Exchanger<Object>();
|
||||
|
|
|
@ -25,10 +25,12 @@ public class SelectChannelServerTest extends HttpServerTestBase
|
|||
{
|
||||
startServer(new SelectChannelConnector());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void testBigBlocks() throws Exception
|
||||
public void testCommittedError() throws Exception
|
||||
{
|
||||
super.testBigBlocks();
|
||||
super.testCommittedError();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -277,11 +277,11 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
}
|
||||
}
|
||||
|
||||
// TODO is this too soon?
|
||||
/* Set the webapp's classpath for Jasper */
|
||||
context.setAttribute("org.apache.catalina.jsp_classpath", context.getClassPath());
|
||||
|
||||
/* Set the system classpath for Jasper */
|
||||
holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context));
|
||||
holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context));
|
||||
}
|
||||
|
||||
//Set the servlet-class
|
||||
|
@ -327,6 +327,8 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
{
|
||||
holder.setForcedPath(jsp_file);
|
||||
holder.setClassName(jspServletClass);
|
||||
//set the system classpath explicitly for the holder that will represent the JspServlet instance
|
||||
holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context));
|
||||
}
|
||||
|
||||
// handle load-on-startup
|
||||
|
|
|
@ -95,12 +95,11 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<!--
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.mortbay.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<version>7.1.1-SNAPSHOT</version>
|
||||
<version>7.5.2-SNAPSHOT</version>
|
||||
<configuration>
|
||||
<stopPort>8087</stopPort>
|
||||
<stopKey>foo</stopKey>
|
||||
|
|
|
@ -259,6 +259,17 @@
|
|||
<url-pattern>/javadoc-proxy/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
|
||||
|
||||
<servlet>
|
||||
<servlet-name>foo.jsp</servlet-name>
|
||||
<jsp-file>/jsp/foo/foo.jsp</jsp-file>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>foo.jsp</servlet-name>
|
||||
<url-pattern>/jsp/foo/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<error-page>
|
||||
<error-code>404</error-code>
|
||||
<location>/error404.html</location>
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<h1>FOO Example</h1>
|
||||
<hr>
|
||||
<p>A trivial FOO example
|
||||
<hr>
|
||||
<c:forEach var="i" begin="1" end="10" step="1">
|
||||
<c:out value="${i}" />
|
||||
<br />
|
||||
</c:forEach>
|
||||
</body>
|
||||
</html>
|
|
@ -11,6 +11,7 @@
|
|||
<li><a href="tagfile.jsp">JSP 2.0 Tag File demo</a><br/>
|
||||
<li><a href="expr.jsp?A=1">JSP 2.0 Tag Expression</a><br/>
|
||||
<li><a href="jstl.jsp">JSTL Expression</a><br/>
|
||||
<li><a href="foo/">Mapping to <jsp-file></a><br/>
|
||||
</ul>
|
||||
<a href="/">Main Menu</a>
|
||||
|
||||
|
|
Loading…
Reference in New Issue