Merge from master
This commit is contained in:
commit
eed2a2a0f7
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
@ -0,0 +1 @@
|
|||
b49b039adf40b695217e6e369513767a7c1e7dc6 lots-of-fantasy-names.txt
|
Loading…
Reference in New Issue