Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/OneScript.StandardLibrary/Binary/BinaryDataContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public BinaryDataContext(Stream stream)

private void ReadFromStream(Stream stream)
{
if (stream.Length < FileBackingConstants.DEFAULT_MEMORY_LIMIT)
if (stream.Length < BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes())
{
LoadToBuffer(stream);
}
Expand Down
3 changes: 2 additions & 1 deletion src/OneScript.StandardLibrary/Binary/FileBackingConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ This Source Code Form is subject to the terms of the
----------------------------------------------------------*/

using System;
using ScriptEngine;

namespace OneScript.StandardLibrary.Binary
{
public static class FileBackingConstants
{
public const int DEFAULT_MEMORY_LIMIT = 1024 * 1024 * 50; // 50 Mb
public const int DEFAULT_MEMORY_LIMIT = BinaryDataConfigurationDefaults.InMemoryMaxBytes;
public const int SYSTEM_IN_MEMORY_LIMIT = Int32.MaxValue;
}
}
3 changes: 2 additions & 1 deletion src/OneScript.StandardLibrary/Binary/FileBackingStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the

using System;
using System.IO;
using ScriptEngine.Machine;

namespace OneScript.StandardLibrary.Binary
{
Expand All @@ -20,7 +21,7 @@ public sealed class FileBackingStream : Stream

private string _backingFileName;

public FileBackingStream() : this(FileBackingConstants.DEFAULT_MEMORY_LIMIT)
public FileBackingStream() : this(BinaryDataRuntimeSettings.GetEffectiveInMemoryMaxBytes())
{
}

Expand Down
6 changes: 5 additions & 1 deletion src/ScriptEngine.HostedScript/oscript.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,8 @@ lib.system = ../lib

#encoding.script=utf-8

#systemlanguage = ru
#systemlanguage = ru

# Порог в байтах: пока размер двоичных данных в памяти не превышает это значение, данные держатся в RAM;
# при большем размере используется временный файл. По умолчанию 52428800 (50 МБ).
#binaryData.inMemoryMaxBytes=52428800
20 changes: 20 additions & 0 deletions src/ScriptEngine/BinaryDataConfigurationDefaults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*----------------------------------------------------------
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
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

namespace ScriptEngine
{
/// <summary>
/// Значения по умолчанию для буферизации двоичных данных в памяти до перехода на временный файл.
/// </summary>
public static class BinaryDataConfigurationDefaults
{
/// <summary>
/// Лимит по умолчанию (50 МБ), если в конфигурации не задан ключ binaryData.inMemoryMaxBytes.
/// </summary>
public const int InMemoryMaxBytes = 1024 * 1024 * 50;
}
}
24 changes: 17 additions & 7 deletions src/ScriptEngine/BslProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,23 +40,33 @@ public BslProcess(int id, ExecutionContext context, IEnumerable<IExecutorProvide
public BslValue Run(BslObjectValue target, IExecutableModule module, BslScriptMethodInfo method, IValue[] arguments)
{
var notifyExecutors = !_isRunning;
if (notifyExecutors)
{
Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this));
}

_isRunning = true;
if (notifyExecutors)
BinaryDataRuntimeSettings.PushFromServices(Services);

try
{
if (notifyExecutors)
Array.ForEach(_executorProviders, e => e.BeforeProcessStart(this));

_isRunning = true;

return _bslExecutorsByModule[module.GetType()](this, target, module, method, arguments);
}
finally
{
if (notifyExecutors)
{
Array.ForEach(_executorProviders, e => e.AfterProcessExit(this));
_isRunning = false;
try
{
if (_isRunning)
Array.ForEach(_executorProviders, e => e.AfterProcessExit(this));
}
finally
{
BinaryDataRuntimeSettings.Pop();
_isRunning = false;
}
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions src/ScriptEngine/Hosting/EngineBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ public static IEngineBuilder SetDefaultOptions(this IEngineBuilder builder)
var holder = sp.Resolve<EngineConfiguration>();
return holder.GetConfig();
});

services.RegisterSingleton<IBinaryDataMemoryLimit>(sp =>
new BinaryDataMemoryLimitFromOptions(sp.Resolve<OneScriptCoreOptions>()));

services.Register<ScriptingEngine>();

Expand Down
28 changes: 28 additions & 0 deletions src/ScriptEngine/IBinaryDataMemoryLimit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*----------------------------------------------------------
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
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

namespace ScriptEngine
{
/// <summary>
/// Лимит объёма данных в памяти для объектов «ДвоичныеДанные» и смежных потоков
/// до выгрузки во временный файл (байты).
/// </summary>
public interface IBinaryDataMemoryLimit
{
int MaxBytesInMemory { get; }
}

internal sealed class BinaryDataMemoryLimitFromOptions : IBinaryDataMemoryLimit
{
public BinaryDataMemoryLimitFromOptions(OneScriptCoreOptions options)
{
MaxBytesInMemory = options.BinaryDataInMemoryMaxBytes;
}

public int MaxBytesInMemory { get; }
}
}
43 changes: 43 additions & 0 deletions src/ScriptEngine/Machine/BinaryDataRuntimeSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*----------------------------------------------------------
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
at http://mozilla.org/MPL/2.0/.
----------------------------------------------------------*/

using System.Threading;
using OneScript.DependencyInjection;

namespace ScriptEngine.Machine
{
/// <summary>
/// Лимит памяти для двоичных данных на время выполнения процесса BSL (AsyncLocal).
/// </summary>
public static class BinaryDataRuntimeSettings
{
private static readonly AsyncLocal<int?> InMemoryMaxBytes = new AsyncLocal<int?>();

internal static void PushFromServices(IServiceContainer services)
{
var opts = services.TryResolve<IBinaryDataMemoryLimit>();
var maxBytes = opts?.MaxBytesInMemory ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
if (maxBytes <= 0)
maxBytes = BinaryDataConfigurationDefaults.InMemoryMaxBytes;
InMemoryMaxBytes.Value = maxBytes;
}

internal static void Pop()
{
InMemoryMaxBytes.Value = null;
}

/// <summary>
/// Текущий лимит «в памяти до временного файла» для двоичных данных (байты).
/// Вне процесса BSL возвращает значение по умолчанию из <see cref="BinaryDataConfigurationDefaults.InMemoryMaxBytes"/>.
/// </summary>
public static int GetEffectiveInMemoryMaxBytes()
{
return InMemoryMaxBytes.Value ?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
}
}
}
29 changes: 29 additions & 0 deletions src/ScriptEngine/OneScriptCoreOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This Source Code Form is subject to the terms of the

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using OneScript.Commons;
using OneScript.Native.Compiler;
Expand All @@ -21,6 +22,7 @@ public class OneScriptCoreOptions
private const string PREPROCESSOR_DEFINITIONS_KEY = "preprocessor.define";
private const string DEFAULT_RUNTIME_KEY = "runtime.default";
private const string EXPLICIT_IMPORT = "lang.explicitImports";
private const string BINARY_DATA_IN_MEMORY_MAX = "binaryData.inMemoryMaxBytes";

public OneScriptCoreOptions(KeyValueConfig config)
{
Expand All @@ -29,6 +31,7 @@ public OneScriptCoreOptions(KeyValueConfig config)
PreprocessorDefinitions = SetupDefinitions(config[PREPROCESSOR_DEFINITIONS_KEY]);
UseNativeAsDefaultRuntime = SetupDefaultRuntime(config[DEFAULT_RUNTIME_KEY]);
ExplicitImports = SetupExplicitImports(config[EXPLICIT_IMPORT]);
BinaryDataInMemoryMaxBytes = SetupBinaryDataInMemoryMax(config[BINARY_DATA_IN_MEMORY_MAX]);
}

public string SystemLanguage { get; }
Expand All @@ -41,6 +44,12 @@ public OneScriptCoreOptions(KeyValueConfig config)

public ExplicitImportsBehavior ExplicitImports { get; }

/// <summary>
/// Максимальный объём двоичных данных в памяти до перехода на временный файл (байты).
/// Задаётся ключом binaryData.inMemoryMaxBytes в конфигурации.
/// </summary>
public int BinaryDataInMemoryMaxBytes { get; }

private static IEnumerable<string> SetupDefinitions(string s)
{
return s?.Split(',') ?? Array.Empty<string>();
Expand Down Expand Up @@ -79,5 +88,25 @@ private static ExplicitImportsBehavior SetupExplicitImports(string keyValue)
return ExplicitImportsBehavior.Warn;
}
}

private static int SetupBinaryDataInMemoryMax(string rawValue)
{
if (string.IsNullOrWhiteSpace(rawValue))
return BinaryDataConfigurationDefaults.InMemoryMaxBytes;

if (!int.TryParse(rawValue.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var bytes))
{
SystemLogger.Write($"Invalid value for {BINARY_DATA_IN_MEMORY_MAX}: {rawValue}");
return BinaryDataConfigurationDefaults.InMemoryMaxBytes;
}

if (bytes <= 0 || bytes == int.MaxValue)
{
SystemLogger.Write($"Value for {BINARY_DATA_IN_MEMORY_MAX} must be between 1 and {int.MaxValue - 1}: {bytes}");
return BinaryDataConfigurationDefaults.InMemoryMaxBytes;
}

return bytes;
}
}
}
10 changes: 8 additions & 2 deletions src/oscript/CgiBehavior.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the
using OneScript.StandardLibrary;
using oscript.Web;

using ScriptEngine;
using ScriptEngine.HostedScript;
using ScriptEngine.Hosting;
using ScriptEngine.Machine;
Expand Down Expand Up @@ -67,8 +68,13 @@ private int RunCGIMode(string scriptFile)
});

var engine = ConsoleHostBuilder.Build(builder);

var request = new WebRequestContext();

var binaryMemoryLimit = engine.Services.TryResolve<IBinaryDataMemoryLimit>()?.MaxBytesInMemory
?? BinaryDataConfigurationDefaults.InMemoryMaxBytes;
if (binaryMemoryLimit <= 0)
binaryMemoryLimit = BinaryDataConfigurationDefaults.InMemoryMaxBytes;

var request = new WebRequestContext(binaryMemoryLimit);
engine.InjectGlobalProperty("ВебЗапрос", "WebRequest", request, true);
engine.InjectObject(this);

Expand Down
16 changes: 14 additions & 2 deletions src/oscript/Web/WebRequestContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This Source Code Form is subject to the terms of the
using OneScript.StandardLibrary.Collections;
using OneScript.StandardLibrary.Text;
using oscript.Web.Multipart;
using ScriptEngine;
using ScriptEngine.Machine;
using ScriptEngine.Machine.Contexts;

Expand All @@ -27,8 +28,18 @@ public sealed class WebRequestContext : AutoContext<WebRequestContext>, IDisposa

private FileBackingStream _postBody;

public WebRequestContext()
private readonly int _postBodyInMemoryMaxBytes;

public WebRequestContext() : this(BinaryDataConfigurationDefaults.InMemoryMaxBytes)
{
}

public WebRequestContext(int postBodyInMemoryMaxBytes)
{
if (postBodyInMemoryMaxBytes <= 0)
throw new ArgumentOutOfRangeException(nameof(postBodyInMemoryMaxBytes),
"Лимит памяти для тела POST-запроса должен быть положительным числом байт.");
_postBodyInMemoryMaxBytes = postBodyInMemoryMaxBytes;
var get = Environment.GetEnvironmentVariable("QUERY_STRING");
Comment thread
coderabbitai[bot] marked this conversation as resolved.
if (get != null) FillGetMap(get);

Expand Down Expand Up @@ -68,7 +79,8 @@ private void ProcessPostData()
var type = Environment.GetEnvironmentVariable("CONTENT_TYPE");

using var stdin = Console.OpenStandardInput();
var dest = new FileBackingStream(FileBackingConstants.DEFAULT_MEMORY_LIMIT, len);
var initialCapacity = Math.Min(len, _postBodyInMemoryMaxBytes);
var dest = new FileBackingStream(_postBodyInMemoryMaxBytes, initialCapacity);
stdin.CopyTo(dest);
dest.Position = 0;

Expand Down