Skip to content

Commit 11d8c5f

Browse files
authored
Merge pull request EvilBeaver#1630 from asosnoviy/fix/rawStream
Fix RAW HTTPStream
2 parents f08b76f + cb898ef commit 11d8c5f

File tree

4 files changed

+125
-35
lines changed

4 files changed

+125
-35
lines changed

src/OneScript.StandardLibrary/Http/HttpResponseBody.cs

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ class HttpResponseBody : IDisposable
2323
bool _backFileIsTemp = false;
2424
byte[] _inMemBody;
2525

26-
private readonly bool _autoDecompress;
26+
private readonly bool _autoDecompress;
2727
private long _contentSize = 0;
28-
private Stream _rawStream;
28+
private readonly Stream _rawStream;
29+
private bool _inMemoryResponseInited;
2930

3031
public HttpResponseBody(HttpWebResponse response, string dumpToFile)
3132
{
@@ -35,69 +36,81 @@ public HttpResponseBody(HttpWebResponse response, string dumpToFile)
3536
return;
3637
}
3738

39+
_inMemoryResponseInited = false;
3840
_rawStream = response.GetResponseStream();
3941
_autoDecompress = string.Equals(response.ContentEncoding, "gzip", StringComparison.OrdinalIgnoreCase);
4042
_contentSize = _autoDecompress ? -1 : response.ContentLength;
4143

4244
if (!String.IsNullOrEmpty(dumpToFile))
4345
{
44-
InitFileBackedResponse(response, dumpToFile);
46+
InitFileBackedResponse(dumpToFile);
4547
}
4648
else if(_autoDecompress)
4749
{
48-
InitInMemoryResponse(response);
50+
InitInMemoryResponse();
4951
}
5052
}
5153

52-
private void InitInMemoryResponse(HttpWebResponse response)
54+
private void InitInMemoryResponse()
5355
{
5456
if(_contentSize > INMEMORY_BODY_LIMIT)
5557
{
5658
var filename = Path.GetTempFileName();
5759
_backFileIsTemp = true;
58-
InitFileBackedResponse(response, filename);
60+
InitFileBackedResponse(filename);
5961
}
6062
else
6163
{
6264
if(_contentSize == UNDEFINED_LENGTH)
6365
{
64-
ReadToStream(response);
66+
ReadToStream();
6567
}
6668
else
6769
{
68-
ReadToArray(response);
70+
ReadToArray();
6971
}
7072
}
73+
_inMemoryResponseInited = true;
7174
}
7275

7376
public bool AutoDecompress => _autoDecompress;
7477

7578
public long ContentSize => _contentSize < 0 ? 0 : _contentSize;
7679

77-
public Stream OpenReadStream()
80+
public Stream OpenReadStream(bool raw = false)
7881
{
82+
if (raw)
83+
{
84+
return GetResponseStream();
85+
}
86+
87+
if (!_inMemoryResponseInited)
88+
{
89+
InitInMemoryResponse();
90+
}
91+
7992
if (_backingFileName != null)
8093
{
8194
return new FileStream(_backingFileName, FileMode.Open, FileAccess.Read);
8295
}
83-
else if (_inMemBody != null)
84-
{
85-
return new MemoryStream(_inMemBody);
86-
}
96+
else if (_inMemBody != null)
97+
{
98+
return new MemoryStream(_inMemBody);
99+
}
87100
else
88-
return _rawStream;
101+
throw new InvalidOperationException("No response body");
89102
}
90103

91-
private Stream GetResponseStream(HttpWebResponse response)
104+
private Stream GetResponseStream()
92105
{
93106
if (_autoDecompress)
94-
return new GZipStream(response.GetResponseStream(), CompressionMode.Decompress);
95-
return response.GetResponseStream();
107+
return new GZipStream(_rawStream, CompressionMode.Decompress);
108+
return _rawStream;
96109
}
97-
98-
private void ReadToStream(HttpWebResponse response)
110+
111+
private void ReadToStream()
99112
{
100-
using (var responseStream = GetResponseStream(response))
113+
using (var responseStream = GetResponseStream())
101114
using(var ms = new MemoryStream())
102115
{
103116
bool memStreamIsAlive = true;
@@ -119,7 +132,7 @@ private void ReadToStream(HttpWebResponse response)
119132
var filename = Path.GetTempFileName();
120133
_backFileIsTemp = true;
121134
_backingFileName = filename;
122-
135+
123136
ms.Position = 0;
124137
using (var file = new FileStream(filename, FileMode.Create))
125138
{
@@ -146,11 +159,11 @@ private void ReadToStream(HttpWebResponse response)
146159
}
147160
}
148161

149-
private void ReadToArray(HttpWebResponse response)
162+
private void ReadToArray()
150163
{
151164
System.Diagnostics.Debug.Assert(_contentSize <= INMEMORY_BODY_LIMIT);
152-
153-
using var stream = GetResponseStream(response);
165+
166+
using var stream = GetResponseStream();
154167
var mustRead = (int)_contentSize;
155168
_inMemBody = new byte[mustRead];
156169
int offset = 0;
@@ -168,10 +181,10 @@ private void ReadToArray(HttpWebResponse response)
168181
}
169182
}
170183

171-
private void InitFileBackedResponse(HttpWebResponse response, string backingFileName)
184+
private void InitFileBackedResponse(string backingFileName)
172185
{
173186
_backingFileName = backingFileName;
174-
using(var responseStream = GetResponseStream(response))
187+
using (var responseStream = GetResponseStream())
175188
{
176189
using(var file = new FileStream(backingFileName, FileMode.Create))
177190
{
@@ -195,7 +208,7 @@ private static void StreamToStreamCopy(Stream responseStream, Stream acceptor)
195208

196209
private void Dispose(bool manualDispose)
197210
{
198-
if(manualDispose)
211+
if (manualDispose)
199212
{
200213
GC.SuppressFinalize(this);
201214
_inMemBody = null;

src/OneScript.StandardLibrary/Http/HttpResponseContext.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,14 +154,14 @@ public IValue GetBodyAsBinaryData()
154154
/// <summary>
155155
/// Интерпретировать ответ, как Поток
156156
/// </summary>
157+
/// <param name="rawStream">Булево. Будет получен сырой поток без дополнительной обработки. По умолчанию Ложь</param>
157158
/// <returns>Поток</returns>
158159
[ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")]
159-
public IValue GetBodyAsStream()
160+
public IValue GetBodyAsStream(bool rawStream = false)
160161
{
161162
if (_body == null)
162163
return ValueFactory.Create();
163-
164-
return new GenericStream(_body.OpenReadStream(), true);
164+
return new GenericStream(_body.OpenReadStream(rawStream), true);
165165
}
166166

167167
/// <summary>

tests/global-funcs.os

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66
///////////////////////////////////////////////////////////////////////
77

88
Перем юТест;
9+
Перем мАдресРесурса; // URL ресурса (хоста) для тестирования запросов
910

1011
////////////////////////////////////////////////////////////////////
1112
// Программный интерфейс
1213

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

16+
Если мАдресРесурса = Неопределено Тогда
17+
ПодключитьСценарий("./tests/http.os", "http_тест");
18+
мАдресРесурса = Новый http_тест().ВыбратьДоступныйХост();
19+
КонецЕсли;
20+
1521
юТест = ЮнитТестирование;
1622

1723
ВсеТесты = Новый Массив;
@@ -652,7 +658,7 @@
652658

653659
Приемник = ПолучитьИмяВременногоФайла();
654660

655-
КопироватьФайл("http://httpbin.org/image", Приемник);
661+
КопироватьФайл(мАдресРесурса + "/image", Приемник);
656662

657663
Попытка
658664
ДД = Новый ДвоичныеДанные(Приемник);
@@ -670,7 +676,7 @@
670676

671677
Приемник = ПолучитьИмяВременногоФайла();
672678

673-
КопироватьФайл("https://httpbin.org/image", Приемник);
679+
КопироватьФайл(мАдресРесурса + "/image", Приемник);
674680

675681
Попытка
676682
ДД = Новый ДвоичныеДанные(Приемник);
@@ -689,7 +695,7 @@
689695
Приемник = ПолучитьИмяВременногоФайла();
690696

691697
Попытка
692-
ПереместитьФайл("http://httpbin.org/image", Приемник);
698+
ПереместитьФайл(мАдресРесурса + "/image", Приемник);
693699
Исключение
694700
//Отвалится по 405, значит delete метод отрабатывает
695701
Описание = ОписаниеОшибки();

tests/http.os

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
ВсеТесты.Добавить("ТестДолженПроверитьЧтоМетодыБезТелаПриУстановленномТелеУспешноВыполняются");
4040
ВсеТесты.Добавить("ТестДолженПроверитьЧтоРаботаетПолучениеДвоичныхДанныхИзОтвета");
4141

42+
ВсеТесты.Добавить("ТестДолженПроверитьЧтоТелоМожноПолучитьНесколькоРаз");
43+
ВсеТесты.Добавить("ТестДолженПроверитьПолучениеТелаПослеЗакрытогоПотока");
44+
ВсеТесты.Добавить("ТестДолженПроверитьНедоступностьЧистогоПотокаПослеПолученияТела");
45+
4246
ВсеТесты.Добавить("ТестДолженПроверитьПолучениеStreamEvent");
4347

4448
Возврат ВсеТесты;
@@ -390,6 +394,72 @@
390394
юТест.ПроверитьТип(ДД, Тип("ДвоичныеДанные"));
391395
юТест.ПроверитьРавенство(ДД.Размер(), 10);
392396

397+
КонецПроцедуры
398+
399+
Процедура ТестДолженПроверитьЧтоТелоМожноПолучитьНесколькоРаз() Экспорт
400+
401+
Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2");
402+
403+
Соединение = Новый HttpСоединение(мАдресРесурса);
404+
Ответ = Соединение.Получить(Запрос);
405+
406+
юТест.ПроверитьРавенство(200, Ответ.КодСостояния);
407+
408+
Ответ.ПолучитьТелоКакПоток();
409+
410+
юТест.ПроверитьРавенство(Ответ.ПолучитьТелоКакПоток().ДоступноЧтение, Истина);
411+
412+
юТест.ПроверитьРавенство(ТипЗнч(Ответ.ПолучитьТелоКакДвоичныеДанные()), Тип("ДвоичныеДанные"));
413+
юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p1"": ""v1""");
414+
юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2""");
415+
416+
юТест.ПроверитьРавенство(ТипЗнч(Ответ.ПолучитьТелоКакДвоичныеДанные()), Тип("ДвоичныеДанные"));
417+
418+
// Оригинальный поток уже вычитан вернется поддельный
419+
юТест.ПроверитьРавенство(Ответ.ПолучитьТелоКакПоток().ДоступноЧтение, Истина);
420+
421+
КонецПроцедуры
422+
423+
Процедура ТестДолженПроверитьПолучениеТелаПослеЗакрытогоПотока() Экспорт
424+
425+
Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2");
426+
427+
Соединение = Новый HttpСоединение(мАдресРесурса);
428+
Ответ = Соединение.Получить(Запрос);
429+
430+
юТест.ПроверитьРавенство(200, Ответ.КодСостояния);
431+
432+
Поток = Ответ.ПолучитьТелоКакПоток();
433+
Поток.Закрыть();
434+
435+
юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2""");
436+
437+
КонецПроцедуры
438+
439+
Процедура ТестДолженПроверитьНедоступностьЧистогоПотокаПослеПолученияТела() Экспорт
440+
441+
Запрос = Новый HttpЗапрос("/get?p1=v1&p2=v2");
442+
443+
Соединение = Новый HttpСоединение(мАдресРесурса);
444+
Ответ = Соединение.Получить(Запрос);
445+
446+
юТест.ПроверитьРавенство(200, Ответ.КодСостояния);
447+
448+
юТест.ПроверитьВхождение(Ответ.ПолучитьТелоКакСтроку(), """p2"": ""v2""");
449+
450+
// чистый поток не доступен для чтения
451+
Поток = Ответ.ПолучитьТелоКакПоток(Истина);
452+
юТест.ПроверитьРавенство(Поток.ДоступноЧтение, Ложь);
453+
454+
// оберточный нормальный
455+
Поток = Ответ.ПолучитьТелоКакПоток();
456+
юТест.ПроверитьРавенство(Поток.ДоступноЧтение, Истина);
457+
ЧтениеДанных = Новый ЧтениеДанных(Поток);
458+
юТест.ПроверитьВхождение(
459+
ПолучитьСтрокуИзДвоичныхДанных(ЧтениеДанных.Прочитать().ПолучитьДвоичныеДанные()),
460+
"""p2"": ""v2"""
461+
);
462+
393463
КонецПроцедуры
394464

395465
Процедура ТестДолженПроверитьПолучениеStreamEvent() Экспорт
@@ -411,7 +481,7 @@
411481
Прошло = ТекущаяУниверсальнаяДатаВМиллисекундах() - Старт;
412482
ютест.ПроверитьМеньше(Прошло, 600);
413483

414-
ТелоПоток = Ответ.ПолучитьТелоКакПоток();
484+
ТелоПоток = Ответ.ПолучитьТелоКакПоток(Истина);
415485
юТест.ПроверитьТип(ТелоПоток, Тип("Поток"));
416486

417487
ЧтениеДанных = Новый ЧтениеДанных(ТелоПоток);
@@ -492,7 +562,7 @@
492562

493563
КонецФункции
494564

495-
Функция ВыбратьДоступныйХост()
565+
Функция ВыбратьДоступныйХост() Экспорт
496566

497567
// Если задана переменная окружения, используем ее при наличии значения
498568
ЗначениеИзОкружения = ПолучитьПеременнуюСреды("OS_HTTP_TEST_HOST");
@@ -508,6 +578,7 @@
508578
// Пробуем типовые варианты httpbin
509579
Кандидаты = Новый Массив;
510580
Кандидаты.Добавить("http://127.0.0.1:8085");
581+
Кандидаты.Добавить("https://connectorhttp.ru");
511582
Кандидаты.Добавить("https://httpbin.org");
512583
Кандидаты.Добавить("https://httpbingo.org");
513584

0 commit comments

Comments
 (0)