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
相关产品推荐
相关产品推荐

