Skip to content

Commit 07f6cec

Browse files
committed
Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # src/OneScript.StandardLibrary/Http/HttpResponseBody.cs
2 parents 94473d1 + 11d8c5f commit 07f6cec

File tree

5 files changed

+132
-41
lines changed

5 files changed

+132
-41
lines changed

.github/workflows/sonar.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ jobs:
1919
with:
2020
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
2121
- name: Cache SonarQube packages
22-
uses: actions/cache@v4
22+
uses: actions/cache@v5
2323
with:
2424
path: ~\sonar\cache
2525
key: ${{ runner.os }}-sonar
2626
restore-keys: ${{ runner.os }}-sonar
2727
- name: Cache SonarQube scanner
2828
id: cache-sonar-scanner
29-
uses: actions/cache@v4
29+
uses: actions/cache@v5
3030
with:
3131
path: .\.sonar\scanner
3232
key: ${{ runner.os }}-sonar-scanner

src/OneScript.StandardLibrary/Http/HttpResponseBody.cs

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*----------------------------------------------------------
2-
This Source Code Form is subject to the terms of the
3-
Mozilla Public License, v.2.0. If a copy of the MPL
4-
was not distributed with this file, You can obtain one
2+
This Source Code Form is subject to the terms of the
3+
Mozilla Public License, v.2.0. If a copy of the MPL
4+
was not distributed with this file, You can obtain one
55
at http://mozilla.org/MPL/2.0/.
66
----------------------------------------------------------*/
77

@@ -25,7 +25,8 @@ class HttpResponseBody : IDisposable
2525

2626
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,47 +36,59 @@ 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
}
46-
else if (_autoDecompress)
48+
else if(_autoDecompress)
4749
{
48-
InitInMemoryResponse(response);
50+
InitInMemoryResponse();
4951
}
5052
}
5153

52-
private void InitInMemoryResponse(HttpWebResponse response)
54+
private void InitInMemoryResponse()
5355
{
54-
if (_contentSize > INMEMORY_BODY_LIMIT)
56+
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
{
62-
if (_contentSize == UNDEFINED_LENGTH)
64+
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);
@@ -85,20 +98,20 @@ public Stream OpenReadStream()
8598
return new MemoryStream(_inMemBody);
8699
}
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
}
97110

98-
private void ReadToStream(HttpWebResponse response)
111+
private void ReadToStream()
99112
{
100-
using (var responseStream = GetResponseStream(response))
101-
using (var ms = new MemoryStream())
113+
using (var responseStream = GetResponseStream())
114+
using(var ms = new MemoryStream())
102115
{
103116
bool memStreamIsAlive = true;
104117

@@ -114,7 +127,7 @@ private void ReadToStream(HttpWebResponse response)
114127

115128
readTotal += bytesRead;
116129

117-
if (readTotal > INMEMORY_BODY_LIMIT)
130+
if(readTotal > INMEMORY_BODY_LIMIT)
118131
{
119132
var filename = Path.GetTempFileName();
120133
_backFileIsTemp = true;
@@ -130,10 +143,11 @@ private void ReadToStream(HttpWebResponse response)
130143
}
131144

132145
break;
146+
133147
}
134148
}
135149

136-
if (memStreamIsAlive)
150+
if(memStreamIsAlive)
137151
{
138152
_inMemBody = new byte[ms.Length];
139153
ms.Position = 0;
@@ -145,11 +159,11 @@ private void ReadToStream(HttpWebResponse response)
145159
}
146160
}
147161

148-
private void ReadToArray(HttpWebResponse response)
162+
private void ReadToArray()
149163
{
150164
System.Diagnostics.Debug.Assert(_contentSize <= INMEMORY_BODY_LIMIT);
151165

152-
using var stream = GetResponseStream(response);
166+
using var stream = GetResponseStream();
153167
var mustRead = (int)_contentSize;
154168
_inMemBody = new byte[mustRead];
155169
int offset = 0;
@@ -167,12 +181,12 @@ private void ReadToArray(HttpWebResponse response)
167181
}
168182
}
169183

170-
private void InitFileBackedResponse(HttpWebResponse response, string backingFileName)
184+
private void InitFileBackedResponse(string backingFileName)
171185
{
172186
_backingFileName = backingFileName;
173-
using (var responseStream = GetResponseStream(response))
187+
using (var responseStream = GetResponseStream())
174188
{
175-
using (var file = new FileStream(backingFileName, FileMode.Create))
189+
using(var file = new FileStream(backingFileName, FileMode.Create))
176190
{
177191
StreamToStreamCopy(responseStream, file);
178192
}
@@ -205,9 +219,9 @@ private void Dispose(bool manualDispose)
205219

206220
private void KillTemporaryFile()
207221
{
208-
if (_backFileIsTemp && _backingFileName != null)
222+
if(_backFileIsTemp && _backingFileName != null)
209223
{
210-
if (File.Exists(_backingFileName))
224+
if(File.Exists(_backingFileName))
211225
{
212226
try
213227
{
@@ -231,4 +245,4 @@ public void Dispose()
231245
Dispose(false);
232246
}
233247
}
234-
}
248+
}

src/OneScript.StandardLibrary/Http/HttpResponseContext.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,14 +131,14 @@ public IValue GetBodyAsBinaryData()
131131
/// <summary>
132132
/// Интерпретировать ответ, как Поток
133133
/// </summary>
134+
/// <param name="rawStream">Булево. Будет получен сырой поток без дополнительной обработки. По умолчанию Ложь</param>
134135
/// <returns>Поток</returns>
135136
[ContextMethod("ПолучитьТелоКакПоток", "GetBodyAsStream")]
136-
public IValue GetBodyAsStream()
137+
public IValue GetBodyAsStream(bool rawStream = false)
137138
{
138139
if (_body == null)
139140
return ValueFactory.Create();
140-
141-
return new GenericStream(_body.OpenReadStream(), true);
141+
return new GenericStream(_body.OpenReadStream(rawStream), true);
142142
}
143143

144144
/// <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)