Jetty 服务器 9 到 12 迁移

问题描述 投票:0回答:1

我有一堂课:

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 合同?我仔细阅读了文档,但没有发现我做的任何事情是明显错误的。 非常感谢任何了解其工作原理和我所缺少的内容的建议。

jetty embedded-jetty jetty-9 jetty-12
1个回答
0
投票

如果您有完整的

byte[]
内容数组,请不要使用阻塞
OutputStream
,只需按原样发送即可,无需使
OutputStream
类变得过于复杂。

您的 Jetty 12 实现甚至无法正确处理内容长度。

分离您的担忧。

  1. 获取响应数据(失败
  2. 准备响应元数据(标头、状态代码)
  3. 编写响应正文内容(并管理回调)

一旦你混合这些,你就会得到一组混乱的代码,变得难以维护。

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
标头值。

© www.soinside.com 2019 - 2024. All rights reserved.