Jetty 12.0.x 10716 charset printwriter (#10737)
* Issue #10716 tck compliance for setting response charset --------- Co-authored-by: gregw <gregw@webtide.com>
This commit is contained in:
parent
524bde565e
commit
467f026d37
|
@ -1602,6 +1602,11 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
|
|||
return true;
|
||||
}
|
||||
|
||||
public HttpField onReplaceField(HttpField oldField, HttpField newField)
|
||||
{
|
||||
return newField;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
|
@ -1623,11 +1628,47 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
|
|||
{
|
||||
field = onAddField(field);
|
||||
if (field != null)
|
||||
return _fields.add(field);
|
||||
_fields.add(field);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable put(HttpField field)
|
||||
{
|
||||
// rewrite put to ensure that removes are called before replace
|
||||
int put = -1;
|
||||
ListIterator<HttpField> i = _fields.listIterator();
|
||||
while (i.hasNext())
|
||||
{
|
||||
HttpField f = i.next();
|
||||
if (f.isSameName(field))
|
||||
{
|
||||
if (put < 0)
|
||||
put = i.previousIndex();
|
||||
else if (onRemoveField(f))
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
|
||||
if (put < 0)
|
||||
{
|
||||
field = onAddField(field);
|
||||
if (field != null)
|
||||
add(field);
|
||||
}
|
||||
else
|
||||
{
|
||||
i = _fields.listIterator(put);
|
||||
HttpField old = i.next();
|
||||
field = onReplaceField(old, field);
|
||||
if (field != null)
|
||||
i.set(field);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mutable clear()
|
||||
{
|
||||
|
@ -1702,9 +1743,9 @@ public interface HttpFields extends Iterable<HttpField>, Supplier<HttpFields>
|
|||
}
|
||||
else
|
||||
{
|
||||
if (last != null && onRemoveField(last))
|
||||
if (last != null)
|
||||
{
|
||||
field = onAddField(field);
|
||||
field = onReplaceField(last, field);
|
||||
if (field != null)
|
||||
{
|
||||
last = null;
|
||||
|
|
|
@ -540,60 +540,67 @@ public class ServletContextResponse extends ContextResponse implements ServletCo
|
|||
@Override
|
||||
public HttpField onAddField(HttpField field)
|
||||
{
|
||||
if (field.getHeader() != null)
|
||||
{
|
||||
switch (field.getHeader())
|
||||
{
|
||||
case CONTENT_LENGTH ->
|
||||
{
|
||||
if (!isCommitted())
|
||||
{
|
||||
return setContentLength(field);
|
||||
}
|
||||
}
|
||||
case CONTENT_TYPE ->
|
||||
{
|
||||
if (!isCommitted())
|
||||
{
|
||||
return setContentType(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isCommitted())
|
||||
return null;
|
||||
|
||||
return super.onAddField(field);
|
||||
if (field.getHeader() == null)
|
||||
return super.onAddField(field);
|
||||
|
||||
return switch (field.getHeader())
|
||||
{
|
||||
case CONTENT_LENGTH -> setContentLength(field);
|
||||
case CONTENT_TYPE -> setContentType(field);
|
||||
default -> super.onAddField(field);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRemoveField(HttpField field)
|
||||
{
|
||||
if (field.getHeader() != null)
|
||||
if (isCommitted())
|
||||
return false;
|
||||
if (field.getHeader() == null)
|
||||
return true;
|
||||
switch (field.getHeader())
|
||||
{
|
||||
switch (field.getHeader())
|
||||
case CONTENT_LENGTH -> _contentLength = -1;
|
||||
case CONTENT_TYPE ->
|
||||
{
|
||||
case CONTENT_LENGTH ->
|
||||
_contentType = null;
|
||||
_mimeType = null;
|
||||
if (!isWriting())
|
||||
{
|
||||
if (!isCommitted())
|
||||
_contentLength = -1;
|
||||
}
|
||||
case CONTENT_TYPE ->
|
||||
{
|
||||
if (!isCommitted())
|
||||
_characterEncoding = switch (_encodingFrom)
|
||||
{
|
||||
_contentType = null;
|
||||
_mimeType = null;
|
||||
_characterEncoding = switch (_encodingFrom)
|
||||
{
|
||||
case SET_CHARACTER_ENCODING, SET_LOCALE -> _characterEncoding;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
case SET_CHARACTER_ENCODING, SET_LOCALE -> _characterEncoding;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpField onReplaceField(HttpField oldField, HttpField newField)
|
||||
{
|
||||
assert oldField != null && newField != null;
|
||||
|
||||
if (isCommitted())
|
||||
return null;
|
||||
|
||||
if (newField.getHeader() == null)
|
||||
return newField;
|
||||
|
||||
return switch (newField.getHeader())
|
||||
{
|
||||
case CONTENT_LENGTH -> setContentLength(newField);
|
||||
case CONTENT_TYPE -> setContentType(newField);
|
||||
default -> newField;
|
||||
};
|
||||
}
|
||||
|
||||
private HttpField setContentLength(HttpField field)
|
||||
{
|
||||
long len = field.getLongValue();
|
||||
|
|
|
@ -34,9 +34,11 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
|
||||
public class ResponseHeadersTest
|
||||
|
@ -444,6 +446,44 @@ public class ResponseHeadersTest
|
|||
assertThat("Response Header X-Foo", response.getField("X-Foo").getIntValue(), is(foovalue));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetIntHeader() throws Exception
|
||||
{
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
HttpServlet addHeaderServlet = new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.addIntHeader("X-Foo", 10);
|
||||
response.setIntHeader("X-Foo", 20);
|
||||
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.println("Done");
|
||||
}
|
||||
};
|
||||
|
||||
contextHandler.addServlet(addHeaderServlet, "/add-intheader/*");
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Request request = new HttpTester.Request();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/add-intheader/");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setHeader("Connection", "close");
|
||||
request.setHeader("Host", "test");
|
||||
|
||||
ByteBuffer responseBuffer = connector.getResponse(request.generate());
|
||||
// System.err.println(BufferUtil.toUTF8String(responseBuffer));
|
||||
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
|
||||
|
||||
// Now test for properly formatted HTTP Response Headers.
|
||||
assertThat("Response Code", response.getStatus(), is(200));
|
||||
// The X-Foo header should be present an unchanged
|
||||
assertThat("Response Header X-Foo", response.getField("X-Foo").getIntValue(), is(20));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFlushPrintWriter() throws Exception
|
||||
{
|
||||
|
@ -480,4 +520,161 @@ public class ResponseHeadersTest
|
|||
assertThat("Response Code", response.getStatus(), is(200));
|
||||
assertThat(response.getContent(), equalTo("Hello\nWorld\n"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentTypeAfterWriterBeforeWrite() throws Exception
|
||||
{
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
HttpServlet contentTypeServlet = new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.setContentType("text/xml;charset=ISO-8859-7");
|
||||
PrintWriter pw = response.getWriter();
|
||||
response.setContentType("text/html;charset=UTF-8");
|
||||
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.println("Hello");
|
||||
}
|
||||
};
|
||||
|
||||
contextHandler.addServlet(contentTypeServlet, "/content/*");
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Request request = new HttpTester.Request();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/content");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setHeader("Connection", "close");
|
||||
request.setHeader("Host", "test");
|
||||
|
||||
ByteBuffer responseBuffer = connector.getResponse(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
|
||||
|
||||
assertThat("Response Code", response.getStatus(), is(200));
|
||||
assertThat("Content Type", response.getField("Content-Type").getValue(), containsString("text/html;charset=ISO-8859-7"));
|
||||
assertThat(response.getContent(), containsString("Hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentTypeNoCharset() throws Exception
|
||||
{
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
HttpServlet contentTypeServlet = new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.setContentType("text/html;charset=Shift_Jis");
|
||||
response.setContentType("text/xml");
|
||||
|
||||
PrintWriter pw = response.getWriter();
|
||||
pw.println("Hello");
|
||||
}
|
||||
};
|
||||
|
||||
contextHandler.addServlet(contentTypeServlet, "/content/*");
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Request request = new HttpTester.Request();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/content");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setHeader("Connection", "close");
|
||||
request.setHeader("Host", "test");
|
||||
|
||||
ByteBuffer responseBuffer = connector.getResponse(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
|
||||
|
||||
assertThat("Response Code", response.getStatus(), is(200));
|
||||
assertThat("Content Type", response.getField("Content-Type").getValue(), containsString("text/xml;charset=Shift_Jis"));
|
||||
assertThat(response.getContent(), containsString("Hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContentTypeNull() throws Exception
|
||||
{
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
HttpServlet contentTypeServlet = new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.setContentType("text/html;charset=Shift_Jis");
|
||||
response.setContentType(null);
|
||||
|
||||
PrintWriter pw = response.getWriter();
|
||||
assertThat(response.getCharacterEncoding(), not(is("Shift_Jis")));
|
||||
pw.println("Hello");
|
||||
}
|
||||
};
|
||||
|
||||
contextHandler.addServlet(contentTypeServlet, "/content/*");
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Request request = new HttpTester.Request();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/content");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setHeader("Connection", "close");
|
||||
request.setHeader("Host", "test");
|
||||
|
||||
ByteBuffer responseBuffer = connector.getResponse(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
|
||||
|
||||
assertThat("Response Code", response.getStatus(), is(200));
|
||||
assertThat("Content Type", response.getField("Content-Type"), nullValue());
|
||||
assertThat(response.getContent(), containsString("Hello"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommittedNoop() throws Exception
|
||||
{
|
||||
ServletContextHandler contextHandler = new ServletContextHandler();
|
||||
contextHandler.setContextPath("/");
|
||||
HttpServlet addHeaderServlet = new HttpServlet()
|
||||
{
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
response.setHeader("Test", "Before");
|
||||
response.setHeader("Content-Length", "2");
|
||||
response.setHeader("Content-Type", "text/html");
|
||||
|
||||
response.getOutputStream().print("OK");
|
||||
response.flushBuffer();
|
||||
|
||||
// These should be silently ignored
|
||||
response.setHeader("Test", "After");
|
||||
response.setHeader("Content-Length", "10");
|
||||
response.setHeader("Content-Type", "text/xml");
|
||||
|
||||
assertThat(response.getHeader("Test"), is("Before"));
|
||||
assertThat(response.getContentType(), is("text/html"));
|
||||
assertThat(response.getHeader("Content-Length"), is("2"));
|
||||
}
|
||||
};
|
||||
|
||||
contextHandler.addServlet(addHeaderServlet, "/test/*");
|
||||
startServer(contextHandler);
|
||||
|
||||
HttpTester.Request request = new HttpTester.Request();
|
||||
request.setMethod("GET");
|
||||
request.setURI("/test/");
|
||||
request.setVersion(HttpVersion.HTTP_1_1);
|
||||
request.setHeader("Connection", "close");
|
||||
request.setHeader("Host", "test");
|
||||
|
||||
ByteBuffer responseBuffer = connector.getResponse(request.generate());
|
||||
HttpTester.Response response = HttpTester.parseResponse(responseBuffer);
|
||||
|
||||
assertThat(response.getStatus(), is(200));
|
||||
assertThat(response.getField("Test").getValue(), is("Before"));
|
||||
assertThat(response.getField("Content-Type").getValue(), is("text/html"));
|
||||
assertThat(response.getContent(), is("OK"));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue