246 lines
11 KiB
C#
246 lines
11 KiB
C#
|
//------------------------------------------------------------
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//------------------------------------------------------------
|
||
|
namespace System.ServiceModel.Transactions
|
||
|
{
|
||
|
using System;
|
||
|
using System.Runtime;
|
||
|
using System.Security.Permissions;
|
||
|
using System.ServiceModel.Channels;
|
||
|
using System.ServiceModel.Security;
|
||
|
using System.Transactions;
|
||
|
using System.Xml;
|
||
|
using Microsoft.Transactions.Wsat.Messaging;
|
||
|
using Microsoft.Transactions.Wsat.Protocol;
|
||
|
using DiagnosticUtility = System.ServiceModel.DiagnosticUtility;
|
||
|
|
||
|
abstract class WsatTransactionFormatter : TransactionFormatter
|
||
|
{
|
||
|
bool initialized;
|
||
|
WsatConfiguration wsatConfig;
|
||
|
WsatProxy wsatProxy;
|
||
|
ProtocolVersion protocolVersion;
|
||
|
|
||
|
protected WsatTransactionFormatter(ProtocolVersion protocolVersion)
|
||
|
{
|
||
|
this.protocolVersion = protocolVersion;
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
void EnsureInitialized()
|
||
|
{
|
||
|
if (!this.initialized)
|
||
|
{
|
||
|
lock (this)
|
||
|
{
|
||
|
if (!this.initialized)
|
||
|
{
|
||
|
this.wsatConfig = new WsatConfiguration();
|
||
|
this.wsatProxy = new WsatProxy(this.wsatConfig, this.protocolVersion);
|
||
|
this.initialized = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
// The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
|
||
|
/*
|
||
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true)] // because we call code from a non-APTCA assembly; WSATs are not supported in partial trust, so customers should not be broken by this demand
|
||
|
*/
|
||
|
public override void WriteTransaction(Transaction transaction, Message message)
|
||
|
{
|
||
|
EnsureInitialized();
|
||
|
|
||
|
ForcePromotion(transaction);
|
||
|
|
||
|
// Make a context and add it to the message
|
||
|
CoordinationContext context;
|
||
|
RequestSecurityTokenResponse issuedToken;
|
||
|
MarshalAsCoordinationContext(transaction, out context, out issuedToken);
|
||
|
if (issuedToken != null)
|
||
|
{
|
||
|
CoordinationServiceSecurity.AddIssuedToken(message, issuedToken);
|
||
|
}
|
||
|
|
||
|
WsatTransactionHeader header = new WsatTransactionHeader(context, this.protocolVersion);
|
||
|
message.Headers.Add(header);
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
void ForcePromotion(Transaction transaction)
|
||
|
{
|
||
|
// Force promotion. This may throw TransactionException.
|
||
|
// We used to check the DistributedIdentifier property first, but VSWhidbey bug 547901
|
||
|
// prevents us from doing so reliably in multi-threaded scenarios (there is a ----
|
||
|
// in the System.Transactions code that can cause a NullReferenceException if we ask
|
||
|
// for the identifier while the transaction is being promoted)
|
||
|
TransactionInterop.GetTransmitterPropagationToken(transaction);
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
// The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
|
||
|
/*
|
||
|
// We demand full trust because we use CoordinationServiceSecurity from a non-APTCA assembly and CoordinationServiceSecurity.GetIssuedToken(..) can call Environment.FailFast.
|
||
|
// It's recommended to not let partially trusted callers to bring down the process.
|
||
|
// WSATs are not supported in partial trust, so customers should not be broken by this demand.
|
||
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true)]
|
||
|
*/
|
||
|
public override TransactionInfo ReadTransaction(Message message)
|
||
|
{
|
||
|
EnsureInitialized();
|
||
|
|
||
|
CoordinationContext context = WsatTransactionHeader.GetCoordinationContext(message, this.protocolVersion);
|
||
|
if (context == null)
|
||
|
return null;
|
||
|
|
||
|
// Incoming transaction tokens are optional
|
||
|
RequestSecurityTokenResponse issuedToken;
|
||
|
try
|
||
|
{
|
||
|
issuedToken = CoordinationServiceSecurity.GetIssuedToken(message, context.Identifier, this.protocolVersion);
|
||
|
}
|
||
|
catch (XmlException e)
|
||
|
{
|
||
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
||
|
new TransactionException(SR.FailedToDeserializeIssuedToken, e));
|
||
|
}
|
||
|
|
||
|
return new WsatTransactionInfo(this.wsatProxy, context, issuedToken);
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
public WsatTransactionInfo CreateTransactionInfo(CoordinationContext context,
|
||
|
RequestSecurityTokenResponse issuedToken)
|
||
|
{
|
||
|
return new WsatTransactionInfo(this.wsatProxy, context, issuedToken);
|
||
|
}
|
||
|
|
||
|
//=======================================================================================
|
||
|
// The demand is not added now (in 4.5), to avoid a breaking change. To be considered in the next version.
|
||
|
/*
|
||
|
// We demand full trust because we use CoordinationContext and CoordinationServiceSecurity from a non-APTCA assembly.
|
||
|
// The CoordinationContext constructor can call Environment.FailFast and it's recommended to not let partially trusted callers to bring down the process.
|
||
|
// WSATs are not supported in partial trust, so customers should not be broken by this demand.
|
||
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true)]
|
||
|
*/
|
||
|
public void MarshalAsCoordinationContext(Transaction transaction,
|
||
|
out CoordinationContext context,
|
||
|
out RequestSecurityTokenResponse issuedToken)
|
||
|
{
|
||
|
Guid transactionId = transaction.TransactionInformation.DistributedIdentifier;
|
||
|
string nonNativeContextId = null;
|
||
|
|
||
|
context = new CoordinationContext(this.protocolVersion);
|
||
|
|
||
|
// Get timeout, description and isolation flags
|
||
|
uint timeout;
|
||
|
IsolationFlags isoFlags;
|
||
|
string description;
|
||
|
OleTxTransactionFormatter.GetTransactionAttributes(transaction,
|
||
|
out timeout,
|
||
|
out isoFlags,
|
||
|
out description);
|
||
|
context.IsolationFlags = isoFlags;
|
||
|
context.Description = description;
|
||
|
|
||
|
// If we can, use cached extended information
|
||
|
// Note - it may be worth using outgoing contexts more than once.
|
||
|
// We'll let performance profiling decide that question
|
||
|
WsatExtendedInformation info;
|
||
|
if (WsatExtendedInformationCache.Find(transaction, out info))
|
||
|
{
|
||
|
context.Expires = info.Timeout;
|
||
|
|
||
|
// The extended info cache only contains an identifier when it's non-native
|
||
|
if (!string.IsNullOrEmpty(info.Identifier))
|
||
|
{
|
||
|
context.Identifier = info.Identifier;
|
||
|
nonNativeContextId = info.Identifier;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
context.Expires = timeout;
|
||
|
if (context.Expires == 0)
|
||
|
{
|
||
|
// If the timeout is zero, there are two possibilities:
|
||
|
// 1) This is a root transaction with an infinite timeout.
|
||
|
// 2) This is a subordinate transaction whose timeout was not flowed.
|
||
|
// We have no mechanism for distinguishing between the two cases.
|
||
|
//
|
||
|
// We could always return zero here, instead of using the local max timeout.
|
||
|
// The problem is that the 2004/08 WS-C spec does not specify the meaning
|
||
|
// of a zero expires field. While we accept zero to mean "as large as possible"
|
||
|
// it would be risky to expect others to do the same. So we only propagate
|
||
|
// zero in the expires field if the local max timeout has been disabled.
|
||
|
//
|
||
|
// This is MB 34596: how can we flow the real timeout?
|
||
|
context.Expires = (uint)TimeoutHelper.ToMilliseconds(this.wsatConfig.MaxTimeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (context.Identifier == null)
|
||
|
{
|
||
|
context.Identifier = CoordinationContext.CreateNativeIdentifier(transactionId);
|
||
|
nonNativeContextId = null;
|
||
|
}
|
||
|
|
||
|
string tokenId;
|
||
|
if (!this.wsatConfig.IssuedTokensEnabled)
|
||
|
{
|
||
|
tokenId = null;
|
||
|
issuedToken = null;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
CoordinationServiceSecurity.CreateIssuedToken(transactionId,
|
||
|
context.Identifier,
|
||
|
this.protocolVersion,
|
||
|
out issuedToken,
|
||
|
out tokenId);
|
||
|
}
|
||
|
|
||
|
AddressHeader refParam = new WsatRegistrationHeader(transactionId, nonNativeContextId, tokenId);
|
||
|
context.RegistrationService = wsatConfig.CreateRegistrationService(refParam, this.protocolVersion);
|
||
|
context.IsolationLevel = transaction.IsolationLevel;
|
||
|
context.LocalTransactionId = transactionId;
|
||
|
|
||
|
if (this.wsatConfig.OleTxUpgradeEnabled)
|
||
|
{
|
||
|
context.PropagationToken = TransactionInterop.GetTransmitterPropagationToken(transaction);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//------------------------------------------------------------------------------------------
|
||
|
// Versioned Wsat transaction formatters
|
||
|
//------------------------------------------------------------------------------------------
|
||
|
|
||
|
class WsatTransactionFormatter10 : WsatTransactionFormatter
|
||
|
{
|
||
|
static WsatTransactionHeader emptyTransactionHeader = new WsatTransactionHeader(null, ProtocolVersion.Version10);
|
||
|
|
||
|
public WsatTransactionFormatter10() : base(ProtocolVersion.Version10) { }
|
||
|
|
||
|
//=======================================================================================
|
||
|
public override MessageHeader EmptyTransactionHeader
|
||
|
{
|
||
|
get { return emptyTransactionHeader; }
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class WsatTransactionFormatter11 : WsatTransactionFormatter
|
||
|
{
|
||
|
static WsatTransactionHeader emptyTransactionHeader = new WsatTransactionHeader(null, ProtocolVersion.Version11);
|
||
|
|
||
|
public WsatTransactionFormatter11() : base(ProtocolVersion.Version11) { }
|
||
|
|
||
|
//=======================================================================================
|
||
|
public override MessageHeader EmptyTransactionHeader
|
||
|
{
|
||
|
get { return emptyTransactionHeader; }
|
||
|
}
|
||
|
}
|
||
|
}
|