Re[5]: Как в Spring Boot включить Content-Length в ответ?
От: Stanislav V. Zudin Россия  
Дата: 06.05.22 14:04
Оценка:
Здравствуйте, Pzz, Вы писали:

SVZ>>Тогда посмотри Poco.


Pzz>Товарищ просил не на C++


На Сях такое программить это форменный мазохизм.
_____________________
С уважением,
Stanislav V. Zudin
Re[6]: Как в Spring Boot включить Content-Length в ответ?
От: Pzz Россия https://github.com/alexpevzner
Дата: 06.05.22 14:09
Оценка:
Здравствуйте, Stanislav V. Zudin, Вы писали:

Pzz>>Товарищ просил не на C++


SVZ>На Сях такое программить это форменный мазохизм.


Я пробовал.

В принципе, ничего такого, чтобы это это было сложно программировать именно на Сях, там нет. Но HTTP сам по себе сложнее, чем кажется, и это надо понимать прежде, чем ввязываешься. И не ввязываться без нужды
Re[3]: Как в Spring Boot включить Content-Length в ответ?
От: GarryIV  
Дата: 06.05.22 15:58
Оценка:
Здравствуйте, Worminator X, Вы писали:

GIV>>Я нихрена не понял кому это неудобно. Это как бы внутре http клиента прозрачно делается.

WX>Так клиент я пишу сам на сокетах.
А, ясно-понятно.

GIV>>Почему это вдруг медленно тоже не особо понятно. Бенчмарки делал?

WX>Нужно собрать тело по кускам. Это очевидно медленно.
Вопросов больше не имею.
WBR, Igor Evgrafov
Re[5]: Как в Spring Boot включить Content-Length в ответ?
От: maxkar  
Дата: 06.05.22 17:38
Оценка:
Здравствуйте, Worminator X, Вы писали:

WX>Spring ведет себя странно, отправляет ответ все равно HTTP/1.1 200 OK. И без Content-Length. Как-то нужно его включать именно на сервере.


Не, не странно. Вполне логично. Ответ вполне удовлетворяет спецификации. Можно еще "Connection: Keep-Alive" в заголовках передавать, но это тот еще костыль и включает новые проблемы.

"Включить content-length на сервере" ручки нет, это тоже костыль и в стандартную поставку не входит. Spring/Jetty ведут себя правильно и согласно спецификации HTTP. Spring не вычисляет длину ответа, она ему не нужна. Он делает кучу магии для нахождения кодека, а потом делает что-то вроде encoder.encode(entity, servletResponse.getOutputStream()). Т.е. пишет ответ сразу в OutputStream, полученный из Jetty. В Jetty же в этом потоке есть буфер. Пока поступают новые данные и есть место в буфере, эти данные пишутся именно в буфер. Если обработка запроса завершается до того, как буфер заполнен, вы получаете Content-Length и данные. Интересности начинаются, если буфера не хватает. В этом случае сервер сбрасывает данные в TCP сокет. Для HTTP 1.1 это будет chunked и первый чанк. Для HTTP 1.0 это будет просто поток данных. В HTTP 1.0 допускается отправлять сообщение без content-length на соединениях без keep-alive. В этом случае закрытие сокета является индикатором конца данных.

Что можно делать. Можно подкрутить костыли. В Jetty есть HttpConfiguration. Там есть OutputBufferSize и OutputAggregationSize. Первый задает буфер, который используется до того, как первая порция данных будет послана. Что делает второй — я . Вполне вероятно, влияет на размер чанков. Можете поэкспериментировать с обоими. Как добраться до них через Spring я не в курсе, не пользуюсь фреймворками. Если делать на сервере, то vsb предложил правильное решение
Автор: vsb
Дата: 06.05.22
. Делать фильтр, в фильтре собирать сообщение и вручную устанавливать Content-Length. На клиенте тоже стоит взять готовый http-client или добавить поддержку сборки фрагментов (или для http 1.0 чтение сообщения до конца потока). На фоне сети это не должно быть "медленно". Или на худой конец можно обычные сокеты сделать (не вебсокеты).
Re[6]: Как в Spring Boot включить Content-Length в ответ?
От: vsb Казахстан  
Дата: 06.05.22 18:46
Оценка: 19 (2)
Это не совсем правильный ответ. Во-первых Spring работает не так. Если он может вычислить длину ответа, он его вычисляет и проставляет Content-Length. Например если контролер отдаёт строку, то спринг корректно вычислит Content-Length, хоть гигабайт отдавай. Если он не может вычислить, то он действительно просто пишет в ServletOutputStream. И при этом Jetty ничего не вычисляет ни при какой ситуации. Вот пример кода и как он работает:

@RestController
public class TestController {

    @JsonInclude(NON_NULL)
    public static class Test1Response {
        private String s;
    }

    @GetMapping("/test1")
    public ResponseEntity<?> getTest1() {
        return ResponseEntity.ok(new Test1Response());
    }

    @GetMapping("/test2")
    public String getTest2() {
        return Strings.repeat("1", 1024 * 1024 * 1024);
    }
}


% curl -v http://127.0.0.1:8080/test1
> GET /test1 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.79.1
> Accept: */*
> 
< HTTP/1.1 200 
< Content-Type: application/json
< Transfer-Encoding: chunked
< Date: Fri, 06 May 2022 18:44:03 GMT
< 
{}                                                                                                 
% curl -v http://127.0.0.1:8080/test2 >/dev/null
> GET /test2 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.79.1
> Accept: */*
> 
< HTTP/1.1 200 
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 1073741824
< Date: Fri, 06 May 2022 18:44:16 GMT
<


Конкретное место, которое в Spring отвечает за вычисление длины ответа это org.springframework.http.converter.AbstractHttpMessageConverter#getContentLength

У обработчика строкового типа этот метод реализован, у jackson — нет.
Отредактировано 06.05.2022 19:04 vsb . Предыдущая версия . Еще …
Отредактировано 06.05.2022 18:47 vsb . Предыдущая версия .
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.