You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetty HTTP/2明文服务器无响应问题排查求助

问题:Jetty h2c服务器无法响应HTTP/2请求(HTTP/1.1正常)

我基于Jetty实现了一个明文HTTP/2(h2c)服务器,处理HTTP/1.1请求时工作正常,但无法响应HTTP/2请求,怀疑是否与请求处理器有关?服务器部署在localhost:8080,以下是相关代码及报错日志:

服务器代码

//server
package server;

import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.ContextHandler;


public class Main {

    public static void main(String[] args) throws Exception {
        Server server = new Server();

        HttpConfiguration httpConfig = new HttpConfiguration();

        HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);

        HTTP2CServerConnectionFactory h2c = new HTTP2CServerConnectionFactory(httpConfig);

        ServerConnector http = new ServerConnector(server,http11, h2c);
        http.setPort(8080);

        // Set the connector
        server.addConnector(http);

        // set servlet handler
        ContextHandler context = new ContextHandler();
        context.setContextPath("/");
        context.setHandler(new RequestHandler());
        server.setHandler(context);

        server.start();
    }
}

请求处理器代码

// request handler

package server;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Objects;

public class RequestHandler extends AbstractHandler {
    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
//        baseRequest.setHandled(true);
        System.out.println(baseRequest.getRequestURI());
        String method = baseRequest.getMethod();
        if(Objects.equals(method, "POST")) {
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }

            System.out.println("REQUEST BODY IS : " + requestBody.toString());
            response.setContentType("application/json");

            response.setStatus(202);
            response.getWriter().println("POST REQUEST");

        }
        else {
            System.out.println("GET REQUEST ");
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
            response.setStatus(200);
            System.out.println("Sending response");
            response.getWriter().println("{\"http-version\" : 2}");
            System.out.println("Sending response >> ");

        }
        baseRequest.setHandled(true);
    }
}

客户端报错日志

> System.Net.Http.HttpRequestException: An error occurred while sending the request.
> System.IO.IOException: The request was aborted.
> System.IO.IOException: The response ended prematurely while waiting for the next frame from the server.
> at System.Net.Http.Http2Connection.<ReadFrameAsync>g__ThrowMissingFrame|69_1()
> at System.Net.Http.Http2Connection.ReadFrameAsync(Boolean initialFrame)
> at System.Net.Http.Http2Connection.ProcessIncomingFramesAsync()
> End of inner exception stack trace ---
> at System.Net.Http.Http2Connection.ThrowRequestAborted(Exception innerException)
> at System.Net.Http.Http2Connection.Http2Stream.CheckResponseBodyState()
> at System.Net.Http.Http2Connection.Http2Stream.TryEnsureHeaders()
> at System.Net.Http.Http2Connection.Http2Stream.ReadResponseHeadersAsync(CancellationToken cancellationToken)
> at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
> End of inner exception stack trace ---
> at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
> at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
> at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
> at System.Net.Http.DecompressionHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
> at System.Net.Http.HttpClient.<SendAsync>g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
> at Pororoca.Infrastructure.Features.Requester.PororocaRequester.RequestAsync(IPororocaVariableResolver variableResolver, PororocaHttpRequest req, Boolean disableSslVerification, CancellationToken cancellationToken) in D:\a\Pororoca\Pororoca\src\Pororoca.Infrastructure\Features\Requester\PororocaRequester.cs:line 51

问题分析与解决方案

问题根源

HTTP/2基于帧协议,对响应的完整性要求远高于HTTP/1.1。你的代码中仅调用response.getWriter().println(...)写入响应内容,但未显式刷新或关闭输出流。HTTP/1.1场景下Jetty会自动处理流的关闭,但HTTP/2场景下,未完整发送响应帧会导致客户端判定响应提前终止,抛出The response ended prematurely错误。

修复方案

在写入响应内容后,必须显式刷新并关闭输出流,确保HTTP/2响应帧完整发送。修改后的请求处理器代码如下:

package server;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.Objects;

public class RequestHandler extends AbstractHandler {
    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException {
        System.out.println(baseRequest.getRequestURI());
        String method = baseRequest.getMethod();
        
        // 统一设置响应头,避免重复代码
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        
        if(Objects.equals(method, "POST")) {
            BufferedReader reader = request.getReader();
            StringBuilder requestBody = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }

            System.out.println("REQUEST BODY IS : " + requestBody.toString());
            response.setStatus(202);
            response.getWriter().println("POST REQUEST");
        }
        else {
            System.out.println("GET REQUEST ");
            response.setStatus(200);
            System.out.println("Sending response");
            response.getWriter().println("{\"http-version\" : 2}");
            System.out.println("Sending response >> ");
        }
        
        // 关键操作:刷新输出流并关闭,确保HTTP/2响应帧完整发送
        response.getWriter().flush();
        response.getWriter().close();
        baseRequest.setHandled(true);
    }
}

额外检查点

  • 确认Jetty依赖版本:确保jetty-http2-server包版本与其他Jetty核心包版本一致,避免版本兼容问题
  • 验证HTTP/2请求方式:h2c请求需通过Upgrade头或直接ALPN协商发起,确保客户端请求符合h2c规范

内容的提问来源于stack exchange,提问作者Naveen Punia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:17:28