diff --git a/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs b/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs index 6a9eb8106..78d6d1a31 100644 --- a/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs +++ b/src/OneScript.StandardLibrary/Http/HttpResponseBody.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -25,6 +25,7 @@ class HttpResponseBody : IDisposable private readonly bool _autoDecompress; private long _contentSize = 0; + private Stream _rawStream; public HttpResponseBody(HttpWebResponse response, string dumpToFile) { @@ -33,17 +34,18 @@ public HttpResponseBody(HttpWebResponse response, string dumpToFile) _inMemBody = Array.Empty(); return; } - + + _rawStream = response.GetResponseStream(); _autoDecompress = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase); _contentSize = _autoDecompress ? -1 : response.ContentLength; - if (String.IsNullOrEmpty(dumpToFile)) + if (!String.IsNullOrEmpty(dumpToFile)) { - InitInMemoryResponse(response); + InitFileBackedResponse(response, dumpToFile); } - else + else if(_autoDecompress) { - InitFileBackedResponse(response, dumpToFile); + InitInMemoryResponse(response); } } @@ -78,12 +80,12 @@ public Stream OpenReadStream() { return new FileStream(_backingFileName, FileMode.Open, FileAccess.Read); } - else if (_inMemBody != null) - { - return new MemoryStream(_inMemBody); - } + else if (_inMemBody != null) + { + return new MemoryStream(_inMemBody); + } else - throw new InvalidOperationException("No response body"); + return _rawStream; } private Stream GetResponseStream(HttpWebResponse response) diff --git a/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs b/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs index 6f4f0fcbb..15302bf88 100644 --- a/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs +++ b/src/OneScript.StandardLibrary/Http/HttpResponseContext.cs @@ -1,4 +1,4 @@ -/*---------------------------------------------------------- +/*---------------------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v.2.0. If a copy of the MPL was not distributed with this file, You can obtain one @@ -28,18 +28,25 @@ public class HttpResponseContext : AutoContext, IDisposable // TODO: Нельзя выделить массив размером больше чем 2GB // поэтому функционал сохранения в файл не должен использовать промежуточный буфер _body private HttpResponseBody _body; - + private HttpWebResponse _response; + private string _defaultCharset; private string _filename; - public HttpResponseContext(HttpWebResponse response) - { - RetrieveResponseData(response, null); - } - public HttpResponseContext(HttpWebResponse response, string dumpToFile) { - RetrieveResponseData(response, dumpToFile); + StatusCode = (int)response.StatusCode; + _defaultCharset = response.CharacterSet; + + ProcessHeaders(response.Headers); + ProcessResponseBody(response, dumpToFile); + _response = response; + + if (_body != null && _body.AutoDecompress) + { + _headers.Delete(ValueFactory.Create("Content-Encoding")); + _headers.SetIndexedValue(ValueFactory.Create("Content-Length"), ValueFactory.Create(_body.ContentSize)); + } } private void RetrieveResponseData(HttpWebResponse response, string dumpToFile) @@ -137,10 +144,10 @@ public IValue GetBodyAsBinaryData() return ValueFactory.Create(); using (var stream = _body.OpenReadStream()) - { - var data = new byte[stream.Length]; - stream.Read(data, 0, data.Length); - return new BinaryDataContext(data); + using (var memoryStream = new MemoryStream()) + { + stream.CopyTo(memoryStream); + return new BinaryDataContext(memoryStream.ToArray()); } } @@ -181,8 +188,11 @@ public void Close() public void Dispose() { - if (_body != null) - _body.Dispose(); + _response?.Dispose(); + _response = null; + + _body?.Dispose(); + _body = null; } } } diff --git a/tests/http.os b/tests/http.os index d2f89a6d5..4d70c4279 100644 --- a/tests/http.os +++ b/tests/http.os @@ -1,6 +1,7 @@ Перем юТест; Перем мАдресРесурса; // URL ресурса (хоста) для тестирования запросов +Перем ПортТестовогоСервера; Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт @@ -36,6 +37,9 @@ ВсеТесты.Добавить("ТестДолженПроверитьЧтоМожноЗадатьТелоЗапросаСПомощьюПотока"); ВсеТесты.Добавить("ТестДолженПроверитьЧтоМетодыБезТелаПриУстановленномТелеУспешноВыполняются"); + ВсеТесты.Добавить("ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета"); + + ВсеТесты.Добавить("ТестДолженПроверитьПолучениеStreamEvent"); Возврат ВсеТесты; КонецФункции @@ -376,6 +380,91 @@ КонецПроцедуры +Процедура ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета() Экспорт + + Запрос = Новый HttpЗапрос("/bytes/10"); + Соединение = Новый HttpСоединение(мАдресРесурса); + Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос); + + ДД = Ответ.ПолучитьТелоКакДвоичныеДанные(); + юТест.ПроверитьТип(ДД, Тип("ДвоичныеДанные")); + юТест.ПроверитьРавенство(ДД.Размер(), 10); + +КонецПроцедуры + +Процедура ТестДолженПроверитьПолучениеStreamEvent() Экспорт + + МенеджерФоновыхЗаданий = Новый МенеджерФоновыхЗаданий; + ЗаданиеВебсервера = МенеджерФоновыхЗаданий.Выполнить(ЭтотОбъект, "Вебсервер"); + + ПортТестовогоСервера = 8181; + Соединение = Новый HTTPСоединение("http://127.0.0.1:" + ПортТестовогоСервера); + Запрос = Новый HTTPЗапрос("/"); + + // Подождем пока поднимится сервер + Приостановить(1000); + + Старт = ТекущаяУниверсальнаяДатаВМиллисекундах(); + + Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос); + + Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт; + ютест.ПроверитьМеньше(Прошло, 600); + + ТелоПоток = Ответ.ПолучитьТелоКакПоток(); + юТест.ПроверитьТип(ТелоПоток, Тип("Поток")); + + ЧтениеДанных = Новый ЧтениеДанных(ТелоПоток); + Таймаут = 600; + Пока Истина Цикл + + ПрочтенныеДанные = ЧтениеДанных.Прочитать(20); + + Строка = ПолучитьСтрокуИзДвоичныхДанных(ПрочтенныеДанные.ПолучитьДвоичныеДанные()); + Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт; + + ютест.ПроверитьМеньше(Прошло, Таймаут); + + Таймаут = Таймаут + Таймаут; + Если Строка = "" ИЛИ Строка = "data: [DONE]" + Символы.ПС + Символы.ПС Тогда + Прервать; + КонецЕсли; + + юТест.ПроверитьРавенство(Строка, "data: [1]" + Символы.ПС + Символы.ПС); + + КонецЦикла; + + ТелоПоток.Закрыть(); + + МенеджерФоновыхЗаданий.Очистить(); +КонецПроцедуры + +Функция ОбработчикЗапроса(Контекст, СледующийОбработчик) Экспорт + + Контекст.Ответ.КодСостояния = 200; + Контекст.Ответ.ТипКонтента = "text/event-stream"; + + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + + Приостановить(500); + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + + Приостановить(500); + ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [DONE]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения(); + ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело); + +КонецФункции + +Процедура Вебсервер() Экспорт + + Вебсервер = Новый ВебСервер(ПортТестовогоСервера); + Вебсервер.ДобавитьОбработчикЗапросов(ЭтотОбъект, "ОбработчикЗапроса"); + Вебсервер.Запустить(); + +КонецПроцедуры + Функция JsonВОбъект(Json) ЧтениеJSON = Новый ЧтениеJSON; @@ -418,6 +507,7 @@ // Пробуем типовые варианты httpbin Кандидаты = Новый Массив; + Кандидаты.Добавить("http://127.0.0.1:8085"); Кандидаты.Добавить("https://httpbin.org"); Кандидаты.Добавить("https://httpbingo.org");