Skip to content

Commit 3fe6700

Browse files
authored
Merge pull request EvilBeaver#1618 from asosnoviy/feature/rawStream
Использование потока httpResponse без вычитывания по возможности
2 parents bcf9e6a + 57d4481 commit 3fe6700

File tree

3 files changed

+127
-25
lines changed

3 files changed

+127
-25
lines changed

src/OneScript.StandardLibrary/Http/HttpResponseBody.cs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*----------------------------------------------------------
1+
/*----------------------------------------------------------
22
This Source Code Form is subject to the terms of the
33
Mozilla Public License, v.2.0. If a copy of the MPL
44
was not distributed with this file, You can obtain one
@@ -25,6 +25,7 @@ class HttpResponseBody : IDisposable
2525

2626
private readonly bool _autoDecompress;
2727
private long _contentSize = 0;
28+
private Stream _rawStream;
2829

2930
public HttpResponseBody(HttpWebResponse response, string dumpToFile)
3031
{
@@ -33,17 +34,18 @@ public HttpResponseBody(HttpWebResponse response, string dumpToFile)
3334
_inMemBody = Array.Empty<byte>();
3435
return;
3536
}
36-
37+
38+
_rawStream = response.GetResponseStream();
3739
_autoDecompress = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase);
3840
_contentSize = _autoDecompress ? -1 : response.ContentLength;
3941

40-
if (String.IsNullOrEmpty(dumpToFile))
42+
if (!String.IsNullOrEmpty(dumpToFile))
4143
{
42-
InitInMemoryResponse(response);
44+
InitFileBackedResponse(response, dumpToFile);
4345
}
44-
else
46+
else if(_autoDecompress)
4547
{
46-
InitFileBackedResponse(response, dumpToFile);
48+
InitInMemoryResponse(response);
4749
}
4850
}
4951

@@ -78,12 +80,12 @@ public Stream OpenReadStream()
7880
{
7981
return new FileStream(_backingFileName, FileMode.Open, FileAccess.Read);
8082
}
81-
else if (_inMemBody != null)
82-
{
83-
return new MemoryStream(_inMemBody);
84-
}
83+
else if (_inMemBody != null)
84+
{
85+
return new MemoryStream(_inMemBody);
86+
}
8587
else
86-
throw new InvalidOperationException("No response body");
88+
return _rawStream;
8789
}
8890

8991
private Stream GetResponseStream(HttpWebResponse response)

src/OneScript.StandardLibrary/Http/HttpResponseContext.cs

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*----------------------------------------------------------
1+
/*----------------------------------------------------------
22
This Source Code Form is subject to the terms of the
33
Mozilla Public License, v.2.0. If a copy of the MPL
44
was not distributed with this file, You can obtain one
@@ -28,18 +28,25 @@ public class HttpResponseContext : AutoContext<HttpResponseContext>, IDisposable
2828
// TODO: Нельзя выделить массив размером больше чем 2GB
2929
// поэтому функционал сохранения в файл не должен использовать промежуточный буфер _body
3030
private HttpResponseBody _body;
31-
31+
private HttpWebResponse _response;
32+
3233
private string _defaultCharset;
3334
private string _filename;
3435

35-
public HttpResponseContext(HttpWebResponse response)
36-
{
37-
RetrieveResponseData(response, null);
38-
}
39-
4036
public HttpResponseContext(HttpWebResponse response, string dumpToFile)
4137
{
42-
RetrieveResponseData(response, dumpToFile);
38+
StatusCode = (int)response.StatusCode;
39+
_defaultCharset = response.CharacterSet;
40+
41+
ProcessHeaders(response.Headers);
42+
ProcessResponseBody(response, dumpToFile);
43+
_response = response;
44+
45+
if (_body != null && _body.AutoDecompress)
46+
{
47+
_headers.Delete(ValueFactory.Create("Content-Encoding"));
48+
_headers.SetIndexedValue(ValueFactory.Create("Content-Length"), ValueFactory.Create(_body.ContentSize));
49+
}
4350
}
4451

4552
private void RetrieveResponseData(HttpWebResponse response, string dumpToFile)
@@ -137,10 +144,10 @@ public IValue GetBodyAsBinaryData()
137144
return ValueFactory.Create();
138145

139146
using (var stream = _body.OpenReadStream())
140-
{
141-
var data = new byte[stream.Length];
142-
stream.Read(data, 0, data.Length);
143-
return new BinaryDataContext(data);
147+
using (var memoryStream = new MemoryStream())
148+
{
149+
stream.CopyTo(memoryStream);
150+
return new BinaryDataContext(memoryStream.ToArray());
144151
}
145152
}
146153

@@ -181,8 +188,11 @@ public void Close()
181188

182189
public void Dispose()
183190
{
184-
if (_body != null)
185-
_body.Dispose();
191+
_response?.Dispose();
192+
_response = null;
193+
194+
_body?.Dispose();
195+
_body = null;
186196
}
187197
}
188198
}

tests/http.os

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Перем юТест;
22

33
Перем мАдресРесурса; // URL ресурса (хоста) для тестирования запросов
4+
Перем ПортТестовогоСервера;
45

56
Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт
67

@@ -36,6 +37,9 @@
3637
ВсеТесты.Добавить("ТестДолженПроверитьЧтоМожноЗадатьТелоЗапросаСПомощьюПотока");
3738

3839
ВсеТесты.Добавить("ТестДолженПроверитьЧтоМетодыБезТелаПриУстановленномТелеУспешноВыполняются");
40+
ВсеТесты.Добавить("ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета");
41+
42+
ВсеТесты.Добавить("ТестДолженПроверитьПолучениеStreamEvent");
3943

4044
Возврат ВсеТесты;
4145
КонецФункции
@@ -376,6 +380,91 @@
376380

377381
КонецПроцедуры
378382

383+
Процедура ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета() Экспорт
384+
385+
Запрос = Новый HttpЗапрос("/bytes/10");
386+
Соединение = Новый HttpСоединение(мАдресРесурса);
387+
Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
388+
389+
ДД = Ответ.ПолучитьТелоКакДвоичныеДанные();
390+
юТест.ПроверитьТип(ДД, Тип("ДвоичныеДанные"));
391+
юТест.ПроверитьРавенство(ДД.Размер(), 10);
392+
393+
КонецПроцедуры
394+
395+
Процедура ТестДолженПроверитьПолучениеStreamEvent() Экспорт
396+
397+
МенеджерФоновыхЗаданий = Новый МенеджерФоновыхЗаданий;
398+
ЗаданиеВебсервера = МенеджерФоновыхЗаданий.Выполнить(ЭтотОбъект, "Вебсервер");
399+
400+
ПортТестовогоСервера = 8181;
401+
Соединение = Новый HTTPСоединение("http://127.0.0.1:" + ПортТестовогоСервера);
402+
Запрос = Новый HTTPЗапрос("/");
403+
404+
// Подождем пока поднимится сервер
405+
Приостановить(1000);
406+
407+
Старт = ТекущаяУниверсальнаяДатаВМиллисекундах();
408+
409+
Ответ = Соединение.ВызватьHTTPМетод("GET", Запрос);
410+
411+
Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт;
412+
ютест.ПроверитьМеньше(Прошло, 600);
413+
414+
ТелоПоток = Ответ.ПолучитьТелоКакПоток();
415+
юТест.ПроверитьТип(ТелоПоток, Тип("Поток"));
416+
417+
ЧтениеДанных = Новый ЧтениеДанных(ТелоПоток);
418+
Таймаут = 600;
419+
Пока Истина Цикл
420+
421+
ПрочтенныеДанные = ЧтениеДанных.Прочитать(20);
422+
423+
Строка = ПолучитьСтрокуИзДвоичныхДанных(ПрочтенныеДанные.ПолучитьДвоичныеДанные());
424+
Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт;
425+
426+
ютест.ПроверитьМеньше(Прошло, Таймаут);
427+
428+
Таймаут = Таймаут + Таймаут;
429+
Если Строка = "" ИЛИ Строка = "data: [DONE]" + Символы.ПС + Символы.ПС Тогда
430+
Прервать;
431+
КонецЕсли;
432+
433+
юТест.ПроверитьРавенство(Строка, "data: [1]" + Символы.ПС + Символы.ПС);
434+
435+
КонецЦикла;
436+
437+
ТелоПоток.Закрыть();
438+
439+
МенеджерФоновыхЗаданий.Очистить();
440+
КонецПроцедуры
441+
442+
Функция ОбработчикЗапроса(Контекст, СледующийОбработчик) Экспорт
443+
444+
Контекст.Ответ.КодСостояния = 200;
445+
Контекст.Ответ.ТипКонтента = "text/event-stream";
446+
447+
ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения();
448+
ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело);
449+
450+
Приостановить(500);
451+
ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [1]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения();
452+
ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело);
453+
454+
Приостановить(500);
455+
ПотокОтвета = ПолучитьДвоичныеДанныеИзСтроки("data: [DONE]" + Символы.ПС + Символы.ПС).ОткрытьПотокДляЧтения();
456+
ПотокОтвета.КопироватьВ(Контекст.Ответ.Тело);
457+
458+
КонецФункции
459+
460+
Процедура Вебсервер() Экспорт
461+
462+
Вебсервер = Новый ВебСервер(ПортТестовогоСервера);
463+
Вебсервер.ДобавитьОбработчикЗапросов(ЭтотОбъект, "ОбработчикЗапроса");
464+
Вебсервер.Запустить();
465+
466+
КонецПроцедуры
467+
379468
Функция JsonВОбъект(Json)
380469

381470
ЧтениеJSON = Новый ЧтениеJSON;
@@ -418,6 +507,7 @@
418507

419508
// Пробуем типовые варианты httpbin
420509
Кандидаты = Новый Массив;
510+
Кандидаты.Добавить("http://127.0.0.1:8085");
421511
Кандидаты.Добавить("https://httpbin.org");
422512
Кандидаты.Добавить("https://httpbingo.org");
423513

0 commit comments

Comments
 (0)