297104 suppot HTTP/1.0 CONNECT

git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1554 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
Greg Wilkins 2010-04-23 14:24:02 +00:00
parent 3be6db58f8
commit ae344afe78
9 changed files with 164 additions and 55 deletions

View File

@ -115,6 +115,19 @@ public class Ajp13Generator extends AbstractGenerator
super(buffers, io);
}
/* ------------------------------------------------------------ */
@Override
public boolean isRequest()
{
return false;
}
/* ------------------------------------------------------------ */
@Override
public boolean isResponse()
{
return true;
}
/* ------------------------------------------------------------ */
@Override
public void reset(boolean returnBuffers)
@ -142,8 +155,7 @@ public class Ajp13Generator extends AbstractGenerator
_last = false;
_head = false;
_noContent = false;
_close = false;
_persistent = true;
@ -342,8 +354,8 @@ public class Ajp13Generator extends AbstractGenerator
_last = _last | allContentAdded;
boolean has_server = false;
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
_close = true;
if (_persistent==null)
_persistent=(_version > HttpVersions.HTTP_1_0_ORDINAL);
// get a header buffer
if (_header == null)

View File

@ -62,8 +62,7 @@ public abstract class AbstractGenerator implements Generator
protected boolean _last = false;
protected boolean _head = false;
protected boolean _noContent = false;
protected boolean _close = false;
protected Boolean _persistent = null;
protected Buffer _header; // Buffer for HTTP header (and maybe small _content)
protected Buffer _buffer; // Buffer for copy of passed _content
@ -88,6 +87,12 @@ public abstract class AbstractGenerator implements Generator
this._endp = io;
}
/* ------------------------------------------------------------------------------- */
public abstract boolean isRequest();
/* ------------------------------------------------------------------------------- */
public abstract boolean isResponse();
/* ------------------------------------------------------------------------------- */
public boolean isOpen()
{
@ -104,7 +109,7 @@ public abstract class AbstractGenerator implements Generator
_last = false;
_head = false;
_noContent=false;
_close = false;
_persistent = true;
_contentWritten = 0;
_contentLength = HttpTokens.UNKNOWN_CONTENT;
_date = null;
@ -134,7 +139,7 @@ public abstract class AbstractGenerator implements Generator
throw new IllegalStateException("Flushed");
_last = false;
_close = false;
_persistent=null;
_contentWritten = 0;
_contentLength = HttpTokens.UNKNOWN_CONTENT;
_content=null;
@ -252,13 +257,15 @@ public abstract class AbstractGenerator implements Generator
*/
public boolean isPersistent()
{
return !_close;
return _persistent!=null
?_persistent.booleanValue()
:(isRequest()?true:_version>HttpVersions.HTTP_1_0_ORDINAL);
}
/* ------------------------------------------------------------ */
public void setPersistent(boolean persistent)
{
_close=!persistent;
_persistent=persistent;
}
/* ------------------------------------------------------------ */
@ -402,7 +409,7 @@ public abstract class AbstractGenerator implements Generator
{
if (Log.isDebugEnabled())
Log.debug("ContentLength written=="+_contentWritten+" != contentLength=="+_contentLength);
_close = true;
_persistent = false;
}
}
@ -441,7 +448,8 @@ public abstract class AbstractGenerator implements Generator
if (!isCommitted())
{
setResponse(code, reason);
_close = close;
if (close)
_persistent=false;
completeHeader(null, false);
if (content != null)
addContent(new View(new ByteArrayBuffer(content)), Generator.LAST);

View File

@ -351,7 +351,20 @@ public class HttpGenerator extends AbstractGenerator
Log.debug(e);
throw new InterruptedIOException(e.toString());
}
}
/* ------------------------------------------------------------ */
@Override
public boolean isRequest()
{
return _method!=null;
}
/* ------------------------------------------------------------ */
@Override
public boolean isResponse()
{
return _method==null;
}
/* ------------------------------------------------------------ */
@ -362,7 +375,7 @@ public class HttpGenerator extends AbstractGenerator
return;
// handle a reset
if (_method==null && _status==0)
if (isResponse() && _status==0)
throw new EofException();
if (_last && !allContentAdded)
@ -375,10 +388,10 @@ public class HttpGenerator extends AbstractGenerator
boolean has_server = false;
if (_method!=null)
if (isRequest())
{
_close = false;
// Request
_persistent=true;
if (_version == HttpVersions.HTTP_0_9_ORDINAL)
{
_contentLength = HttpTokens.NO_CONTENT;
@ -402,18 +415,19 @@ public class HttpGenerator extends AbstractGenerator
}
else
{
// Response
// Responses
if (_version == HttpVersions.HTTP_0_9_ORDINAL)
{
_close = true;
_persistent = false;
_contentLength = HttpTokens.EOF_CONTENT;
_state = STATE_CONTENT;
return;
}
else
{
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
_close = true;
if (_persistent==null)
_persistent= (_version > HttpVersions.HTTP_1_0_ORDINAL);
// add response line
Status status = _status<__status.length?__status[_status]:null;
@ -528,7 +542,7 @@ public class HttpGenerator extends AbstractGenerator
break;
case HttpHeaders.CONNECTION_ORDINAL:
if (_method!=null)
if (isRequest())
field.put(_header);
int connection_value = field.getValueOrdinal();
@ -547,10 +561,10 @@ public class HttpGenerator extends AbstractGenerator
{
case HttpHeaderValues.CLOSE_ORDINAL:
close=true;
if (_method==null)
_close=true;
if (isResponse())
_persistent=false;
keep_alive=false;
if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
_contentLength = HttpTokens.EOF_CONTENT;
break;
@ -558,8 +572,8 @@ public class HttpGenerator extends AbstractGenerator
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
{
keep_alive = true;
if (_method==null)
_close = false;
if (isResponse())
_persistent = true;
}
break;
@ -586,7 +600,7 @@ public class HttpGenerator extends AbstractGenerator
case HttpHeaderValues.UPGRADE_ORDINAL:
{
// special case for websocket connection ordering
if (_method==null)
if (isResponse())
{
field.put(_header);
continue;
@ -595,9 +609,9 @@ public class HttpGenerator extends AbstractGenerator
case HttpHeaderValues.CLOSE_ORDINAL:
{
close=true;
if (_method==null)
_close=true;
if (_close && _method==null && _contentLength == HttpTokens.UNKNOWN_CONTENT)
if (isResponse())
_persistent=false;
if (!_persistent && isResponse() && _contentLength == HttpTokens.UNKNOWN_CONTENT)
_contentLength = HttpTokens.EOF_CONTENT;
break;
}
@ -606,8 +620,8 @@ public class HttpGenerator extends AbstractGenerator
if (_version == HttpVersions.HTTP_1_0_ORDINAL)
{
keep_alive = true;
if (_method==null)
_close = false;
if (isResponse())
_persistent=true;
}
break;
}
@ -655,13 +669,13 @@ public class HttpGenerator extends AbstractGenerator
// written yet?
// Response known not to have a body
if (_contentWritten == 0 && _method==null && (_status < 200 || _status == 204 || _status == 304))
if (_contentWritten == 0 && isResponse() && (_status < 200 || _status == 204 || _status == 304))
_contentLength = HttpTokens.NO_CONTENT;
else if (_last)
{
// we have seen all the _content there is
_contentLength = _contentWritten;
if (content_length == null && (_method==null || _contentLength>0 || content_type ))
if (content_length == null && (isResponse() || _contentLength>0 || content_type ))
{
// known length but not actually set.
_header.put(HttpHeaders.CONTENT_LENGTH_BUFFER);
@ -674,8 +688,8 @@ public class HttpGenerator extends AbstractGenerator
else
{
// No idea, so we must assume that a body is coming
_contentLength = (_close || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
if (_method!=null && _contentLength==HttpTokens.EOF_CONTENT)
_contentLength = (!_persistent || _version < HttpVersions.HTTP_1_1_ORDINAL ) ? HttpTokens.EOF_CONTENT : HttpTokens.CHUNKED_CONTENT;
if (isRequest() && _contentLength==HttpTokens.EOF_CONTENT)
{
_contentLength=HttpTokens.NO_CONTENT;
_noContent=true;
@ -684,12 +698,12 @@ public class HttpGenerator extends AbstractGenerator
break;
case HttpTokens.NO_CONTENT:
if (content_length == null && _method==null && _status >= 200 && _status != 204 && _status != 304)
if (content_length == null && isResponse() && _status >= 200 && _status != 204 && _status != 304)
_header.put(CONTENT_LENGTH_0);
break;
case HttpTokens.EOF_CONTENT:
_close = _method==null;
_persistent = isRequest();
break;
case HttpTokens.CHUNKED_CONTENT:
@ -720,12 +734,12 @@ public class HttpGenerator extends AbstractGenerator
if (_contentLength==HttpTokens.EOF_CONTENT)
{
keep_alive=false;
_close=true;
_persistent=false;
}
if (_method==null)
if (isResponse())
{
if (_close && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
if (!_persistent && (close || _version > HttpVersions.HTTP_1_0_ORDINAL))
{
_header.put(CONNECTION_CLOSE);
if (connection!=null)
@ -872,7 +886,7 @@ public class HttpGenerator extends AbstractGenerator
{
if (_state == STATE_FLUSHING)
_state = STATE_END;
if (_state==STATE_END && _close && _status!=100)
if (_state==STATE_END && !_persistent && _status!=100)
_endp.close();
}
else

View File

@ -2,6 +2,7 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.concurrent.CountDownLatch;
@ -10,6 +11,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpMethods;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.io.Buffer;
@ -171,7 +173,7 @@ public class ProxyHandler extends HandlerWrapper
if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
{
_logger.debug("CONNECT request for {}", request.getRequestURI(), null);
handleConnect(request, response, request.getRequestURI());
handleConnect(baseRequest,request, response, request.getRequestURI());
}
else
{
@ -190,7 +192,7 @@ public class ProxyHandler extends HandlerWrapper
* @throws ServletException if an application error occurs
* @throws IOException if an I/O error occurs
*/
protected void handleConnect(HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
protected void handleConnect(Request baseRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
{
boolean proceed = handleAuthentication(request, response, serverAddress);
if (!proceed)
@ -237,8 +239,12 @@ public class ProxyHandler extends HandlerWrapper
// CONNECT expects a 200 response
response.setStatus(HttpServletResponse.SC_OK);
// Flush it so that the client receives it
response.flushBuffer();
// Prevent close
((HttpGenerator)baseRequest.getConnection().getGenerator()).setPersistent(true);
// close to force last flush it so that the client receives it
response.getOutputStream().close();
upgradeConnection(request, response, clientToProxy);
}
@ -608,15 +614,21 @@ public class ProxyHandler extends HandlerWrapper
}
return this;
}
catch (ClosedChannelException x)
{
_logger.debug("ClientToProxy",x);
closeServer();
throw x;
}
catch (IOException x)
{
_logger.warn("ClientToProxy: Unexpected exception", x);
_logger.warn("ClientToProxy", x);
close();
throw x;
}
catch (RuntimeException x)
{
_logger.warn("ClientToProxy: Unexpected exception", x);
_logger.warn("ClientToProxy", x);
close();
throw x;
}

View File

@ -400,11 +400,19 @@ public class HttpServerTestBase extends TestCase
for (int c=0;c<1;c++)
{
String test=encoding[e]+"x"+b+"x"+w+"x"+c;
URL url=new URL("http://"+HOST+":"+port+"/?writes="+w+"&block="+b+ (e==0?"":("&encoding="+encoding[e]))+(c==0?"&chars=true":""));
InputStream in = (InputStream)url.getContent();
String response=IO.toString(in,e==0?null:encoding[e]);
assertEquals(test,b*w,response.length());
try
{
URL url=new URL("http://"+HOST+":"+port+"/?writes="+w+"&block="+b+ (e==0?"":("&encoding="+encoding[e]))+(c==0?"&chars=true":""));
InputStream in = (InputStream)url.getContent();
String response=IO.toString(in,e==0?null:encoding[e]);
assertEquals(test,b*w,response.length());
}
catch(Exception ex)
{
System.err.println(test);
throw ex;
}
}
}
}

View File

@ -29,6 +29,18 @@ public class HttpWriterTest extends TestCase
ByteArrayEndPoint endp = new ByteArrayEndPoint();
AbstractGenerator generator = new AbstractGenerator(buffers,endp)
{
@Override
public boolean isRequest()
{
return false;
}
@Override
public boolean isResponse()
{
return true;
}
@Override
public void completeHeader(HttpFields fields, boolean allContentAdded) throws IOException
{

View File

@ -23,4 +23,10 @@ public class SocketServerTest extends HttpServerTestBase
{
super(new SocketConnector());
}
@Override
public void testFlush() throws Exception
{
super.testFlush();
}
}

View File

@ -125,7 +125,7 @@ public abstract class AbstractProxyHandlerTest extends TestCase
protected Socket newSocket() throws IOException
{
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
socket.setSoTimeout(5000);
socket.setSoTimeout(500000);
return socket;
}

View File

@ -89,6 +89,43 @@ public class ProxyHandlerConnectTest extends AbstractProxyHandlerTest
}
}
public void testCONNECT10AndGET() throws Exception
{
String hostPort = "localhost:" + serverConnector.getLocalPort();
String request = "" +
"CONNECT " + hostPort + " HTTP/1.0\r\n" +
"Host: " + hostPort + "\r\n" +
"\r\n";
Socket socket = newSocket();
try
{
OutputStream output = socket.getOutputStream();
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
output.write(request.getBytes("UTF-8"));
output.flush();
// Expect 200 OK from the CONNECT request
Response response = readResponse(input);
assertEquals("200", response.getCode());
request = "" +
"GET /echo" + " HTTP/1.1\r\n" +
"Host: " + hostPort + "\r\n" +
"\r\n";
output.write(request.getBytes("UTF-8"));
output.flush();
response = readResponse(input);
assertEquals("200", response.getCode());
assertEquals("GET /echo", response.getBody());
}
finally
{
socket.close();
}
}
public void testCONNECTAndGETPipelined() throws Exception
{
String hostPort = "localhost:" + serverConnector.getLocalPort();