/* ======================================================================== * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved. * * OPC Foundation MIT License 1.00 * * 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. * * The complete license agreement can be found here: * http://opcfoundation.org/License/MIT/1.00/ * ======================================================================*/ using System; namespace Opc.Ua.Server { /// /// Stores information used while a thread is completing an operation on behalf of a client. /// public class OperationContext : ISessionOperationContext { /// /// Initializes the context with a session. /// /// The request header. /// The secure channel context. /// Type of the request. /// The request lifetime. /// The identity used in the request. public OperationContext( RequestHeader requestHeader, SecureChannelContext? secureChannelContext, RequestType requestType, RequestLifetime requestLifetime, IUserIdentity? identity = null) { if (requestHeader == null) { throw new ArgumentNullException(nameof(requestHeader)); } ChannelContext = secureChannelContext; Session = null!; UserIdentity = identity!; PreferredLocales = Array.Empty(); DiagnosticsMask = (DiagnosticsMasks)requestHeader.ReturnDiagnostics; StringTable = new StringTable(); AuditEntryId = requestHeader.AuditEntryId!; RequestId = Utils.IncrementIdentifier(ref s_lastRequestId); RequestType = requestType; ClientHandle = requestHeader.RequestHandle; OperationDeadline = DateTime.MaxValue; RequestLifetime = requestLifetime ?? RequestLifetime.None; if (requestHeader.TimeoutHint > 0) { OperationDeadline = DateTime.UtcNow.AddMilliseconds(requestHeader.TimeoutHint); } } /// /// Initializes the context with a session. /// /// The request header. /// The secure channel context. /// Type of the request. /// The request lifetime. /// The session. public OperationContext( RequestHeader requestHeader, SecureChannelContext secureChannelContext, RequestType requestType, RequestLifetime requestLifetime, ISession session) { if (requestHeader == null) { throw new ArgumentNullException(nameof(requestHeader)); } ChannelContext = secureChannelContext; Session = session ?? throw new ArgumentNullException(nameof(session)); UserIdentity = session.EffectiveIdentity; PreferredLocales = session.PreferredLocales; DiagnosticsMask = (DiagnosticsMasks)requestHeader.ReturnDiagnostics; StringTable = new StringTable(); AuditEntryId = requestHeader.AuditEntryId!; RequestId = Utils.IncrementIdentifier(ref s_lastRequestId); RequestType = requestType; ClientHandle = requestHeader.RequestHandle; OperationDeadline = DateTime.MaxValue; RequestLifetime = requestLifetime ?? RequestLifetime.None; if (requestHeader.TimeoutHint > 0) { OperationDeadline = DateTime.UtcNow.AddMilliseconds(requestHeader.TimeoutHint); } } /// /// Initializes the context with a session. /// /// The session. /// The diagnostics masks. public OperationContext(ISession session, DiagnosticsMasks diagnosticsMasks) { ChannelContext = null!; Session = session ?? throw new ArgumentNullException(nameof(session)); UserIdentity = session.EffectiveIdentity; PreferredLocales = session.PreferredLocales; DiagnosticsMask = diagnosticsMasks; StringTable = new StringTable(); AuditEntryId = null!; RequestId = 0; RequestType = RequestType.Unknown; ClientHandle = 0; OperationDeadline = DateTime.MaxValue; RequestLifetime = RequestLifetime.None; } /// /// Initializes the context with a monitored item. /// /// The monitored item. public OperationContext(IMonitoredItem monitoredItem) { if (monitoredItem == null) { throw new ArgumentNullException(nameof(monitoredItem)); } ChannelContext = null!; UserIdentity = monitoredItem.EffectiveIdentity; Session = monitoredItem.Session; if (Session != null) { UserIdentity = Session.Identity; PreferredLocales = Session.PreferredLocales; } DiagnosticsMask = DiagnosticsMasks.SymbolicId; StringTable = new StringTable(); AuditEntryId = null!; RequestId = 0; RequestType = RequestType.Unknown; ClientHandle = 0; OperationDeadline = DateTime.MaxValue; RequestLifetime = RequestLifetime.None; } /// /// The context for the secure channel used to send the request. /// /// The channel context. public SecureChannelContext? ChannelContext { get; } /// /// The session associated with the context. /// /// The session. public ISession Session { get; } = null!; /// /// The lifetime of the request. /// This object is used to track the lifetime of the request and to trigger cancellation if the client aborts the request or if the request times out. /// public RequestLifetime RequestLifetime { get; } /// /// The security policy used for the secure channel. /// /// The security policy URI. public string SecurityPolicyUri { get { if (ChannelContext != null && ChannelContext.EndpointDescription != null) { return ChannelContext.EndpointDescription.SecurityPolicyUri!; } return null!; } } /// /// The type of request. /// /// The type of the request. public RequestType RequestType { get; } /// /// A unique identifier assigned to the request by the server. /// /// The request id. public uint RequestId { get; } /// /// The handle assigned by the client to the request. /// /// The client handle. public uint ClientHandle { get; } /// /// Updates the status code (thread safe). /// /// The status code. [Obsolete("Use RequestLifetime.TryCancel instead to update the status code and trigger cancellation if needed.")] public void SetStatusCode(StatusCode statusCode) { RequestLifetime.TryCancel(statusCode); } /// /// The identifier for the session (null if multiple sessions are associated with the operation). /// /// The session id. public NodeId SessionId { get { if (Session != null) { return Session.Id; } return default; } } /// /// The identity context to use when processing the request. /// /// The user identity. public IUserIdentity UserIdentity { get; } /// /// The locales to use for the operation. /// /// The preferred locales. public ArrayOf PreferredLocales { get; } /// /// The diagnostics mask specified with the request. /// /// The diagnostics mask. public DiagnosticsMasks DiagnosticsMask { get; } /// /// A table of diagnostics strings to return in the response. /// /// The string table. /// /// This object is thread safe. /// public StringTable StringTable { get; } /// /// When the request times out. /// /// The operation deadline. public DateTime OperationDeadline { get; } /// /// Gets the token that will be cancelled when the request is aborted. /// public System.Threading.CancellationToken CancellationToken => RequestLifetime?.CancellationToken ?? System.Threading.CancellationToken.None; /// /// The current status of the request (used to check for timeouts/client cancel requests). /// /// The operation status. public StatusCode OperationStatus => RequestLifetime.StatusCode; /// /// The audit log entry id provided by the client which must be included in an audit events generated by the server. /// /// The audit entry id. public string AuditEntryId { get; } = null!; private static uint s_lastRequestId; } }