Merge from master

This commit is contained in:
Jesse McConnell 2012-02-21 11:38:28 -06:00
commit eed2a2a0f7
10 changed files with 3551 additions and 93 deletions

View File

@ -157,7 +157,8 @@ public class MongoSessionIdManager extends AbstractSessionIdManager
DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
for ( DBObject session : checkSessions )
{
{
__log.debug("SessionIdManager:scavenge: invalidating " + (String)session.get(MongoSessionManager.__ID));
invalidateAll((String)session.get(MongoSessionManager.__ID));
}
}

View File

@ -11,7 +11,7 @@ The easiest place to put these lines are in the start.ini file.
For debugging the spengo authentication the following options are helpful:
Dorg.eclipse.jetty.util.log.DEBUG=true
-Dorg.eclipse.jetty.LEVEL=debug
-Dsun.security.spnego.debug=all
@ -62,54 +62,4 @@ embedded, via the jetty.xml or in a context file for the webapp.
</Get>
Important Configuration Files:
spengo.properties - configures the user realm with runtime properties
krb5.ini - configures the underlying kerberos setup
spnego.conf - configures the glue between gssapi and kerberos
It is important to note that the keytab file referenced in the krb5.ini and the spengo.conf files needs to
contain the keytab for the targetName for the http server. To do this use a process similar to this:
On the windows active domain controller run:
> setspn -A HTTP/linux.mortbay.org ADUser
To create the keytab file use the following process:
> ktpass -out c:\dir\krb5.keytab -princ HTTP/linux.mortbay.org@MORTBAY.ORG -mapUser ADUser -mapOp set -pass ADUserPWD -crypto RC4-HMAC-NT -pType KRB5_NT_PRINCIPAL
This step should give you the keytab file which should then be copied over to the machine running this
http server and referenced from the configuration files. For our testing we put the keytab into the etc
directory of jetty and referenced it from there.
Setting up your Browser:
Firefox:
* browse to about:config and agree to the warnings
* search through to find the 'network' settings
** set network.negotiate-auth.delegation-uris to http://,https://
** set network.negotiate-auth.trusted-uris to http://,https://
IE:
* Tools -> Options -> Security -> Local Intranet -> Sites
** make sure everything is checked here
* Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced
** add url to server (http:// and/or https://) making sure to use the hostname
* Tools -> Options -> Security -> Local Intranet -> Sites -> Advanced -> Close
* Tools -> Options -> Security -> Local Intranet -> Sites -> Ok
* Tools -> Options -> Advanced -> Security (in the checkbox list)
** locate and check 'Enable Integrated Windows Authentication'
* Tools -> Options -> Advanced -> Security -> Ok
* close IE then reopen and browse to your spengo protected resource
NOTE: you must go to the hostname and not the IP, if you go to the IP it will default to NTLM authentication...the following conditions apply to having spnego work
* Intranet Zone
* Accessing the server using a Hostname rather then IP
* Integrated Windows Authentication in IE is enabled, the host is trusted in Firefox
* The Server is not local to the browser
* The client's Kerberos system is authenticated to a domain controller
8

View File

@ -372,6 +372,9 @@ public class Server extends HandlerWrapper implements Attributes
baseRequest.setAttribute(AsyncContext.ASYNC_QUERY_STRING,baseRequest.getQueryString());
baseRequest.setAttribute(AsyncContext.ASYNC_CONTEXT_PATH,state.getSuspendedContext().getContextPath());
// Part of #371649 reset here since we skip it in finally
baseRequest.setServletPath(null);
final String contextPath=state.getServletContext().getContextPath();
HttpURI uri = new HttpURI(URIUtil.addPaths(contextPath,path));

View File

@ -1005,6 +1005,7 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
}
finally
{
if (old_context != _scontext)
{
// reset the classloader
@ -1017,8 +1018,13 @@ public class ContextHandler extends ScopedHandler implements Attributes, Server.
baseRequest.setContext(old_context);
__context.set(old_context);
baseRequest.setContextPath(old_context_path);
baseRequest.setServletPath(old_servlet_path);
baseRequest.setPathInfo(old_path_info);
// #371649 if we have started async then we need to protect this state
if (!baseRequest.getAsyncContinuation().isAsyncStarted())
{
baseRequest.setServletPath(old_servlet_path);
baseRequest.setPathInfo(old_path_info);
}
}
}
}

View File

@ -51,6 +51,7 @@ import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.security.IdentityService;
import org.eclipse.jetty.security.SecurityHandler;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.AsyncContext;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.AbstractHttpConnection;
import org.eclipse.jetty.server.Request;
@ -357,7 +358,7 @@ public class ServletHandler extends ScopedHandler
// Get the base requests
final String old_servlet_path=baseRequest.getServletPath();
final String old_path_info=baseRequest.getPathInfo();
DispatcherType type = baseRequest.getDispatcherType();
ServletHolder servlet_holder=null;
@ -382,7 +383,7 @@ public class ServletHandler extends ScopedHandler
baseRequest.setAttribute(Dispatcher.INCLUDE_PATH_INFO, path_info);
}
else
{
{
baseRequest.setServletPath(servlet_path);
baseRequest.setPathInfo(path_info);
}
@ -403,6 +404,21 @@ public class ServletHandler extends ScopedHandler
old_scope=baseRequest.getUserIdentityScope();
baseRequest.setUserIdentityScope(servlet_holder);
/*
* this is an interim solution for Bug 371635
*
* these will always be set now, when they ought to only be set on the dispatch
*/
if ( baseRequest.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) == null )
{
baseRequest.setAttribute(AsyncContext.ASYNC_SERVLET_PATH,baseRequest.getServletPath());
}
if ( baseRequest.getAttribute(AsyncContext.ASYNC_PATH_INFO) == null )
{
baseRequest.setAttribute(AsyncContext.ASYNC_PATH_INFO,baseRequest.getPathInfo());
}
// start manual inline of nextScope(target,baseRequest,request,response);
if (never())
nextScope(target,baseRequest,request,response);
@ -416,13 +432,18 @@ public class ServletHandler extends ScopedHandler
}
finally
{
if (old_scope!=null)
baseRequest.setUserIdentityScope(old_scope);
if (!(DispatcherType.INCLUDE.equals(type)))
// #371649 if we have started async then we need to protect this state
if (!baseRequest.getAsyncContinuation().isAsyncStarted())
{
baseRequest.setServletPath(old_servlet_path);
baseRequest.setPathInfo(old_path_info);
if (old_scope != null)
baseRequest.setUserIdentityScope(old_scope);
if (!(DispatcherType.INCLUDE.equals(type)))
{
baseRequest.setServletPath(old_servlet_path);
baseRequest.setPathInfo(old_path_info);
}
}
}
}

View File

@ -4,7 +4,6 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@ -12,6 +11,9 @@ import javax.servlet.http.HttpServletResponse;
import junit.framework.Assert;
import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.server.AsyncContext;
import org.eclipse.jetty.server.AsyncContinuation;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.LocalConnector;
@ -55,7 +57,7 @@ public class AsyncContextTest
}
@Test
@Ignore ("test fails without a patch")
//Ignore ("test fails without a patch")
public void testSimpleAsyncContext() throws Exception
{
String request = "GET /servletPath HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
@ -76,7 +78,7 @@ public class AsyncContextTest
}
@Test
@Ignore ("test fails without a patch")
//Ignore ("test fails without a patch")
public void testDispatchAsyncContext() throws Exception
{
String request = "GET /servletPath?dispatch=true HTTP/1.1\r\n" + "Host: localhost\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n"
@ -102,7 +104,7 @@ public class AsyncContextTest
}
@Test
@Ignore ("test fails without a patch")
//Ignore ("test fails without a patch")
public void testSimpleWithContextAsyncContext() throws Exception
{
_contextHandler.setContextPath("/foo");
@ -125,7 +127,7 @@ public class AsyncContextTest
}
@Test
@Ignore ("test fails without a patch")
//Ignore ("test fails without a patch")
public void testDispatchWithContextAsyncContext() throws Exception
{
_contextHandler.setContextPath("/foo");
@ -168,24 +170,27 @@ public class AsyncContextTest
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
AsyncContinuation continuation = (AsyncContinuation) ContinuationSupport.getContinuation(request);
if (request.getParameter("dispatch") != null)
{
System.out.println("DISPATCHING");
AsyncContext asyncContext = request.startAsync(request,response);
asyncContext.dispatch("/servletPath2");
continuation.suspend();
continuation.dispatch("/servletPath2");
//AsyncContext asyncContext = request.startAsync(request,response);
}
else
{
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
AsyncContext asyncContext = request.startAsync(request,response);
continuation.suspend();
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
//AsyncContext asyncContext = request.startAsync(request,response);
// Runnable runable = new AsyncRunnable(asyncContext);
// new Thread(runable).start();
asyncContext.start(new AsyncRunnable(asyncContext));
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
Runnable runable = new AsyncRunnable(continuation);
new Thread(runable).start();
//asyncContext.start(new AsyncRunnable(asyncContext));
}
return;
@ -198,13 +203,17 @@ public class AsyncContextTest
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
AsyncContinuation continuation = (AsyncContinuation) ContinuationSupport.getContinuation(request);
response.getOutputStream().print("doGet:getServletPath:" + request.getServletPath() + "\n");
AsyncContext asyncContext = request.startAsync(request, response);
continuation.suspend();
//AsyncContext asyncContext = request.startAsync(request, response);
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)asyncContext.getRequest()).getServletPath() + "\n");
asyncContext.start(new AsyncRunnable(asyncContext));
response.getOutputStream().print("doGet:async:getServletPath:" + ((HttpServletRequest)continuation.getRequest()).getServletPath() + "\n");
Runnable runable = new AsyncRunnable(continuation);
new Thread(runable).start();
//asyncContext.start(new AsyncRunnable(asyncContext));
return;
}
@ -212,33 +221,32 @@ public class AsyncContextTest
private class AsyncRunnable implements Runnable
{
private AsyncContext _context;
private AsyncContinuation _continuation;
public AsyncRunnable(AsyncContext context)
public AsyncRunnable(AsyncContinuation continuation)
{
_context = context;
_continuation = continuation;
}
@Override
public void run()
{
HttpServletRequest req = (HttpServletRequest)_context.getRequest();
HttpServletRequest req = (HttpServletRequest)_continuation.getRequest();
try
{
_context.getResponse().getOutputStream().print("async:run:" + req.getServletPath() + "\n");
_context.getResponse().getOutputStream().print("async:run:attr:servletPath:" + req.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) + "\n");
_context.getResponse().getOutputStream().print("async:run:attr:pathInfo:" + req.getAttribute(AsyncContext.ASYNC_PATH_INFO) + "\n");
_context.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
_context.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
_context.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:" + req.getServletPath() + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:servletPath:" + req.getAttribute(AsyncContext.ASYNC_SERVLET_PATH) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:pathInfo:" + req.getAttribute(AsyncContext.ASYNC_PATH_INFO) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:queryString:" + req.getAttribute(AsyncContext.ASYNC_QUERY_STRING) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:contextPath:" + req.getAttribute(AsyncContext.ASYNC_CONTEXT_PATH) + "\n");
_continuation.getResponse().getOutputStream().print("async:run:attr:requestURI:" + req.getAttribute(AsyncContext.ASYNC_REQUEST_URI) + "\n");
}
catch (IOException e)
{
e.printStackTrace();
}
_context.complete();
_continuation.complete();
}
}

View File

@ -0,0 +1,199 @@
package org.eclipse.jetty.servlets;
import static org.hamcrest.Matchers.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.servlets.gzip.Hex;
import org.eclipse.jetty.servlets.gzip.NoOpOutputStream;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestingDir;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
/**
* Test the effects of Gzip filtering when in the context of HTTP/1.1 Pipelining.
*/
public class GzipWithPipeliningTest
{
@Rule
public TestingDir testingdir = new TestingDir();
private Server server;
private URI serverUri;
@Before
public void startServer() throws Exception
{
// Configure Server
server = new Server(0);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
DefaultServlet servlet = new DefaultServlet();
ServletHolder holder = new ServletHolder(servlet);
holder.setInitParameter("resourceBase",MavenTestingUtils.getTestResourcesDir().getAbsolutePath());
context.addServlet(holder,"/");
FilterHolder filter = context.addFilter(GzipFilter.class,"/*",0);
filter.setInitParameter("mimeTypes","text/plain");
server.setHandler(context);
// Start Server
server.start();
Connector conn = server.getConnectors()[0];
String host = conn.getHost();
if (host == null)
{
host = "localhost";
}
int port = conn.getLocalPort();
serverUri = new URI(String.format("ws://%s:%d/",host,port));
// System.out.printf("Server URI: %s%n",serverUri);
}
@After
public void stopServer() throws Exception
{
server.stop();
}
@Test
public void testGzipThenImagePipelining() throws Exception
{
testingdir.ensureEmpty();
File outputDir = testingdir.getDir();
PipelineHelper client = new PipelineHelper(serverUri);
try
{
File txtFile = MavenTestingUtils.getTestResourceFile("lots-of-fantasy-names.txt");
File pngFile = MavenTestingUtils.getTestResourceFile("jetty_logo.png");
// Size of content, as it exists on disk, without gzip compression.
long rawsize = txtFile.length() + pngFile.length();
Assert.assertThat("Ensure that we have sufficient file size to trigger chunking",rawsize,greaterThan(300000L));
String respHeader;
client.connect();
// Request text that will be gzipped + chunked in the response
client.issueGET("/lots-of-fantasy-names.txt",true);
respHeader = client.readResponseHeader();
System.out.println("Response Header #1 --\n" + respHeader);
Assert.assertThat("Content-Encoding should be gzipped",respHeader,containsString("Content-Encoding: gzip\r\n"));
Assert.assertThat("Transfer-Encoding should be chunked",respHeader,containsString("Transfer-Encoding: chunked\r\n"));
// Raw output / gzipped, writted to disk (checked for sha1sum later)
File rawOutputFile = new File(outputDir, "response-1.gz");
FileOutputStream rawOutputStream = new FileOutputStream(rawOutputFile);
long chunkSize = client.readChunkSize();
System.out.println("Chunk Size: " + chunkSize);
// Read only 20% - intentionally a partial read.
System.out.println("Attempting to read partial content ...");
int readBytes = client.readBody(rawOutputStream,(int)((float)chunkSize * 0.20f));
System.out.printf("Read %,d bytes%n",readBytes);
// Issue another request
client.issueGET("/jetty_logo.png",true);
// Finish reading chunks
System.out.println("Finish reading remaining chunks ...");
String line;
chunkSize = chunkSize - readBytes;
while (chunkSize > 0)
{
readBytes = client.readBody(rawOutputStream,(int)chunkSize);
System.out.printf("Read %,d bytes%n",readBytes);
line = client.readLine();
Assert.assertThat("Chunk delim should be an empty line with CR+LF",line,is(""));
chunkSize = client.readChunkSize();
System.out.printf("Next Chunk: (0x%X) %,d bytes%n",chunkSize,chunkSize);
}
// Inter-pipeline delim
line = client.readLine();
Assert.assertThat("Inter-pipeline delim should be an empty line with CR+LF",line,is(""));
// Sha1tracking for 1st Request
MessageDigest digestTxt = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterTxt = new DigestOutputStream(new NoOpOutputStream(),digestTxt);
// Decompress 1st request and calculate sha1sum
IO.close(rawOutputStream);
FileInputStream rawInputStream = new FileInputStream(rawOutputFile);
GZIPInputStream ungzipStream = new GZIPInputStream(rawInputStream);
IO.copy(ungzipStream, digesterTxt);
// Read 2nd request http response header
respHeader = client.readResponseHeader();
System.out.println("Response Header #2 --\n" + respHeader);
Assert.assertThat("Content-Encoding should NOT be gzipped",respHeader,not(containsString("Content-Encoding: gzip\r\n")));
Assert.assertThat("Transfer-Encoding should NOT be chunked",respHeader,not(containsString("Transfer-Encoding: chunked\r\n")));
// Sha1tracking for 2nd Request
MessageDigest digestImg = MessageDigest.getInstance("SHA1");
DigestOutputStream digesterImg = new DigestOutputStream(new NoOpOutputStream(),digestImg);
// Read 2nd request body
int contentLength = client.getContentLength(respHeader);
Assert.assertThat("Image Content Length",(long)contentLength,is(pngFile.length()));
client.readBody(digesterImg,contentLength);
// Validate checksums
IO.close(rawOutputStream);
assertChecksum("lots-of-fantasy-names.txt",digestTxt);
IO.close(digesterImg);
assertChecksum("jetty_logo.png",digestImg);
}
finally
{
client.disconnect();
}
}
private void assertChecksum(String testResourceFile, MessageDigest digest) throws IOException
{
String expectedSha1 = loadSha1sum(testResourceFile + ".sha1");
String actualSha1 = Hex.asHex(digest.digest());
Assert.assertEquals(testResourceFile + " / SHA1Sum of content",expectedSha1,actualSha1);
}
private String loadSha1sum(String testResourceSha1Sum) throws IOException
{
File sha1File = MavenTestingUtils.getTestResourceFile(testResourceSha1Sum);
String contents = IO.readToString(sha1File);
Pattern pat = Pattern.compile("^[0-9A-Fa-f]*");
Matcher mat = pat.matcher(contents);
Assert.assertTrue("Should have found HEX code in SHA1 file: " + sha1File,mat.find());
return mat.group();
}
}

View File

@ -0,0 +1,269 @@
package org.eclipse.jetty.servlets;
import static org.hamcrest.Matchers.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Assert;
public class PipelineHelper
{
private static final Logger LOG = Log.getLogger(PipelineHelper.class);
private URI uri;
private SocketAddress endpoint;
private Socket socket;
private OutputStream outputStream;
private InputStream inputStream;
public PipelineHelper(URI uri)
{
if (LOG instanceof StdErrLog)
{
((StdErrLog)LOG).setLevel(StdErrLog.LEVEL_DEBUG);
}
this.uri = uri;
this.endpoint = new InetSocketAddress(uri.getHost(),uri.getPort());
}
/**
* Open the Socket to the destination endpoint and
*
* @return the open java Socket.
* @throws IOException
*/
public Socket connect() throws IOException
{
LOG.info("Connecting to endpoint: " + endpoint);
socket = new Socket();
socket.setTcpNoDelay(true);
socket.connect(endpoint,1000);
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
return socket;
}
/**
* Issue a HTTP/1.1 GET request with Connection:keep-alive set.
*
* @param path
* the path to GET
* @param acceptGzipped
* to turn on acceptance of GZIP compressed responses
* @throws IOException
*/
public void issueGET(String path, boolean acceptGzipped) throws IOException
{
LOG.debug("Issuing GET on " + path);
StringBuilder req = new StringBuilder();
req.append("GET ").append(uri.resolve(path).getPath()).append(" HTTP/1.1\r\n");
req.append("Host: ").append(uri.getHost()).append(":").append(uri.getPort()).append("\r\n");
req.append("User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3\r\n");
req.append("Accept: */*\r\n");
req.append("Referer: http://mycompany.com/index.html\r\n");
req.append("Accept-Language: en-us\r\n");
if (acceptGzipped)
{
req.append("Accept-Encoding: gzip, deflate\r\n");
}
req.append("Cookie: JSESSIONID=spqx8v8szylt1336t96vc6mw0\r\n");
req.append("Connection: keep-alive\r\n");
req.append("\r\n");
LOG.debug("Request:" + req);
// Send HTTP GET Request
byte buf[] = req.toString().getBytes();
outputStream.write(buf,0,buf.length);
outputStream.flush();
}
public String readResponseHeader() throws IOException
{
// Read Response Header
socket.setSoTimeout(10000);
LOG.debug("Reading http header");
StringBuilder response = new StringBuilder();
boolean foundEnd = false;
String line;
while (!foundEnd)
{
line = readLine();
// System.out.printf("RESP: \"%s\"%n",line);
if (line.length() == 0)
{
foundEnd = true;
LOG.debug("Got full http response header");
}
else
{
response.append(line).append("\r\n");
}
}
return response.toString();
}
public String readLine() throws IOException
{
StringBuilder line = new StringBuilder();
boolean foundCR = false;
boolean foundLF = false;
int b;
while (!(foundCR && foundLF))
{
b = inputStream.read();
Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1));
if (b == 0x0D)
{
foundCR = true;
}
else if (b == 0x0A)
{
foundLF = true;
}
else
{
foundCR = false;
foundLF = false;
line.append((char)b);
}
}
return line.toString();
}
public long readChunkSize() throws IOException
{
StringBuilder chunkSize = new StringBuilder();
String validHex = "0123456789ABCDEF";
boolean foundCR = false;
boolean foundLF = false;
int b;
while (!(foundCR && foundLF))
{
b = inputStream.read();
Assert.assertThat("Should not have hit EOL (yet) during chunk size read",(int)b,not(-1));
if (b == 0x0D)
{
foundCR = true;
}
else if (b == 0x0A)
{
foundLF = true;
}
else
{
foundCR = false;
foundLF = false;
// Must be valid char
char c = (char)b;
if (validHex.indexOf(c) >= 0)
{
chunkSize.append(c);
}
else
{
Assert.fail(String.format("Encountered invalid chunk size byte 0x%X",b));
}
}
}
return Long.parseLong(chunkSize.toString(),16);
}
public int readBody(OutputStream stream, int size) throws IOException
{
int left = size;
while (left > 0)
{
int val = inputStream.read();
if (val == (-1))
{
Assert.fail(String.format("Encountered an early EOL (expected another %,d bytes)",left));
}
stream.write(val);
left--;
}
return size - left;
}
public byte[] readResponseBody(int size) throws IOException
{
byte partial[] = new byte[size];
int readBytes = 0;
int bytesLeft = size;
while (readBytes < size)
{
int len = inputStream.read(partial,readBytes,bytesLeft);
Assert.assertThat("Read should not have hit EOL yet",len,not(-1));
System.out.printf("Read %,d bytes%n",len);
if (len > 0)
{
readBytes += len;
bytesLeft -= len;
}
}
return partial;
}
public OutputStream getOutputStream()
{
return outputStream;
}
public InputStream getInputStream()
{
return inputStream;
}
public SocketAddress getEndpoint()
{
return endpoint;
}
public Socket getSocket()
{
return socket;
}
public void disconnect() throws IOException
{
LOG.debug("disconnect");
socket.close();
}
public int getContentLength(String respHeader)
{
Pattern pat = Pattern.compile("Content-Length: ([0-9]*)",Pattern.CASE_INSENSITIVE);
Matcher mat = pat.matcher(respHeader);
if (mat.find())
{
try
{
return Integer.parseInt(mat.group(1));
}
catch (NumberFormatException e)
{
return -1;
}
}
else
{
// Undefined content length
return -1;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
b49b039adf40b695217e6e369513767a7c1e7dc6 lots-of-fantasy-names.txt