当服务器仅从请求中读取标头时,Http客户端不会收到响应

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了当服务器仅从请求中读取标头时,Http客户端不会收到响应相关的知识,希望对你有一定的参考价值。

我正在乱用Java中的HTTP和套接字,希望你能对此有所了解:

当我用Java SE 11编写的HTTP服务器没有读取整个请求然后响应时,客户端没有得到它或者收到错误。这是为什么?在服务器读取整个请求之前,客户端是否无法读取响应?如果在下面的代码段中执行对readBody的调用,则可以正常工作。如果响应具有Content-Length标头和文本正文,它也可以正常工作。这实际上对我来说更令人费解。

我的示例请求是带有数据fds的POST。邮递员说“无法得到任何请求”,卷曲说“卷曲:(56)Recv失败:连接由同行重置”。

import java.io.*;
import java.net.Socket;
import java.util.*;

class Handler {

    public synchronized void read(Socket incoming) {
        try (incoming;
             OutputStream outputStream = incoming.getOutputStream();
             InputStream inputStream = incoming.getInputStream();
             PrintWriter pw = new PrintWriter(outputStream)) {

            writeRequest(inputStream);
            pw.print("HTTP/1.1 200 OK
");
            pw.print("
");
            pw.flush();
        } catch (IOException e) {
            System.out.println("ERROR: " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void writeRequest(InputStream inputStream) throws IOException {
        String verbLine = readLine(inputStream);

        Map<String, String> headers = readHeaders(inputStream);

        //readBody(inputStream, headers);
    }

    private void readBody(InputStream inputStream, Map<String, String> headers) throws IOException {
        Optional<String> optKey = headers.keySet().stream()
                .filter(k -> k.equalsIgnoreCase("Content-Length"))
                .findFirst();
        if (optKey.isPresent()) {
            int contentLength = Integer.parseInt(headers.get(optKey.get()));
            byte[] bytes = inputStream.readNBytes(contentLength);
        }
    }

    private Map<String, String> readHeaders(InputStream inputStream) throws IOException {
        Map<String, String> headers = new HashMap<>();
        while (true) {
            String line = readLine(inputStream);
            if (line == null || line.isEmpty()) {
                return headers;
            }
            String key = line.split(":")[0].trim();
            String value = line.split(":")[1].trim();
            headers.put(key, value);
        }
    }

    private String readLine(InputStream inputStream) throws IOException {
        byte[] buf = new byte[200];
        int offset = 0;
        while (true) {
            int read = inputStream.read();
            if (read == -1) {
                return null;
            }
            buf[offset] = (byte) read;
            if (buf[0] == '
' || (buf[0] == '
' && buf[1] == '
')) {
                return "";
            }
            if (buf[offset] == 0x0A) {
                int endOfLine = buf[offset - 1] == 0x0D ? offset - 1 : offset;
                return new String(buf, 0, endOfLine);
            } else {
                offset++;
            }
        }
    }
}
答案

如果在仍有未读数据的情况下关闭服务器上的套接字,则会导致客户端出现连接重置错误。这是因为您没有阅读完整的请求。如果尚未读取服务器的完整响应,则会向用户显示此错误。

如果您使用content-length发送响应然后发送完整的主体,则客户端将读取完整响应,因此将忽略错误。相反,如果您既不发送content-length也不使用chunked编码,则客户端将期望响应以正确关闭TCP连接结束。在这种情况下,连接重置将传播给用户,因为尚未(正确)读取服务器的完整响应。

另一答案

您的响应需要具有Content-Length标头或Transfer-Encoding标头 - 它告诉客户端响应将如何传输,并允许它确定何时接收到所有字节。如果没有它,它将需要等待EOF并假设响应主体由EOF终止(这是为了与HTTP / 1.0兼容)。您的客户可能不支持该功能。了解您正在使用的客户端可能会有所帮助。

以上是关于当服务器仅从请求中读取标头时,Http客户端不会收到响应的主要内容,如果未能解决你的问题,请参考以下文章

当请求 API 的自定义标头错误时,将 http 状态代码设置为 417

在 ASP.NET Core 中使用 HTTP 标头传播

如何在 Flask 中捕获和读取传入 HTTP 请求的标头? [复制]

在 ASP.NET Core 中使用 HTTP 标头传播

为BlazeDS和AMF发送的请求添加自定义HTTP标头

自定义 Authorization HTTP 标头