我有一堂课:
MockServerHandler
我在使用 Jetty 9 的 itests 中使用它,我必须迁移到 Jetty 12。
迁移之前(Jetty 9):
public class MockServerHandler extends DefaultHandler {
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
try {
log.debug("Got request for: {} request type {} ", target, request.getMethod());
handleDownload(request, response);
} catch (Exception e) {
log.error("Mock handler caught and exception", e);
}
}
private void handleDownload(HttpServletRequest request, HttpServletResponse response) {
byte[] byteBuffer = getContentToReturn();
long lastModified = getLastModified();
String contentType = getContentType();
int returnCode = getReturnCode();
response.setStatus(returnCode);
if (lastModified > 0L) {
response.setDateHeader(HttpHeader.LAST_MODIFIED.asString(), lastModified);
}
response.setContentType(contentType);
response.setContentLength(byteBuffer.length);
OutputStream outputStream = response.getOutputStream();
outputStream.write(byteBuffer, 0, byteBuffer.length);
response.flushBuffer();
outputStream.close();
}
}
到达 12 号码头后:
public class MockServerHandler extends DefaultHandler {
@Override
public boolean handle(Request request, Response response, Callback callback)
throws IOException, ServletException {
try {
String target = request.getHttpURI().getPath();
log.debug("Got request for: {} request type {} ", target, request.getMethod());
handleDownload(request, response);
callback.succeeded();
} catch (Exception e) {
callback.failed(e);
response.setStatus(HttpStatus.SC_INTERNAL_SERVER_ERROR);
log.error("Mock handler caught and exception", e);
}
return true;
}
private void handleDownload(Request request, Response response) {
byte[] byteBuffer = getContentToReturn();
long lastModified = getLastModified();
String contentType = getContentType();
int returnCode = getReturnCode();
response.setStatus(returnCode);
if (lastModified > 0L) {
response.getHeaders().addDateField(HttpHeader.LAST_MODIFIED.asString(), lastModified);
}
response.getHeaders().add(HttpHeader.CONTENT_TYPE, contentType);
OutputStream outputStream = Content.Sink.asOutputStream(response);
outputStream.write(byteBuffer, 0, byteBuffer.length);
outputStream.flush();
outputStream.close();
}
}
在这些更改之后,我的集成测试开始失败。一些是由于读取超时,另一些则期望 UTF-8 编码的响应正文,但突然失败,抱怨第一个字节不合规,等等。 我显然错过了一些东西,因为我试图做出尽可能少的改变来保留行为。
我注意到
DefaultHandler
传递了 super(InvocationType.NON_BLOCKING);
,这是我没想到的(在 Jetty 9 上它不是非阻塞的,对吧?)。我尝试直接扩展 Handler.Abstract
但这并没有改变任何东西。
我是否违反了新的 Jetty 合同?我仔细阅读了文档,但没有发现我做的任何事情是明显错误的。 非常感谢任何了解其工作原理和我所缺少的内容的建议。
如果您有完整的
byte[]
内容数组,请不要使用阻塞 OutputStream
,只需按原样发送即可,无需使 OutputStream
类变得过于复杂。
您的 Jetty 12 实现甚至无法正确处理内容长度。
分离您的担忧。
一旦你混合这些,你就会得到一组混乱的代码,变得难以维护。
package handlers;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class MockDownloadHandler extends Handler.Abstract
{
private static final Logger LOG = LoggerFactory.getLogger(MockDownloadHandler.class);
@Override
public boolean handle(Request request, Response response, Callback callback) throws Exception
{
String target = Request.getPathInContext(request);
LOG.debug("Got request for: {} request type {} ", target, request.getMethod());
// Get response content
ByteBuffer responseBody;
try
{
responseBody = ByteBuffer.wrap(getContentToReturn());
}
catch (Throwable e)
{
callback.failed(e);
return true; // you handled the failure.
}
// Prepare response metadata
long lastModified = getLastModified();
// The contentType string here should have the `;charset=` section defined too.
// Example: "text/html;charset=utf-8" or "text/html;charset=iso8859-1"
// Note: not all content-types support a `;charset=` here, some are implied.
// for example json is implied to be `utf-8` but never reported in this
// content-type as `;charset=utf-8`, that would, in fact, be a violation
// of the json spec to include the `;charset=` entry.
String contentType = getContentType();
int returnCode = getReturnCode();
response.setStatus(returnCode);
response.getHeaders().add(HttpHeader.CONTENT_TYPE, contentType);
response.getHeaders().add(HttpHeader.CONTENT_LENGTH, responseBody.remaining());
if (lastModified > 0)
response.getHeaders().addDateField(HttpHeader.LAST_MODIFIED.asString(), lastModified);
// Send the response
response.write(true, responseBody, callback);
return true;
}
protected abstract int getReturnCode();
protected abstract String getContentType();
protected abstract long getLastModified();
public byte[] getContentToReturn()
{
// Be careful of proper conversion to bytes.
// If the data is already binary, no need to do anything to encode it.
// But if the data starts out as text, the conversion to bytes needs to be encoded
// based on the same charset that you are going to report back on the `Content-Type`
// response header.
// Example: a string to byte conversion using UTF-8
return "Hello € euro".getBytes(StandardCharsets.UTF_8);
}
}
您可能希望您的
getContentToReturn()
步骤也能够识别mime类型和字符集,以便您可以对内容转换为字节执行正确的行为,并且也可以继承正确的内容类型(带有字符集)。
还要注意
MimeTypes
对象,它可以为你做一些事情。
就像让您了解假定的字符集与推断的字符集一样。
示例。
package mimetypes;
import java.nio.charset.Charset;
import org.eclipse.jetty.http.MimeTypes;
public class MimeTypeDemo
{
public static void main(String[] args)
{
MimeTypes mimeTypes = new MimeTypes();
dump(mimeTypes, "text/html");
dump(mimeTypes, "text/html;charset=iso8859-1");
dump(mimeTypes, "image/jpeg");
dump(mimeTypes, "application/json");
dump(mimeTypes, "application/json;charset=UTF-8");
dump(mimeTypes, "application/json;charset=us-ascii");
}
public static void dump(MimeTypes mimeTypes, String applicationSpecifiedContentType)
{
System.out.printf("%n-- dump: \"%s\" --%n", applicationSpecifiedContentType);
System.out.printf(" mime-type = %s%n", MimeTypes.getContentTypeWithoutCharset(applicationSpecifiedContentType));
System.out.printf(" charset (string) = %s%n", MimeTypes.getCharsetFromContentType(applicationSpecifiedContentType));
System.out.printf(" charset (class) = %s%n", mimeTypes.getCharset(applicationSpecifiedContentType));
System.out.printf(" assumed charset = %s%n", mimeTypes.getAssumedCharset(applicationSpecifiedContentType));
System.out.printf(" inferred charset = %s%n", mimeTypes.getInferredCharset(applicationSpecifiedContentType));
System.out.printf(" charset inferred from content-type = %s%n", mimeTypes.getCharsetInferredFromContentType(applicationSpecifiedContentType));
String contentTypeHeaderValue = toHeaderValue(mimeTypes, applicationSpecifiedContentType);
System.out.printf(" Content-Type (clean header to send) = %s%n", contentTypeHeaderValue);
}
private static String toHeaderValue(MimeTypes mimeTypes, String rawContentType)
{
// Get the mimetype from the raw content-type string
String mimeType = MimeTypes.getContentTypeWithoutCharset(rawContentType);
// Figure out the charset, normalized if found, add if missing and not assumed.
String rawCharset = MimeTypes.getCharsetFromContentType(rawContentType);
if (rawCharset == null)
{
Charset inferredCharset = mimeTypes.getInferredCharset(mimeType);
if (inferredCharset != null)
rawCharset = inferredCharset.name();
}
if (rawCharset != null)
return String.format("%s;charset=%s", mimeType, rawCharset);
else
return mimeType;
}
}
输出...
-- dump: "text/html" --
mime-type = text/html
charset (string) = null
charset (class) = UTF-8
assumed charset = null
inferred charset = UTF-8
charset inferred from content-type = UTF-8
Content-Type (clean header to send) = text/html;charset=UTF-8
-- dump: "text/html;charset=iso8859-1" --
mime-type = text/html
charset (string) = iso8859-1
charset (class) = ISO-8859-1
assumed charset = null
inferred charset = null
charset inferred from content-type = null
Content-Type (clean header to send) = text/html;charset=iso8859-1
-- dump: "image/jpeg" --
mime-type = image/jpeg
charset (string) = null
charset (class) = null
assumed charset = null
inferred charset = null
charset inferred from content-type = null
Content-Type (clean header to send) = image/jpeg
-- dump: "application/json" --
mime-type = application/json
charset (string) = null
charset (class) = UTF-8
assumed charset = UTF-8
inferred charset = null
charset inferred from content-type = null
Content-Type (clean header to send) = application/json
-- dump: "application/json;charset=UTF-8" --
mime-type = application/json
charset (string) = utf-8
charset (class) = UTF-8
assumed charset = null
inferred charset = null
charset inferred from content-type = null
Content-Type (clean header to send) = application/json;charset=utf-8
-- dump: "application/json;charset=us-ascii" --
mime-type = application/json
charset (string) = us-ascii
charset (class) = US-ASCII
assumed charset = null
inferred charset = null
charset inferred from content-type = null
Content-Type (clean header to send) = application/json;charset=us-ascii
这表明现在可以询问应用程序提供的 Content-Type(带有可选字符集)并将其解析为适合在 HTTP 协议上使用的字符串,作为
Content-Type
标头值。