Http-клиент не получает ответа, когда сервер читает только заголовки из запроса - PullRequest
0 голосов
/ 22 февраля 2019

Я возиться с HTTP и сокетами в Java и надеялся, что вы могли бы пролить свет на это:

Когда мой HTTP-сервер, написанный на Java SE 11, не читает весь запрос и затем отвечает,клиент не получает его или получает ошибку.Это почему?Клиент не может прочитать ответ до того, как сервер прочитает весь запрос?Если вызов readBody выполнен в фрагменте ниже, это работает нормально.Это также хорошо работает, если ответ имеет заголовок Content-Length и текстовое тело.Это на самом деле более загадочно для меня.

Мой пример запроса - это POST с данными fds .Почтальон говорит: «Не удалось получить ни одного запроса», а curl говорит: «curl: (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\r\n");
            pw.print("\r\n");
            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] == '\n' || (buf[0] == '\r' && buf[1] == '\n')) {
                return "";
            }
            if (buf[offset] == 0x0A) {
                int endOfLine = buf[offset - 1] == 0x0D ? offset - 1 : offset;
                return new String(buf, 0, endOfLine);
            } else {
                offset++;
            }
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 22 февраля 2019

Если вы закроете сокет на сервере, пока еще есть непрочитанные данные, это приведет к ошибке сброса соединения на клиенте.Это происходит здесь, так как вы не прочитали полный запрос.Эта ошибка будет видна пользователю, если полный ответ с сервера еще не был прочитан.

Если вы отправите ответ с content-length, а затем в полном теле, тогда клиент прочитает полный ответ и, таким образом, ошибка будет проигнорирована.Если вместо этого вы не отправите ни content-length, ни используете chunked-кодировку, клиент будет ожидать, что ответ завершится с надлежащим закрытием соединения TCP.В этом случае сброс соединения будет передан пользователю, поскольку полный ответ от сервера еще не был (правильно) прочитан.

0 голосов
/ 22 февраля 2019

Ваш ответ должен иметь либо заголовок Content-Length, либо заголовок Transfer-Encoding - который сообщает клиенту, как будет передаваться ответ, и позволяет ему выяснить, когда были получены все байты.Без этого ему придется ждать EOF и предполагать, что тело ответа завершается EOF (это для совместимости с HTTP / 1.0).Вполне возможно, что ваш клиент не поддерживает это.Это может помочь узнать, каким клиентом вы пользуетесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...