1
0
mirror of https://github.com/Bayselonarrend/OpenIntegrations.git synced 2025-11-27 22:18:36 +02:00
Files
OpenIntegrations/src/en/OInt/tools/Modules/internal/Classes/OPI_HTTPClient.os
Vitaly the Alpaca (bot) 515a31dd45 Main build (Jenkins)
2025-05-06 23:00:33 +03:00

2367 lines
62 KiB
Plaintext
Vendored

// OneScript: ./OInt/tools/Modules/internal/Classes/OPI_HTTPClient.os
// Lib: HTTP-client
// CLI: none
// MIT License
// Copyright (c) 2023-2025 Anton Tsitavets
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// https://github.com/Bayselonarrend/OpenIntegrations
// BSLLS:Typo-off
// BSLLS:LatinAndCyrillicSymbolInWord-off
// BSLLS:IncorrectLineBreak-off
// BSLLS:NumberOfOptionalParams-off
// BSLLS:UsingServiceTag-off
// BSLLS:LineLength-off
// BSLLS:ExportVariables-off
// BSLLS:OneStatementPerLine-off
//@skip-check module-structure-top-region
//@skip-check module-structure-method-in-regions
//@skip-check wrong-string-literal-content
//@skip-check method-too-many-params
//@skip-check constructor-function-return-section
//@skip-check doc-comment-collection-item-type
//@skip-check object-module-export-variable
// #If Not Client Then
#Region Variables
// Processor
Var Initialized; // Flag indicating that the mandatory Initialize() function was called
Var Error Export; // Execution error flag to skip remaining actions in the chain
Var Log; // Array of messages about actions within the processing
// Request
Var Request; // HTTPRequest object
Var Connection; // HTTPConnection object
Var Settings; // Additional settings structure
Var RequestURL; // Request URL
Var RequestServer; // Server from the request URL
Var RequestPort; // Port from the request URL or default
Var RequestAdress; // Path from the request URL
Var RequestAdressFull; // Path with parameters and section from the request URL
Var RequestSection; // Section in the URL if present
Var RequestProtected; // HTTPS usage flag
Var RequestDomain; // Domain from the request URL
Var RequestMethod; // HTTP method used
Var RequestURLParams; // URL parameters structure
Var RequestBody; // Request body data
Var RequestHeaders; // Request headers mapping
Var RequestUser; // User for basic authorization
Var RequestPassword; // Password for basic authorization
Var RequestTimeout; // Request timeout
Var RequestProxy; // Request proxy settings
Var RequestOutputFile; // Path to the file for saving the request result
Var RequestBodyFile; // Path to the file with the request body
Var RequestBodyStream; // Request body stream
Var RequestDataWriter; // Request body data writing
Var RequestDataType; // MIME type for Content-Type
Var RequestTypeSetManualy; // Flag to disable automatic Content-Type detection
Var BodyTemporaryFile; // Flag to delete the body file if it was created automatically
// AWS
Var AWS4Using; // Flag to use AWS4 authorization
Var AWS4Data; // Credentials structure
// Bearer
Var Bearer; // Bearer token
// Response
Var Response; // HTTPResponse object
Var ResponseStatusCode; // Response status code
Var ResponseBody; // Response body data
Var ResponseHeaders; // Response headers mapping
// Multipart
Var Multipart; // Flag indicating the body is set in Multipart format
Var Boundary; // Boundary for separating body parts
Var LineSeparator; // Body line separator
#EndRegion
#Region Public
#Region Initialization
// Initialize !NOCLI
// Initializes a new empty request
//
// Note
// The function must be called first when creating a new processor object
//
// Parameters:
// URL - String - URL address for request
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function Initialize(Val URL = "") Export
Log = New Array;
AddLog("Initialize: setting of default values");
Initialized = True;
Error = False;
RequestURLParams = New Array;
RequestBody = Undefined;
RequestHeaders = New Map;
RequestTimeout = 3600;
RequestTypeSetManualy = False;
BodyTemporaryFile = False;
AWS4Using = False;
ResponseStatusCode = 0;
ResponseBody = Undefined;
ResponseHeaders = New Map;
Multipart = False;
SetDefaultSettings();
SetURL(URL);
Return ЭтотОбъект;
EndFunction
// Set URL !NOCLI
// Sets the new request URL
//
// Parameters:
// URL - String - Request URL - url
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetURL(Val URL) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If ValueIsFilled(URL) Then
AddLog("SetURL: setting the value");
OPI_TypeConversion.GetLine(URL);
OPI_Tools.RestoreEscapeSequences(URL);
If GetSetting("URLencoding") Then
EncodeURLInURL(URL);
EndIf;
RequestURL = URL;
Else
AddLog("SetURL: URL is empty - skip");
EndIf;
Return SplitURL();
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set URL params !NOCLI
// Sets a collection of URL parameters
//
// Parameters:
// Value - Arbitrary - Structure or map of URL parameters - params
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetURLParams(Val Value) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not ValueIsFilled(Value) Then Value = New Structure; EndIf;
AddLog("SetURLParams: parameter setting");
ErrorText = "SetURLParams: the passed parameters are not a key/value collection";
OPI_TypeConversion.GetKeyValueCollection(Value, ErrorText);
RequestURLParams = Value;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set response file
// Sets the file path to save the query result
//
// Parameters:
// Value - String - File path - filepath
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetResponseFile(Val Value) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not ValueIsFilled(Value) Then
RequestOutputFile = Undefined;
AddLog("SetResponseFile: response file not specified - skip");
Return ЭтотОбъект;
EndIf;
AddLog("SetResponseFile: setting the value");
OPI_TypeConversion.GetLine(Value);
RequestOutputFile = Value;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set data type !NOCLI
// Sets the Content-Type of the request
//
// Note
// If the data type is not set manually, it will be matched during the process of setting the request body
//
// Parameters:
// Value - String - ContentType header value - type
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetDataType(Val Value) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
AddLog("SetDataType: setting the value");
OPI_TypeConversion.GetLine(Value);
RequestDataType = Value;
RequestTypeSetManualy = True;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Get log !NOCLI
// Gets the execution log
//
// Parameters:
// AsString - Boolean - Return the log as a string - string
//
// Returns:
// String, Array - Execution log
Function GetLog(Val AsString = False) Export
OPI_TypeConversion.GetBoolean(AsString);
If Not ValueIsFilled(Log) Then
Return ?(AsString, "" , New Array);
Else
Return ?(AsString, StrConcat(Log, Chars.LF), Log);
EndIf;
EndFunction
#EndRegion
#Region Settings
// Use encoding !NOCLI
// Sets the encoding of the request body
//
// Note
// UTF-8 is used by default
//
// Parameters:
// Encoding - String - Encoding name - enc
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function UseEncoding(Val Encoding) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
AddLog("UseEncoding: setting the value");
OPI_TypeConversion.GetLine(Encoding);
SetSetting("EncodeRequestBody", Encoding);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Use Gzip compression !NOCLI
// Enables or disables the header for receiving data from the server in compressed form
//
// Note
// By default, the response from the server is requested with gzip compression
//
// Parameters:
// Flag - Boolean - Flag for gzip using - gzip
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function UseGzipCompression(Val Flag) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
AddLog("UseGzipCompression: setting the value");
OPI_TypeConversion.GetBoolean(Flag);
SetSetting("gzip", Flag);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
#EndRegion
#Region BodySet
// Set binary body !NOCLI
// Sets the request body from binary data, file or string (with conversion to binary data)
//
// Parameters:
// Data - String, BinaryData - File, string, or request body data - data
// SetIfEmpty - Boolean - Sets the body even when empty data is passed - empty
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetBinaryBody(Val Data, Val SetIfEmpty = False) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
CancelMultipartBody();
If Not RequestTypeSetManualy Then
RequestDataType = "application/octet-stream";
EndIf;
OPI_TypeConversion.GetBinaryData(Data, True, False);
OPI_TypeConversion.GetBoolean(SetIfEmpty);
Data = ?(Data = Undefined, ПолучитьДвоичныеДанныеИзСтроки(""), Data);
IsData = Data.Size() > 0;
If IsData Or SetIfEmpty Then
If Not IsData Then
Data = ПолучитьДвоичныеДанныеИзСтроки("");
EndIf;
AddLog("SetBinaryBody: beginning of body setting");
SetBodyFromBinary(Data);
AddLog(StrTemplate("SetBinaryBody: body set, size %1", RequestBody.Size()));
Else
AddLog("SetBinaryBody: an empty body has been passed - skip");
EndIf;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set string body !NOCLI
// Sets the body of the request from the string
//
// Parameters:
// Data - String - Request body data - data
// WriteBOM - Boolean - True > BOM will be added - bom
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetStringBody(Val Data, Val WriteBOM = False) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
CancelMultipartBody();
If Not ValueIsFilled(Data) Then
AddLog("SetStringBody: no data - skip");
Return ЭтотОбъект;
EndIf;
Encoding = GetSetting("EncodeRequestBody");
OPI_TypeConversion.GetLine(Encoding);
If Not RequestTypeSetManualy Then
RequestDataType = StrTemplate("text/plain; charset=%1", Encoding);
EndIf;
AddLog("SetStringBody: beginning of body setting");
SetBodyFromString(Data, WriteBOM);
AddLog(StrTemplate("SetStringBody: body set, size %1", RequestBody.Size()));
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set JSON body !NOCLI
// Sets the body in JSON format from a suitable collection or string
//
// Parameters:
// Data - Arbitrary - String or collection to convert to JSON - data
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetJsonBody(Val Data) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
CancelMultipartBody();
If Not ValueIsFilled(Data) Then
AddLog("SetJsonBody: no data - skip");
Return ЭтотОбъект;
EndIf;
If Not RequestTypeSetManualy Then
RequestDataType = "application/json; charset=utf-8";
EndIf;
AddLog("SetJsonBody: beginning of body setting");
If Not TypeOf(Data) = Type("BinaryData") Then
OPI_TypeConversion.GetCollection(Data);
EndIf;
SetBodyFromString(Data);
AddLog(StrTemplate("SetJsonBody: body set, size %1", RequestBody.Size()));
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Set Form body !NOCLI
// Sets the body to x-www-form-urlencoded from a collection of field values
//
// Parameters:
// Data - Arbitrary - KeyValue collection with form parameters - data
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - Set Form body
Function SetFormBody(Val Data) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
CancelMultipartBody();
If Not ValueIsFilled(Data) Then
AddLog("SetFormBody: no data - skip");
Return ЭтотОбъект;
EndIf;
If Not RequestTypeSetManualy Then
RequestDataType = "application/x-www-form-urlencoded; charset=utf-8";
EndIf;
AddLog("SetFormBody: beginning of body setting");
OPI_TypeConversion.GetCollection(Data);
If TypeOf(Data) = Type("Array") Then
Data = Data[0];
If Not TypeOf(Data) = Type("BinaryData") Then
OPI_TypeConversion.GetLine(Data);
EndIf;
Else
Data = RequestParametersToString(Data);
EndIf;
SetBodyFromString(Data);
AddLog(StrTemplate("SetFormBody: body set, size %1", RequestBody.Size()));
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Start Multipart body !NOCLI
// Initializes writing data to the body in multipart format
//
// Note
// The `AddMultipartFormDataFile` and `AddMultipartFormDataField` methods are used for further body formation
//
// Parameters:
// UseFile - Boolean - True > use a temporary file, False > form a body in memory - file
// View - String - Multipart data type: form data, related - type
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function StartMultipartBody(UseFile = True, Val View = "form-data") Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
OPI_TypeConversion.GetBoolean(UseFile);
OPI_TypeConversion.GetLine(View);
Multipart = True;
Boundary = StrReplace(String(New UUID), "-", "");
LineSeparator = Chars.CR + Chars.LF;
Encoding = GetSetting("EncodeRequestBody");
RequestDataType = StrTemplate("multipart/%1; boundary=%2", View, Boundary);
If UseFile Then
AddLog("StartMultipartBody: creating a temporary file");
// BSLLS:MissingTemporaryFileDeletion-off
RequestBodyFile = GetTempFileName();
// BSLLS:MissingTemporaryFileDeletion-on
BodyTemporaryFile = True;
RequestDataWriter = New DataWriter(RequestBodyFile
, Encoding
, ByteOrder.LittleEndian
, ""
, False
, ""
, False);
Else
AddLog("StartMultipartBody: creating a stream in memory");
RequestBodyStream = New MemoryStream();
RequestDataWriter = New DataWriter(RequestBodyStream
, Encoding
, ByteOrder.LittleEndian
, ""
, ""
, False);
EndIf;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add Multipart file !NOCLI
// Adds a file block to the multipart/form-data body
//
// Note
// The Multipart record must first be initialized using the `StartMultipartBody` function
//
// Parameters:
// FieldName - String - Form field name - field
// FileName - String - File name with extension - filename
// Data - BinaryData, String - File data to be written - data
// DataType - String - MIME type of data - mime
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddMultipartFormDataFile(Val FieldName, Val FileName, Val Data, Val DataType = "") Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not Multipart Then Return Error("AddMultipartFile: Multipart record not initialized"); EndIf;
OPI_TypeConversion.GetBinaryData(Data);
AddLog("AddMultipartFile: writing the block header");
Header = StrTemplate("Content-Disposition: form-data; name=""%1""; filename=""%2""", FieldName, FileName);
RequestDataWriter.WriteLine("--" + Boundary + LineSeparator);
RequestDataWriter.WriteLine(Header);
RequestDataWriter.WriteLine(LineSeparator);
If ValueIsFilled(DataType) Then
RequestDataWriter.WriteLine("Content-Type: " + DataType);
EndIf;
RequestDataWriter.WriteLine(LineSeparator);
RequestDataWriter.WriteLine(LineSeparator);
AddLog("AddMultipartFile: data writing");
WriteBinaryData(RequestDataWriter, Data);
RequestDataWriter.WriteLine(LineSeparator);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add Multipart field !NOCLI
// Adds a form field to the multipart/form-data body
//
// Note
// The Multipart record must first be initialized using the `StartMultipartBody` function
//
// Parameters:
// FieldName - String - Form field name - field
// Value - Arbitrary - Field value - data
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddMultipartFormDataField(Val FieldName, Val Value) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not Multipart Then Return Error("AddMultipartField: multipart record not initialized"); EndIf;
ValeType = TypeOf(Value);
AddLog("AddMultipartField: writing the block header");
Header = StrTemplate("Content-Disposition: form-data; name=""%1""", FieldName);
RequestDataWriter.WriteLine("--" + boundary + LineSeparator);
RequestDataWriter.WriteLine(Header);
RequestDataWriter.WriteLine(LineSeparator);
RequestDataWriter.WriteLine(LineSeparator);
AddLog("AddMultipartField: data writing");
If ValeType = Type("Boolean") Then
Value = ?(Value, "true", "false");
RequestDataWriter.WriteLine(Value);
ElsIf ValeType = Type("BinaryData") Then
WriteBinaryData(RequestDataWriter, Value);
Else
OPI_TypeConversion.GetLine(Value);
RequestDataWriter.WriteLine(Value);
EndIf;
RequestDataWriter.WriteLine(LineSeparator);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add data as Related
// Adds data to the multipart/related body
//
// Note
// The Multipart record must first be initialized using the `StartMultipartBody` function
//
// Parameters:
// Data - Arbitrary - Data to be written - data
// DataType - String - MIME type of data - mime
// ContentID - String - Content ID, if required - cid
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddDataAsRelated(Val Data, Val DataType, Val ContentID = "") Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not Multipart Then Return Error("AddFileAsRelated: multipart record not initialized"); EndIf;
OPI_TypeConversion.GetLine(DataType);
OPI_TypeConversion.GetLine(ContentID);
OPI_TypeConversion.GetBinaryData(Data, True, False);
AddLog("AddFileAsRelated: writing the block header");
RequestDataWriter.WriteLine("--" + Boundary + LineSeparator);
RequestDataWriter.WriteLine("Content-Type: " + DataType);
If ValueIsFilled(ContentID) Then
RequestDataWriter.WriteLine("Content-ID: " + ContentID);
EndIf;
RequestDataWriter.WriteLine(LineSeparator);
RequestDataWriter.WriteLine(LineSeparator);
AddLog("AddFileAsRelated: data writing");
WriteBinaryData(RequestDataWriter, Data);
RequestDataWriter.WriteLine(LineSeparator);
RequestDataWriter.WriteLine(LineSeparator);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
#EndRegion
#Region HeadersSetting
// Set headers !NOCLI
// Sets a collection of query headers
//
// Note
// `FullReplace` also clears headers previously set by other methods (e.g., authorization headers)
//
// Parameters:
// Value - Arbitrary - Structure or map of request headers - headers
// FullReplace - Boolean - Clears all existing headers before setting up - replace
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function SetHeaders(Val Value, Val FullReplace = False) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not ValueIsFilled(Value) Then Value = New Map; EndIf;
ErrorText = "SetHeaders: the passed parameters are not a key/value collection";
OPI_TypeConversion.GetKeyValueCollection(Value, ErrorText);
OPI_TypeConversion.GetBoolean(FullReplace);
AddLog("SetHeaders: query header setting");
If FullReplace Then
RequestHeaders = Value;
Else
For Each Title In Value Do
RequestHeaders.Insert(Title.Key, Title.Value);
EndDo;
EndIf;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add header !NOCLI
// Adds a header to the request header set
//
// Parameters:
// Name - String - Header key - header
// Value - String - Header value - value
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddHeader(Val Name, Val Value) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
If Not ValueIsFilled(Value) Then Value = New Map; EndIf;
OPI_TypeConversion.GetLine(Name);
OPI_TypeConversion.GetLine(Value);
AddLog("AddHeader: header setting");
RequestHeaders.Insert(Name, Value);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
#EndRegion
#Region Authorization
// Add Basic authorization !NOCLI
// Adds standard authorization by username and password
//
// Parameters:
// User - String - Users name - user
// Password - String - Password - pwd
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddBasicAuthorization(Val User, Val Password) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
OPI_TypeConversion.GetLine(User);
OPI_TypeConversion.GetLine(Password);
RequestUser = User;
RequestPassword = Password;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add Bearer authorization
// Adds a request header for Bearer authorization
//
// Parameters:
// Token - String - Bearer token value - token
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddBearerAuthorization(Val Token) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
OPI_TypeConversion.GetLine(Token);
Bearer = Token;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Add AWS4 authorization !NOCLI
// Adds data for AWS4 authorization
//
// Parameters:
// AccessKey - String - Access key for authorization - access
// SecretKey - String - Secret key for authorization - secret
// Region - String - Service region - region
// Service - String - Type of service, if different from s3 - service
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function AddAWS4Authorization(Val AccessKey, Val SecretKey, Val Region, Val Service = "s3") Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
String_ = "String";
AWS4Using = True;
AWS4Data = New Structure;
OPI_Tools.AddField("AccessKey", AccessKey, String_, AWS4Data);
OPI_Tools.AddField("SecretKey", SecretKey, String_, AWS4Data);
OPI_Tools.AddField("Region" , Region , String_, AWS4Data);
OPI_Tools.AddField("Service" , Service , String_, AWS4Data);
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
#EndRegion
#Region RequestProcessing
// Process request !NOCLI
// Creates a request based on the entered data with or without execution
//
// Note
// `ExecuteRequest=False` can be used to get ready^^
// HTTPConnection and HTTPConnection objects without executing them. See `ReturnRequest` and `ReturnConnection`.
//
// Parameters:
// Method - String - Request HTTP method - method
// Start - Boolean - Executes the request immediately after it is generated - run
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function ProcessRequest(Val Method, Val Start = True) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
OPI_TypeConversion.GetLine(Method);
OPI_TypeConversion.GetBoolean(Start);
RequestMethod = Method;
AddLog("ProcessRequest: creation of HTTPRequest object");
If FormRequest().Error Then Return ЭтотОбъект; EndIf;
AddLog("ProcessRequest: place the body in the HTTPRequest object");
If SetRequestBody().Error Then Return ЭтотОбъект; EndIf;
CompleteHeaders();
If Start Then
ExecuteMethod();
EndIf;
Return ЭтотОбъект;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Execute request !NOCLI
// Executes the request if it has been generated or set previously
//
// Parameters:
// Method - String - Request HTTP method - method
//
// Returns:
// DataProcessorObject.OPI_HTTPClient - This processor object
Function ExecuteRequest(Val Method) Export
Try
If StopExecution() Then Return ЭтотОбъект; EndIf;
OPI_TypeConversion.GetLine(Method);
RequestMethod = Method;
AddLog("ExecuteRequest: executing");
Return ExecuteMethod();
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Return request !NOCLI
// Returns the object of the current HTTP request
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the request if there were errors in it - force
//
// Returns:
// DataProcessorObject.OPI_HTTPClient, HTTPRequest, Undefined - The request or the same processing object
Function ReturnRequest(Forced = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution() And Not Forced Then Return ЭтотОбъект; EndIf;
Return Request;
EndFunction
// Return connection !NOCLI
// Returns the object of the current HTTP connection
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the connection if there were errors in it - force
//
// Returns:
// DataProcessorObject.OPI_HTTPClient, HTTPConnection, Undefined - Connection or the same processor object
Function ReturnConnection(Forced = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution() And Not Forced Then Return ЭтотОбъект; EndIf;
Return Connection;
EndFunction
#EndRegion
#Region ResponseReceiving
// Return response !NOCLI
// Returns the object of the current HTTP response
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the response if there were errors in it - force
// ExceptionOnError - Boolean - Causes an exception with a log if there were errors during processing - ex
//
// Returns:
// DataProcessorObject.OPI_HTTPClient, HTTPResponse, Undefined - The response or the same processing object
Function ReturnResponse(Val Forced = False, Val ExceptionOnError = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution(ExceptionOnError) And Not Forced Then Return ЭтотОбъект; EndIf;
Return Response;
EndFunction
// Return response as JSON object !NOCLI
// Returns the response body as a collection from JSON
//
// Parameters:
// ToMap - Boolean - Use map instead of structure - map
// ExceptionOnError - Boolean - Causes an exception with a log if there were errors during processing - ex
//
// Returns:
// Arbitrary - The response or the same processing object
Function ReturnResponseAsJSONObject(Val ToMap = True, Val ExceptionOnError = False) Export
If StopExecution(ExceptionOnError) Then Return ЭтотОбъект; EndIf;
Try
OPI_TypeConversion.GetBoolean(ToMap);
ResponseBody = GetResponseBody();
Try
If ResponseBody.Size() > 0 Then
JSON = OPI_Tools.JsonToStructure(ResponseBody, ToMap);
Else
JSON = New Map;
EndIf;
Except
JSON = ResponseBody;
EndTry;
Return JSON;
Except
Return Error(DetailErrorDescription(ErrorInfo()));
EndTry;
EndFunction
// Return response as binary data
// Returns the response body as binary data
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the response if there were errors in it - force
// ExceptionOnError - Boolean - Causes an exception with a log if there were errors during processing - ex
//
// Returns:
// Arbitrary - The response or the same processing object
Function ReturnResponseAsBinaryData(Val Forced = False, Val ExceptionOnError = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution(ExceptionOnError) And Not Forced Then Return ЭтотОбъект; EndIf;
BodyAsString = GetResponseBody();
Return BodyAsString;
EndFunction
// Return response as string !NOCLI
// Returns the body of the response as a string
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the response if there were errors in it - force
// ExceptionOnError - Boolean - Causes an exception with a log if there were errors during processing - ex
//
// Returns:
// Arbitrary - The response or the same processing object
Function ReturnResponseAsString(Val Forced = False, Val ExceptionOnError = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution(ExceptionOnError) And Not Forced Then Return ЭтотОбъект; EndIf;
BodyAsString = ПолучитьСтрокуИзДвоичныхДанных(GetResponseBody());
Return BodyAsString;
EndFunction
// Return response filename !NOCLI
// Returns the path to the response body file
//
// Parameters:
// Forced - Boolean - False > The processor object will be returned instead of the response if there were errors in it - force
// ExceptionOnError - Boolean - Causes an exception with a log if there were errors during processing - ex
//
// Returns:
// Arbitrary - The response or the same processing object
Function ReturnResponseFilename(Val Forced = False, Val ExceptionOnError = False) Export
OPI_TypeConversion.GetBoolean(Forced);
If StopExecution(ExceptionOnError) And Not Forced Then Return ЭтотОбъект; EndIf;
BodyFileName = Response.GetBodyFileName();
Return BodyFileName;
EndFunction
#EndRegion
#EndRegion
#Region Private
#Region HTTP
Function ConvertParameterToString(Val Value)
EncodeURL = GetSetting("URLencoding");
If TypeOf(Value) = Type("Array") Then
For N = 0 To Value.UBound() Do
Value[N] = ConvertParameterToString(Value[N]);
EndDo;
Value = StrConcat(Value, ",");
If EncodeURL Then
Value = EncodeString(Value, StringEncodingMethod.URLInURLEncoding);
EndIf;
Value = "[" + Value + "]";
ElsIf TypeOf(Value) = Type("Map") Or TypeOf(Value) = Type("Structure") Then
JSONParameters = New JSONWriterSettings(JSONLineBreak.None, "");
JSONWriter = New JSONWriter;
JSONWriter.SetString(JSONParameters);
WriteJSON(JSONWriter, Value);
Value = JSONWriter.Close();
ElsIf TypeOf(Value) = Type("Boolean") Then
Value = ?(Value, "true", "false");
Else
OPI_TypeConversion.GetLine(Value);
If EncodeURL Then
Value = EncodeString(Value, StringEncodingMethod.URLencoding);
EndIf;
EndIf;
Return Value;
EndFunction
Function SetBodyFromBinary(Val Value)
OPI_TypeConversion.GetBinaryData(Value, True, False);
RequestBody = Value;
Return ЭтотОбъект;
EndFunction
Function SetBodyFromString(Val Value, Val WriteBOM = False)
If TypeOf(Value) = Type("BinaryData") Then
RequestBody = Value;
Else
Encoding = GetSetting("EncodeRequestBody");
OPI_TypeConversion.GetLine(Value);
OPI_TypeConversion.GetBoolean(WriteBOM);
RequestBody = ПолучитьДвоичныеДанныеИзСтроки(Value, Encoding, WriteBOM);
EndIf;
Return ЭтотОбъект;
EndFunction
Function SplitURL()
AddLog("SplitURL: splitting a request into component parts");
URL = RequestURL;
RequestProtected = Not StrStartsWith(RequestURL, "http://");
AddLog("SplitURL: Secure = " + String(RequestProtected));
URL = StrReplace(URL, "https://", "");
URL = StrReplace(URL, "http://" , "");
Section = StrFind(URL, "#");
If Section > 0 Then
RequestSection = Right(URL, StrLen(URL) - Section + 1);
AddLog("SplitURL: Section = " + RequestSection);
URL = Left(URL, Section - 1);
EndIf;
If StrFind(URL, "/") = 0 Then
RequestAdress = "";
RequestDomain = URL;
Else
RequestAdress = Right(URL, StrLen(URL) - StrFind(URL, "/", SearchDirection.FromBegin) + 1);
RequestDomain = Left(URL, StrFind(URL, "/", SearchDirection.FromBegin) - 1);
EndIf;
AddLog("SplitURL: Address = " + RequestAdress);
AddLog("SplitURL: Domain = " + RequestDomain);
If StrFind(RequestDomain, ":") <> 0 Then
HostPort = StrSplit(RequestDomain, ":");
RequestDomain = HostPort[0];
RequestPort = HostPort[1];
OPI_TypeConversion.GetNumber(RequestPort);
Else
RequestPort = ?(RequestProtected, 443, 80);
EndIf;
AddLog("SplitURL: Port = " + OPI_Tools.NumberToString(RequestPort));
If OPI_Tools.IsOneScript() And RequestProtected Then
RequestServer = "https://" + RequestDomain;
Else
RequestServer = RequestDomain;
EndIf;
AddLog("SplitURL: Host = " + RequestServer);
Return ЭтотОбъект;
EndFunction
Function FormRequest()
If Not ValueIsFilled(RequestURL) Then
Return Error("URL is not set");
EndIf;
AddLog("FormRequest: Adding parameters");
CompleteURLWithParameters();
AddLog("FormRequest: Creating a request object");
CreateRequest();
AddLog("FormRequest: Creating a connection object");
CreateConnection();
Return ЭтотОбъект;
EndFunction
Function CreateRequest()
Headers = GetDefaultHeaders();
Request = New HTTPRequest(RequestAdressFull, Headers);
Return ЭтотОбъект;
EndFunction
Function CreateConnection()
If Not ValueIsFilled(RequestPort) Then
RequestPort = ?(RequestProtected, 443, 80);
EndIf;
If RequestProtected Then
If OPI_Tools.IsOneScript() Then
Connection = New HTTPConnection(RequestServer
, RequestPort
, RequestUser
, RequestPassword
, RequestProxy
, RequestTimeout);
Else
SSL = New OpenSSLSecureConnection;
Connection = New HTTPConnection(RequestServer
, RequestPort
, RequestUser
, RequestPassword
, RequestProxy
, RequestTimeout
, SSL);
EndIf;
Else
Connection = New HTTPConnection(RequestServer
, RequestPort
, RequestUser
, RequestPassword
, RequestProxy
, RequestTimeout);
EndIf;
Return ЭтотОбъект;
EndFunction
Function CompleteURLWithParameters()
If StrEndsWith(RequestAdress, "?") Or Not ValueIsFilled(RequestURLParams) Then
FirstSymbol = "";
ElsIf StrFind(RequestAdress, "?") <> 0 Then
FirstSymbol = "&";
Else
FirstSymbol = "?";
EndIf;
RequestAdressFull = RequestAdress + FirstSymbol + RequestParametersToString(RequestURLParams) + RequestSection;
Return ЭтотОбъект;
EndFunction
Function RequestParametersToString(Val Parameters)
If Not ValueIsFilled(Parameters) Then
Return "";
EndIf;
AddLog("RequestParametersToString: Retrieve collection KeyValue");
OPI_TypeConversion.GetKeyValueCollection(Parameters);
ParameterString = "";
AddLog("RequestParametersToString: Adding parameters");
For Each Parameter In Parameters Do
CurrentValue = Parameter.Value;
CurrentKey = Parameter.Key;
If Not TypeOf(CurrentValue) = Type("Array") Or Not GetSetting("SplitArrayParams") Then
ParameterValue = ConvertParameterToString(CurrentValue);
ParameterString = ParameterString + Parameter.Key + "=" + ParameterValue + "&";
Else
ParameterValue = SplitArrayAsURLParameters(CurrentKey, CurrentValue);
ParameterString = ParameterString + ParameterValue + "&";
EndIf;
EndDo;
ParameterString = Left(ParameterString, StrLen(ParameterString) - 1);
Return ParameterString;
EndFunction
Function SplitArrayAsURLParameters(Val Key, Val Value)
KeyArray = Key + "=";
For N = 0 To Value.UBound() Do
CurrentValue = Value[N];
OPI_TypeConversion.GetLine(CurrentValue);
If GetSetting("URLencoding") Then
CurrentValue = EncodeString(CurrentValue, StringEncodingMethod.URLInURLEncoding);
EndIf;
Value.Set(N, KeyArray + CurrentValue);
EndDo;
ParameterString = StrConcat(Value, "&");
Return ParameterString;
EndFunction
Function GetDefaultHeaders()
Headers = New Map;
Headers.Insert("Accept" , "*/*");
Headers.Insert("Connection" , "keep-alive");
Headers.Insert("Accept-Charset" , "utf-8");
Return Headers;
EndFunction
Function CompleteHeaders()
If Request.Headers.Get("Content-Length") = Undefined Then
AddLog("CompleteHeaders: Content-Length setting");
If RequestBodyFile = Undefined Then
If RequestBody = Undefined Then
BodySize = 0;
Else
BodySize = RequestBody.Size();
EndIf;
Else
BodyFile = New File(RequestBodyFile);
BodySize = BodyFile.Size();
EndIf;
OPI_TypeConversion.GetLine(BodySize);
Request.Headers.Insert("Content-Length" , BodySize);
EndIf;
If AWS4Using Then
AddLog("CompleteHeaders: generating AWS4 Authorization Header");
AddAWS4();
EndIf;
If ValueIsFilled(Bearer) Then
AddLog("CompleteHeaders: generating Bearer Authorization Header");
Request.Headers.Insert("Authorization", StrTemplate("Bearer %1", Bearer));
EndIf;
If GetSetting("gzip") Then
AddLog("CompleteHeaders: setting the gzip header");
Request.Headers.Insert("Accept-Encoding", "gzip");
EndIf;
If ValueIsFilled(RequestDataType) Then
Request.Headers.Insert("Content-Type", RequestDataType);
EndIf;
If TypeOf(RequestHeaders) = Type("Map") Then
For Each Title In RequestHeaders Do
Request.Headers.Insert(Title.Key, Title.Value);
EndDo;
EndIf;
Return ЭтотОбъект;
EndFunction
Function SetRequestBody()
If Multipart Then
EndMultipartBody();
EndIf;
If ValueIsFilled(RequestBodyFile) Then
Request.SetBodyFileName(RequestBodyFile);
Else
If TypeOf(RequestBody) = Type("BinaryData") Then
Request.SetBodyFromBinary(RequestBody);
EndIf;
EndIf;
Return ЭтотОбъект;
EndFunction
Function ExecuteMethod(Val RedirectCount = 0)
If ValueIsFilled(RequestOutputFile) Then
Response = Connection.CallHTTPMethod(RequestMethod, Request, RequestOutputFile);
Else
Response = Connection.CallHTTPMethod(RequestMethod, Request);
EndIf;
If ThisIsRedirection(Response) Then
If RedirectCount = 5 Then
Error("ExecuteMethod: the number of redirects has been exceeded");
Return ЭтотОбъект;
EndIf;
URL = Response.Headers["Location"];
SetURL(URL);
CreateConnection();
Request.ResourceAddress = RequestAdress;
ExecuteMethod(RedirectCount + 1);
EndIf;
Return ЭтотОбъект;
EndFunction
Function GetResponseBody()
NeedsUnpacking = False;
For Each ResponseHeader In Response.Headers Do
HeaderKey = ResponseHeader.Key;
HeaderValue = ResponseHeader.Value;
If Lower(HeaderKey) = "content-encoding" Then
If Lower(HeaderValue) = "gzip" Then
NeedsUnpacking = True;
Break;
EndIf;
EndIf;
EndDo;
If NeedsUnpacking Then
Data = UnpackResponse(Response);
Else
Data = GetResponseBodyAsBinaryData();
EndIf;
Data = ?(TypeOf(Data) = Type("HTTPResponse"), GetResponseBodyAsBinaryData(), Data);
If Not TypeOf(Data) = Type("BinaryData") Then
OPI_TypeConversion.GetBinaryData(Data);
EndIf;
Return Data;
EndFunction
Function ThisIsRedirection(Val Response)
Redirection = 300;
RequestError = 400;
ThisIsRedirection = Response.StatusCode >= Redirection And Response.StatusCode < RequestError And ValueIsFilled(
Response.Headers["Location"]);
Return ThisIsRedirection;
EndFunction
Function GetResponseBodyAsBinaryData()
BodyStream = Response.GetBodyAsStream();
If BodyStream = Undefined Then
Return ПолучитьДвоичныеДанныеИзСтроки("");
EndIf;
DataReader = New DataReader(BodyStream);
ReadingResult = DataReader.Read();
Data = ReadingResult.GetBinaryData();
DataReader.Close();
BodyStream.Close();
Return Data;
EndFunction
Function GetResponseBodyAsBinaryOrPath()
BodyFileName = Response.GetBodyFileName();
If Not ValueIsFilled(BodyFileName) Then
Return GetResponseBodyAsBinaryData();
Else
Return BodyFileName;
EndIf;
EndFunction
Function GetRequestBodyAsBinaryData()
If ValueIsFilled(RequestBodyFile) Then
Data = New BinaryData(RequestBodyFile);
Else
Data = RequestBody;
EndIf;
If Data = Undefined Then
Data = ПолучитьДвоичныеДанныеИзСтроки("");
EndIf;
Return Data;
EndFunction
Procedure CancelMultipartBody()
If Not Multipart Then
Return;
EndIf;
AddLog("CancelMultipartBody: Deleting recorded data");
Multipart = False;
Try
RequestDataWriter.Close();
Except
AddLog("CancelMultipartBody: Could not close the writer. It may have already been closed");
EndTry;
If ValueIsFilled(RequestBodyFile) Then
Try
DeleteFiles(RequestBodyFile);
AddLog("CancelMultipartBody: The body file has been deleted");
Except
AddLog("CancelMultipartBody: Failed to delete the body file. It may have already been deleted");
EndTry;
Else
If TypeOf(RequestBodyStream) = Type("MemoryStream") Then
Try
RequestBodyStream.Close();
Except
AddLog("CancelMultipartBody: Failed to close the stream. It may have already been closed");
EndTry;
EndIf;
EndIf;
RequestDataWriter = Undefined;
RequestBodyFile = Undefined;
EndProcedure
Procedure WriteBinaryData(DataWriter, Val BinaryData)
ChunkSize = 268435456;
BytesRead = 0;
CurrentPosition = 0;
TotalSize = BinaryData.Size();
While BytesRead < TotalSize Do
DataReader = New DataReader(BinaryData);
BytesRead = DataReader.Skip(CurrentPosition);
Result = DataReader.Read(ChunkSize);
CurrentData = Result.GetBinaryData();
CurrentSize = CurrentData.Size();
If Not ValueIsFilled(CurrentData) Then
Break;
EndIf;
DataWriter.Write(CurrentData);
FreeObject(CurrentData);
RunGarbageCollection();
CurrentPosition = CurrentPosition + CurrentSize;
EndDo;
EndProcedure
Procedure EndMultipartBody()
Try
RequestDataWriter.WriteLine("--" + Boundary + "--" + LineSeparator);
RequestDataWriter.Close();
Except
AddLog("EndMultipartBody: Could not close the writer. It may have already been closed");
EndTry;
If TypeOf(RequestBodyStream) = Type("MemoryStream") Then
RequestBody = RequestBodyStream.CloseAndGetBinaryData();
EndIf;
EndProcedure
#EndRegion
#Region GZip
// Structure description at https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT
// Source: https://github.com/vbondarevsky/Connector
// Connector: convenient HTTP client for 1C:Enterprise 8
//
// Copyright 2017-2023 Vladimir Bondarevskiy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
// URL: https://github.com/vbondarevsky/Connector
// e-mail: vbondarevsky@gmail.com
// Version: 2.4.8
//
// Requirements: 1C platform version 8.3.10 and above
Function UnpackResponse(Response)
Try
Return ReadGZip(GetResponseBodyAsBinaryOrPath());
Except
Return Response;
EndTry;
EndFunction
Function ReadGZip(CompressedData)
GZipPrefixSize = 10;
GZipPostfixSize = 8;
SizeDD = ZipSizeDD();
SizeCDH = ZipSizeCDH();
SizeESD = ZipSizeEOCD();
SizeLFH = ZipSizeLFH();
DataReader = New DataReader(CompressedData);
DataReader.Skip(GZipPrefixSize);
CompressedDataSize = DataReader.SourceStream().Size() - GZipPrefixSize - GZipPostfixSize;
ZipStream = New MemoryStream(SizeLFH + CompressedDataSize + SizeDD + SizeCDH + SizeESD);
DataWriter = New DataWriter(ZipStream);
DataWriter.WriteBinaryDataBuffer(ZipLFH());
DataReader.CopyTo(DataWriter, CompressedDataSize);
DataWriter.Close();
DataWriter = New DataWriter(ZipStream);
CRC32 = DataReader.ReadInt32();
UncompressedDataSize = DataReader.ReadInt32();
DataReader.Close();
DataWriter.WriteBinaryDataBuffer(ZipDD(CRC32 , CompressedDataSize, UncompressedDataSize));
DataWriter.WriteBinaryDataBuffer(ZipCDH(CRC32, CompressedDataSize, UncompressedDataSize));
DataWriter.WriteBinaryDataBuffer(ZipEOCD(CompressedDataSize));
DataWriter.Close();
Return ReadZip(ZipStream);
EndFunction
Function ReadZip(CompressedData, ErrorText = Undefined)
Directory = GetTempFileName();
ReadingZip = New ZipFileReader(CompressedData);
FileName = ReadingZip.Items[0].Name;
Try
ReadingZip.Extract(ReadingZip.Items[0], Directory, ZIPRestoreFilePathsMode.DontRestore);
Except
// Ignore archive integrity check, just read the result
ErrorText = DetailErrorDescription(ErrorInfo());
EndTry;
ReadingZip.Close();
Result = New BinaryData(Directory + GetPathSeparator() + FileName);
DeleteFiles(Directory);
Return Result;
EndFunction
Function ZipSizeLFH()
Return 34;
EndFunction
Function ZipSizeDD()
Return 16;
EndFunction
Function ZipSizeCDH()
Return 50;
EndFunction
Function ZipSizeEOCD()
Return 22;
EndFunction
Function ZipLFH()
// Local file header
Buffer = New BinaryDataBuffer(ZipSizeLFH());
Buffer.WriteInt32(0, 67324752); // signature 0x04034b50
Buffer.WriteInt16(4, 20); // version
Buffer.WriteInt16(6, 10); // bit flags
Buffer.WriteInt16(8, 8); // compression method
Buffer.WriteInt16(10, 0); // time
Buffer.WriteInt16(12, 0); // date
Buffer.WriteInt32(14, 0); // crc-32
Buffer.WriteInt32(18, 0); // compressed size
Buffer.WriteInt32(22, 0); // uncompressed size
Buffer.WriteInt16(26, 4); // filename legth - "data"
Buffer.WriteInt16(28, 0); // extra field length
Buffer.Write(30, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", False));
Return Buffer;
EndFunction
Function ZipDD(CRC32, CompressedDataSize, UncompressedDataSize)
// Data descriptor
Buffer = New BinaryDataBuffer(ZipSizeDD());
Buffer.WriteInt32(0, 134695760);
Buffer.WriteInt32(4, CRC32);
Buffer.WriteInt32(8, CompressedDataSize);
Buffer.WriteInt32(12, UncompressedDataSize);
Return Buffer;
EndFunction
Function ZipCDH(CRC32, CompressedDataSize, UncompressedDataSize)
// Central directory header
Buffer = New BinaryDataBuffer(ZipSizeCDH());
Buffer.WriteInt32(0, 33639248); // signature 0x02014b50
Buffer.WriteInt16(4, 798); // version made by
Buffer.WriteInt16(6, 20); // version needed to extract
Buffer.WriteInt16(8, 10); // bit flags
Buffer.WriteInt16(10, 8); // compression method
Buffer.WriteInt16(12, 0); // time
Buffer.WriteInt16(14, 0); // date
Buffer.WriteInt32(16, CRC32); // crc-32
Buffer.WriteInt32(20, CompressedDataSize); // compressed size
Buffer.WriteInt32(24, UncompressedDataSize); // uncompressed size
Buffer.WriteInt16(28, 4); // file name length
Buffer.WriteInt16(30, 0); // extra field length
Buffer.WriteInt16(32, 0); // file comment length
Buffer.WriteInt16(34, 0); // disk number start
Buffer.WriteInt16(36, 0); // internal file attributes
Buffer.WriteInt32(38, 2176057344); // external file attributes
Buffer.WriteInt32(42, 0); // relative offset of local header
Buffer.Write(46, ПолучитьБуферДвоичныхДанныхИзСтроки("data", "ascii", False));
Return Buffer;
EndFunction
Function ZipEOCD(CompressedDataSize)
// End of central directory
SizeCDH = 50;
Buffer = New BinaryDataBuffer(ZipSizeEOCD());
Buffer.WriteInt32(0, 101010256); // signature 0x06054b50
Buffer.WriteInt16(4, 0); // number of this disk
Buffer.WriteInt16(6, 0); // number of the disk with the start of the central directory
Buffer.WriteInt16(8, 1); // total number of entries in the central directory on this disk
Buffer.WriteInt16(10, 1); // total number of entries in the central directory
Buffer.WriteInt32(12, SizeCDH); // size of the central directory
// offset of start of central directory with respect to the starting disk number
Buffer.WriteInt32(16, ZipSizeLFH() + CompressedDataSize + ZipSizeDD());
Buffer.WriteInt16(20, 0); // the starting disk number
Return Buffer;
EndFunction
#EndRegion
#Region AWS4
Function AddAWS4()
AuthorizationHeader = CreateAuthorizationHeader();
Request.Headers.Insert("Authorization", AuthorizationHeader);
Return ЭтотОбъект;
EndFunction
Function CreateAuthorizationHeader()
AccessKey = AWS4Data["AccessKey"];
CurrentDate = CurrentUniversalDate();
Request.Headers.Insert("x-amz-date", OPI_Tools.ISOTimestamp(CurrentDate));
Request.Headers.Insert("Host" , Connection.Host);
MainParts = GetMainSignatureParts(CurrentDate);
Scope = MainParts["Scope"];
Signature = MainParts["Signature"];
HeadersKeys = MainParts["HeadersKeys"];
AuthorizationHeader = FormAuthorizationHeader(AccessKey, Scope, Signature, HeadersKeys);
Return AuthorizationHeader;
EndFunction
Function GetMainSignatureParts(Val CurrentDate)
SecretKey = AWS4Data["SecretKey"];
Region = AWS4Data["Region"];
Service = AWS4Data["Service"];
SignKey = GetSignatureKey(SecretKey, Region, Service, CurrentDate);
CanonicalRequest = CreateCanonicalRequest();
Scope = CreateScope(Region, Service, CurrentDate);
StringToSign = CreateSignatureString(CanonicalRequest, Scope, CurrentDate);
Signature = OPI_Cryptography.HMACSHA256(SignKey, StringToSign);
Signature = Lower(ПолучитьHexСтрокуИзДвоичныхДанных(Signature));
HeadersKeys = GetHeadersKeysString();
PartsStructure = New Structure;
PartsStructure.Insert("Scope" , Scope);
PartsStructure.Insert("Signature" , Signature);
PartsStructure.Insert("HeadersKeys", HeadersKeys);
Return PartsStructure;
EndFunction
Function FormAuthorizationHeader(Val AccessKey, Val Scope, Val Signature, Val HeadersKeys)
HeaderTemplate = "AWS4-HMAC-SHA256 "
+ "Credential=%1/%2, "
+ "SignedHeaders=%3, "
+ "Signature=%4";
AuthorizationHeader = StrTemplate(HeaderTemplate, AccessKey, Scope, HeadersKeys, Signature);
Return AuthorizationHeader;
EndFunction
Function GetSignatureKey(Val SecretKey, Val Region, Val Service, Val CurrentDate)
SecretKey = ПолучитьДвоичныеДанныеИзСтроки("AWS4" + SecretKey);
DateData = ПолучитьДвоичныеДанныеИзСтроки(Format(CurrentDate, "DF=yyyyMMdd;"));
Region = ПолучитьДвоичныеДанныеИзСтроки(Region);
Service = ПолучитьДвоичныеДанныеИзСтроки(Service);
AWSRequest = ПолучитьДвоичныеДанныеИзСтроки("aws4_request");
DataKey = OPI_Cryptography.HMACSHA256(SecretKey, DateData);
RegionKey = OPI_Cryptography.HMACSHA256(DataKey, Region);
ServiceKey = OPI_Cryptography.HMACSHA256(RegionKey, Service);
FinalKey = OPI_Cryptography.HMACSHA256(ServiceKey, AWSRequest);
Return FinalKey;
EndFunction
Function CreateCanonicalRequest()
RequestTemplate = "";
RequestBody = GetRequestBodyAsBinaryData();
HashSum = OPI_Cryptography.Hash(RequestBody, HashFunction.SHA256);
PartsAmount = 6;
Request.Headers.Insert("x-amz-content-sha256", Lower(ПолучитьHexСтрокуИзДвоичныхДанных(HashSum)));
For N = 1 To PartsAmount Do
RequestTemplate = RequestTemplate + "%" + String(N) + ?(N = PartsAmount, "", Chars.LF);
EndDo;
Method = Upper(RequestMethod);
URIString = GetURIString();
ParameterString = GetParamsString();
HeadersString = GetHeadersString();
KeysString = GetHeadersKeysString();
HashString = Lower(ПолучитьHexСтрокуИзДвоичныхДанных(HashSum));
CanonicalRequest = StrTemplate(RequestTemplate
, Method
, URIString
, ParameterString
, HeadersString
, KeysString
, HashString);
Return CanonicalRequest;
EndFunction
Function CreateScope(Val Region, Val Service, Val CurrentDate)
CommonDate = Format(CurrentDate, "DF=yyyyMMdd;");
Scope = New Array;
Scope.Add(CommonDate);
Scope.Add(Region);
Scope.Add(Service);
Scope.Add("aws4_request");
ScopeString = StrConcat(Scope, "/");
Return ScopeString;
EndFunction
Function CreateSignatureString(Val CanonicalRequest, Val Scope, Val CurrentDate)
StringTemplate = "";
Algorithm = "AWS4-HMAC-SHA256";
DateISO = OPI_Tools.ISOTimestamp(CurrentDate);
PartsAmount = 4;
CanonicalRequest = ПолучитьДвоичныеДанныеИзСтроки(CanonicalRequest);
CanonicalRequest = OPI_Cryptography.Hash(CanonicalRequest, HashFunction.SHA256);
CanonicalRequest = Lower(ПолучитьHexСтрокуИзДвоичныхДанных(CanonicalRequest));
For N = 1 To PartsAmount Do
StringTemplate = StringTemplate + "%" + String(N) + ?(N = PartsAmount, "", Chars.LF);
EndDo;
SignatureString = StrTemplate(StringTemplate, Algorithm, DateISO, Scope, CanonicalRequest);
SignatureString = ПолучитьДвоичныеДанныеИзСтроки(SignatureString);
Return SignatureString;
EndFunction
Function GetHeadersKeysString()
HeadersList = New ValueList;
For Each Title In Request.Headers Do
CurrentKey = Title.Key;
CurrentKeyN = Lower(CurrentKey);
If Not StrStartsWith(CurrentKeyN, "host") And Not StrStartsWith(CurrentKeyN, "x-amz") Then
Continue;
EndIf;
HeaderString = Lower(CurrentKey);
HeadersList.Add(HeaderString);
EndDo;
HeadersList.SortByValue();
HeadersString = StrConcat(HeadersList.UnloadValues(), ";");
Return HeadersString;
EndFunction
Function GetURIString()
URI = Request.ResourceAddress;
URI = ?(StrStartsWith(URI, "/"), URI, "/" + URI);
ParamsStart = StrFind(URI, "?");
If ParamsStart <> 0 Then
URI = Left(URI, ParamsStart - 1);
EndIf;
Return URI;
EndFunction
Function GetParamsString()
URI = Request.ResourceAddress;
ParamsStart = StrFind(URI, "?");
If ParamsStart = 0 Then
ParameterString = "";
Else
URILength = StrLen(URI);
ParameterString = Right(URI, URILength - ParamsStart);
ProcessRequestParametersString(ParameterString);
EndIf;
Return ParameterString;
EndFunction
Function GetHeadersString()
HeadersList = New ValueList;
For Each Title In Request.Headers Do
CurrentKey = Title.Key;
CurrentKeyN = Lower(CurrentKey);
If Not StrStartsWith(CurrentKeyN, "host") And Not StrStartsWith(CurrentKeyN, "x-amz") Then
Continue;
EndIf;
HeaderString = Lower(CurrentKey) + ":" + Title.Value;
HeadersList.Add(HeaderString);
EndDo;
HeadersList.SortByValue();
HeadersString = StrConcat(HeadersList.UnloadValues(), Chars.LF);
HeadersString = HeadersString + Chars.LF;
Return HeadersString;
EndFunction
Procedure ProcessRequestParametersString(ParameterString)
ParameterArray = StrSplit(ParameterString, "&");
ParamsList = New ValueList();
ParamsList.LoadValues(ParameterArray);
ParamsList.SortByValue();
ParameterArray = ParamsList.UnloadValues();
For N = 0 To ParameterArray.UBound() Do
QueryParameter = ParameterArray[N];
If StrFind(QueryParameter, "=") = 0 Then
ParameterArray[N] = QueryParameter + "=";
EndIf;
EndDo;
ParameterString = StrConcat(ParameterArray, "&");
EndProcedure
#EndRegion
#Region Auxiliary
Function StopExecution(Val ExceptionOnError = False)
OPI_TypeConversion.GetBoolean(ExceptionOnError);
IsError = RequestInitialized().Error;
If IsError And ExceptionOnError Then
Raise GetLog(True);
Else
Return IsError;
EndIf;
EndFunction
Function RequestInitialized()
Return ?(ValueIsFilled(Initialized)
, ЭтотОбъект
, Error("The request is not initialized. It is necessary to call the Initialize() function before starting work"));
EndFunction
Function Error(Val ErrorText)
Error = True;
If BodyTemporaryFile And ValueIsFilled(RequestBodyFile) Then
Try
DeleteFiles(RequestBodyFile);
RequestBodyFile = Undefined;
BodyTemporaryFile = False;
Except
AddLog("Error: Failed to delete a temporary file on exception");
EndTry;
EndIf;
Return AddLog(ErrorText);
EndFunction
Function AddLog(Val Text)
OPI_TypeConversion.GetLine(Text);
Log.Add(Text);
OPI_Tools.DebugInfo(Text);
Return ЭтотОбъект;
EndFunction
Function GetSetting(Val SettingKey)
Return Settings[SettingKey];
EndFunction
Procedure SetSetting(Val SettingKey, Val Value)
Settings[SettingKey] = Value;
EndProcedure
Procedure SetDefaultSettings()
AddLog("SetDefaultSettings: configuration setting");
Settings = New Structure;
Settings.Insert("gzip" , True);
Settings.Insert("SplitArrayParams" , False);
Settings.Insert("URLencoding" , True);
Settings.Insert("EncodeRequestBody" , "UTF-8");
EndProcedure
Procedure EncodeURLInURL(URL) Export
Plug = StrTemplate("@#%1#@", String(New UUID));
URL = StrReplace(URL, "&" , Plug);
URL = EncodeString(URL, StringEncodingMethod.URLInURLEncoding);
URL = StrReplace(URL, Plug, "&");
EndProcedure
#EndRegion
#EndRegion
// #EndIf