|
//----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
namespace System.ServiceModel.Channels
{
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.WebSockets;
using System.Runtime;
using System.Runtime.Diagnostics;
using System.Security.Authentication.ExtendedProtection;
using System.ServiceModel;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Diagnostics.Application;
using System.ServiceModel.Security;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
abstract class HttpRequestContext : RequestContextBase
{
HttpOutput httpOutput;
bool errorGettingHttpInput;
HttpChannelListener listener;
SecurityMessageProperty securityProperty;
EventTraceActivity eventTraceActivity;
HttpPipeline httpPipeline;
ServerWebSocketTransportDuplexSessionChannel webSocketChannel;
protected HttpRequestContext(HttpChannelListener listener, Message requestMessage, EventTraceActivity eventTraceActivity)
: base(requestMessage, listener.InternalCloseTimeout, listener.InternalSendTimeout)
{
this.listener = listener;
this.eventTraceActivity = eventTraceActivity;
}
public bool KeepAliveEnabled
{
get
{
return listener.KeepAliveEnabled;
}
}
public bool HttpMessagesSupported
{
get { return this.listener.HttpMessageSettings.HttpMessagesSupported; }
}
public abstract string HttpMethod { get; }
public abstract bool IsWebSocketRequest { get; }
internal ServerWebSocketTransportDuplexSessionChannel WebSocketChannel
{
get
{
return this.webSocketChannel;
}
set
{
Fx.Assert(this.webSocketChannel == null, "webSocketChannel should not be set twice.");
this.webSocketChannel = value;
}
}
internal HttpChannelListener Listener
{
get { return this.listener; }
}
internal EventTraceActivity EventTraceActivity
{
get
{
return this.eventTraceActivity;
}
}
// Note: This method will return null in the case where throwOnError is false, and a non-fatal error occurs.
// Please exercice caution when passing in throwOnError = false. This should basically only be done in error
// code paths, or code paths where there is very good reason that you would not want this method to throw.
// When passing in throwOnError = false, please handle the case where this method returns null.
public HttpInput GetHttpInput(bool throwOnError)
{
HttpPipeline pipeline = this.httpPipeline;
if ((pipeline != null) && pipeline.IsHttpInputInitialized)
{
return pipeline.HttpInput;
}
HttpInput httpInput = null;
if (throwOnError || !this.errorGettingHttpInput)
{
try
{
httpInput = GetHttpInput();
this.errorGettingHttpInput = false;
}
catch (Exception e)
{
this.errorGettingHttpInput = true;
if (throwOnError || Fx.IsFatal(e))
{
throw;
}
DiagnosticUtility.TraceHandledException(e, TraceEventType.Warning);
}
}
return httpInput;
}
internal static HttpRequestContext CreateContext(HttpChannelListener listener, HttpListenerContext listenerContext, EventTraceActivity eventTraceActivity)
{
return new ListenerHttpContext(listener, listenerContext, eventTraceActivity);
}
protected abstract SecurityMessageProperty OnProcessAuthentication();
public abstract HttpOutput GetHttpOutput(Message message);
protected abstract HttpInput GetHttpInput();
public HttpOutput GetHttpOutputCore(Message message)
{
if (this.httpOutput != null)
{
return this.httpOutput;
}
return this.GetHttpOutput(message);
}
protected override void OnAbort()
{
if (this.httpOutput != null)
{
this.httpOutput.Abort(HttpAbortReason.Aborted);
}
this.Cleanup();
}
protected override void OnClose(TimeSpan timeout)
{
try
{
if (this.httpOutput != null)
{
this.httpOutput.Close();
}
}
finally
{
this.Cleanup();
}
}
protected virtual void Cleanup()
{
if (this.httpPipeline != null)
{
this.httpPipeline.Close();
}
}
public void InitializeHttpPipeline(TransportIntegrationHandler transportIntegrationHandler)
{
this.httpPipeline = HttpPipeline.CreateHttpPipeline(this, transportIntegrationHandler, this.IsWebSocketRequest);
}
internal void SetMessage(Message message, Exception requestException)
{
if ((message == null) && (requestException == null))
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new ProtocolException(SR.GetString(SR.MessageXmlProtocolError),
new XmlException(SR.GetString(SR.MessageIsEmpty))));
}
this.TraceHttpMessageReceived(message);
if (requestException != null)
{
base.SetRequestMessage(requestException);
message.Close();
}
else
{
message.Properties.Security = (this.securityProperty != null) ? (SecurityMessageProperty)this.securityProperty.CreateCopy() : null;
base.SetRequestMessage(message);
}
}
void TraceHttpMessageReceived(Message message)
{
if (FxTrace.Trace.IsEnd2EndActivityTracingEnabled)
{
bool attached = false;
Guid relatedId = this.eventTraceActivity != null ? this.eventTraceActivity.ActivityId : Guid.Empty;
HttpRequestMessageProperty httpProperty;
// Encoder will always add an activity. We need to remove this and read it
// from the web headers for http since correlation might be propogated.
if (message.Headers.MessageId == null &&
message.Properties.TryGetValue<HttpRequestMessageProperty>(HttpRequestMessageProperty.Name, out httpProperty))
{
try
{
string e2eId = httpProperty.Headers[EventTraceActivity.Name];
if (!String.IsNullOrEmpty(e2eId))
{
byte[] data = Convert.FromBase64String(e2eId);
if (data != null && data.Length == 16)
{
Guid id = new Guid(data);
this.eventTraceActivity = new EventTraceActivity(id, true);
message.Properties[EventTraceActivity.Name] = this.eventTraceActivity;
attached = true;
}
}
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
{
throw;
}
}
}
if (!attached)
{
this.eventTraceActivity = EventTraceActivityHelper.TryExtractActivity(message, true);
}
if (TD.MessageReceivedByTransportIsEnabled())
{
TD.MessageReceivedByTransport(
this.eventTraceActivity,
this.listener != null && this.listener.Uri != null ? this.listener.Uri.AbsoluteUri : string.Empty,
relatedId);
}
}
}
protected abstract HttpStatusCode ValidateAuthentication();
bool PrepareReply(ref Message message)
{
bool closeOnReceivedEof = false;
// null means we're done
if (message == null)
{
// A null message means either a one-way request or that the service operation returned null and
// hence we can close the HttpOutput. By default we keep the HttpOutput open to allow the writing to the output
// even after the HttpInput EOF is received and the HttpOutput will be closed only on close of the HttpRequestContext.
closeOnReceivedEof = true;
message = CreateAckMessage(HttpStatusCode.Accepted, string.Empty);
}
if (!listener.ManualAddressing)
{
if (message.Version.Addressing == AddressingVersion.WSAddressingAugust2004)
{
if (message.Headers.To == null ||
listener.AnonymousUriPrefixMatcher == null ||
!listener.AnonymousUriPrefixMatcher.IsAnonymousUri(message.Headers.To))
{
message.Headers.To = message.Version.Addressing.AnonymousUri;
}
}
else if (message.Version.Addressing == AddressingVersion.WSAddressing10
|| message.Version.Addressing == AddressingVersion.None)
{
if (message.Headers.To != null &&
(listener.AnonymousUriPrefixMatcher == null ||
!listener.AnonymousUriPrefixMatcher.IsAnonymousUri(message.Headers.To)))
{
message.Headers.To = null;
}
}
else
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
new ProtocolException(SR.GetString(SR.AddressingVersionNotSupported, message.Version.Addressing)));
}
}
message.Properties.AllowOutputBatching = false;
this.httpOutput = GetHttpOutputCore(message);
// Reuse the HttpInput we got previously.
HttpInput input = this.httpPipeline.HttpInput;
if (input != null)
{
HttpDelayedAcceptStream requestStream = input.GetInputStream(false) as HttpDelayedAcceptStream;
if (requestStream != null && TransferModeHelper.IsRequestStreamed(listener.TransferMode)
&& requestStream.EnableDelayedAccept(this.httpOutput, closeOnReceivedEof))
{
return false;
}
}
return true;
}
protected override void OnReply(Message message, TimeSpan timeout)
{
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
Message responseMessage = message;
try
{
bool closeOutputAfterReply = PrepareReply(ref responseMessage);
this.httpPipeline.SendReply(responseMessage, timeoutHelper.RemainingTime());
if (closeOutputAfterReply)
{
httpOutput.Close();
}
if (TD.MessageSentByTransportIsEnabled())
{
TD.MessageSentByTransport(eventTraceActivity, this.Listener.Uri.AbsoluteUri);
}
}
finally
{
if (message != null &&
!object.ReferenceEquals(message, responseMessage))
{
responseMessage.Close();
}
}
}
protected override IAsyncResult OnBeginReply(
Message message, TimeSpan timeout, AsyncCallback callback, object state)
{
return new ReplyAsyncResult(this, message, timeout, callback, state);
}
protected override void OnEndReply(IAsyncResult result)
{
ReplyAsyncResult.End(result);
}
public bool ProcessAuthentication()
{
if (TD.HttpContextBeforeProcessAuthenticationIsEnabled())
{
TD.HttpContextBeforeProcessAuthentication(this.eventTraceActivity);
}
HttpStatusCode statusCode = ValidateAuthentication();
if (statusCode == HttpStatusCode.OK)
{
bool authenticationSucceeded = false;
statusCode = HttpStatusCode.Forbidden;
try
{
this.securityProperty = OnProcessAuthentication();
authenticationSucceeded = true;
return true;
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
if (e.Data.Contains(HttpChannelUtilities.HttpStatusCodeKey))
{
if (e.Data[HttpChannelUtilities.HttpStatusCodeKey] is HttpStatusCode)
{
statusCode = (HttpStatusCode)e.Data[HttpChannelUtilities.HttpStatusCodeKey];
}
}
throw;
}
finally
{
if (!authenticationSucceeded)
{
SendResponseAndClose(statusCode);
}
}
}
else
{
SendResponseAndClose(statusCode);
return false;
}
}
internal void SendResponseAndClose(HttpStatusCode statusCode)
{
SendResponseAndClose(statusCode, string.Empty);
}
internal void SendResponseAndClose(HttpStatusCode statusCode, string statusDescription)
{
if (ReplyInitiated)
{
this.Close();
return;
}
using (Message ackMessage = CreateAckMessage(statusCode, statusDescription))
{
this.Reply(ackMessage);
}
this.Close();
}
internal void SendResponseAndClose(HttpResponseMessage httpResponseMessage)
{
if (this.TryInitiateReply())
{
// Send the response message.
try
{
if (this.httpOutput == null)
{
this.httpOutput = this.GetHttpOutputCore(new NullMessage());
}
this.httpOutput.Send(httpResponseMessage, this.DefaultSendTimeout);
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
{
throw;
}
DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information);
}
}
// Close the request context.
try
{
this.Close(); // this also closes the HttpOutput
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
{
throw;
}
DiagnosticUtility.TraceHandledException(ex, TraceEventType.Information);
}
}
Message CreateAckMessage(HttpStatusCode statusCode, string statusDescription)
{
Message ackMessage = new NullMessage();
HttpResponseMessageProperty httpResponseProperty = new HttpResponseMessageProperty();
httpResponseProperty.StatusCode = statusCode;
httpResponseProperty.SuppressEntityBody = true;
if (statusDescription.Length > 0)
{
httpResponseProperty.StatusDescription = statusDescription;
}
ackMessage.Properties.Add(HttpResponseMessageProperty.Name, httpResponseProperty);
return ackMessage;
}
[System.Diagnostics.CodeAnalysis.SuppressMessage(FxCop.Category.ReliabilityBasic, "Reliability104",
Justification = "The exceptions will be traced and thrown by the handling method.")]
public void AcceptWebSocket(HttpResponseMessage response, string protocol, TimeSpan timeout)
{
Task<WebSocketContext> acceptTask;
bool success = false;
try
{
acceptTask = this.AcceptWebSocketCore(response, protocol);
try
{
if (!acceptTask.Wait(TimeoutHelper.ToMilliseconds(timeout)))
{
throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.AcceptWebSocketTimedOutError)));
}
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
{
throw;
}
WebSocketHelper.ThrowCorrectException(ex);
}
success = true;
}
finally
{
if (!success)
{
this.OnAcceptWebSocketError();
}
}
this.SetReplySent();
this.OnAcceptWebSocketSuccess(acceptTask.Result, response.RequestMessage);
}
protected abstract Task<WebSocketContext> AcceptWebSocketCore(HttpResponseMessage response, string protocol);
protected virtual void OnAcceptWebSocketError()
{
}
protected abstract void OnAcceptWebSocketSuccess(WebSocketContext context, HttpRequestMessage requestMessage);
protected void OnAcceptWebSocketSuccess(
WebSocketContext context,
RemoteEndpointMessageProperty remoteEndpointMessageProperty,
byte[] webSocketInternalBuffer,
bool shouldDisposeWebSocketAfterClose,
HttpRequestMessage requestMessage)
{
this.webSocketChannel.SetWebSocketInfo(
context,
remoteEndpointMessageProperty,
this.securityProperty,
webSocketInternalBuffer,
shouldDisposeWebSocketAfterClose,
requestMessage);
}
public IAsyncResult BeginAcceptWebSocket(HttpResponseMessage response, string protocol, AsyncCallback callback, object state)
{
return new AcceptWebSocketAsyncResult(this, response, protocol, callback, state);
}
public void EndAcceptWebSocket(IAsyncResult result)
{
AcceptWebSocketAsyncResult.End(result);
}
class ReplyAsyncResult : AsyncResult
{
static AsyncCallback onSendCompleted;
static Action<object, HttpResponseMessage> onHttpPipelineSend;
bool closeOutputAfterReply;
HttpRequestContext context;
Message message;
Message responseMessage;
TimeoutHelper timeoutHelper;
public ReplyAsyncResult(HttpRequestContext context, Message message, TimeSpan timeout, AsyncCallback callback, object state)
: base(callback, state)
{
this.context = context;
this.message = message;
this.responseMessage = null;
this.timeoutHelper = new TimeoutHelper(timeout);
ThreadTrace.Trace("Begin sending http reply");
this.responseMessage = this.message;
if (this.SendResponse())
{
base.Complete(true);
}
}
public static void End(IAsyncResult result)
{
AsyncResult.End<ReplyAsyncResult>(result);
}
void OnSendResponseCompleted(IAsyncResult result)
{
try
{
context.httpOutput.EndSend(result);
ThreadTrace.Trace("End sending http reply");
if (this.closeOutputAfterReply)
{
context.httpOutput.Close();
}
}
finally
{
if (this.message != null &&
!object.ReferenceEquals(this.message, this.responseMessage))
{
this.responseMessage.Close();
}
}
}
static void OnSendResponseCompletedCallback(IAsyncResult result)
{
if (result.CompletedSynchronously)
{
return;
}
ReplyAsyncResult thisPtr = (ReplyAsyncResult)result.AsyncState;
Exception completionException = null;
try
{
thisPtr.OnSendResponseCompleted(result);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
completionException = e;
}
thisPtr.Complete(false, completionException);
}
static void OnHttpPipelineSendCallback(object target, HttpResponseMessage httpResponseMessage)
{
ReplyAsyncResult thisPtr = (ReplyAsyncResult)target;
Exception pendingException = null;
bool completed = false;
try
{
completed = thisPtr.SendResponse(httpResponseMessage);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
{
throw;
}
pendingException = e;
completed = true;
}
if (completed)
{
thisPtr.Complete(false, pendingException);
}
}
public bool SendResponse(HttpResponseMessage httpResponseMessage)
{
if (onSendCompleted == null)
{
onSendCompleted = Fx.ThunkCallback(new AsyncCallback(OnSendResponseCompletedCallback));
}
bool success = false;
try
{
return this.SendResponseCore(httpResponseMessage, out success);
}
finally
{
if (!success && this.message != null &&
!object.ReferenceEquals(this.message, this.responseMessage))
{
this.responseMessage.Close();
}
}
}
public bool SendResponse()
{
if (onSendCompleted == null)
{
onSendCompleted = Fx.ThunkCallback(new AsyncCallback(OnSendResponseCompletedCallback));
}
bool success = false;
try
{
this.closeOutputAfterReply = context.PrepareReply(ref this.responseMessage);
if (onHttpPipelineSend == null)
{
onHttpPipelineSend = new Action<object, HttpResponseMessage>(OnHttpPipelineSendCallback);
}
if (context.httpPipeline.SendAsyncReply(this.responseMessage, onHttpPipelineSend, this) == AsyncCompletionResult.Queued)
{
//// In Async send + HTTP pipeline path, we will send the response back after the result coming out from the pipeline.
//// So we don't need to call it here.
success = true;
return false;
}
HttpResponseMessage httpResponseMessage = null;
if (this.context.HttpMessagesSupported)
{
httpResponseMessage = HttpResponseMessageProperty.GetHttpResponseMessageFromMessage(this.responseMessage);
}
return this.SendResponseCore(httpResponseMessage, out success);
}
finally
{
if (!success && this.message != null &&
!object.ReferenceEquals(this.message, this.responseMessage))
{
this.responseMessage.Close();
}
}
}
bool SendResponseCore(HttpResponseMessage httpResponseMessage, out bool success)
{
success = false;
IAsyncResult result;
if (httpResponseMessage == null)
{
result = context.httpOutput.BeginSend(this.timeoutHelper.RemainingTime(), onSendCompleted, this);
}
else
{
result = context.httpOutput.BeginSend(httpResponseMessage, this.timeoutHelper.RemainingTime(), onSendCompleted, this);
}
success = true;
if (!result.CompletedSynchronously)
{
return false;
}
this.OnSendResponseCompleted(result);
return true;
}
}
internal IAsyncResult BeginProcessInboundRequest(
ReplyChannelAcceptor replyChannelAcceptor,
Action acceptorCallback,
AsyncCallback callback,
object state)
{
return this.httpPipeline.BeginProcessInboundRequest(replyChannelAcceptor, acceptorCallback, callback, state);
}
internal void EndProcessInboundRequest(IAsyncResult result)
{
this.httpPipeline.EndProcessInboundRequest(result);
}
class ListenerHttpContext : HttpRequestContext, HttpRequestMessageProperty.IHttpHeaderProvider
{
HttpListenerContext listenerContext;
byte[] webSocketInternalBuffer;
public ListenerHttpContext(HttpChannelListener listener,
HttpListenerContext listenerContext, EventTraceActivity eventTraceActivity)
: base(listener, null, eventTraceActivity)
{
this.listenerContext = listenerContext;
}
public override string HttpMethod
{
get { return listenerContext.Request.HttpMethod; }
}
public override bool IsWebSocketRequest
{
get { return this.listenerContext.Request.IsWebSocketRequest; }
}
protected override HttpInput GetHttpInput()
{
return new ListenerContextHttpInput(this);
}
protected override Task<WebSocketContext> AcceptWebSocketCore(HttpResponseMessage response, string protocol)
{
// CopyHeaders would still throw when the response contains a "WWW-Authenticate"-header
// But this is ok in this case because the "WWW-Authenticate"-header doesn't make sense
// for a response returning 101 (Switching Protocol)
HttpChannelUtilities.CopyHeaders(response, this.listenerContext.Response.Headers.Add);
this.webSocketInternalBuffer = this.Listener.TakeWebSocketInternalBuffer();
return this.listenerContext.AcceptWebSocketAsync(
protocol,
WebSocketHelper.GetReceiveBufferSize(this.listener.MaxReceivedMessageSize),
this.Listener.WebSocketSettings.GetEffectiveKeepAliveInterval(),
new ArraySegment<byte>(this.webSocketInternalBuffer)).Upcast<HttpListenerWebSocketContext, WebSocketContext>();
}
protected override void OnAcceptWebSocketError()
{
byte[] buffer = Interlocked.CompareExchange<byte[]>(ref this.webSocketInternalBuffer, null, this.webSocketInternalBuffer);
if (buffer != null)
{
this.Listener.ReturnWebSocketInternalBuffer(buffer);
}
}
protected override void OnAcceptWebSocketSuccess(WebSocketContext context, HttpRequestMessage requestMessage)
{
RemoteEndpointMessageProperty remoteEndpointMessageProperty = null;
if (this.listenerContext.Request.RemoteEndPoint != null)
{
remoteEndpointMessageProperty = new RemoteEndpointMessageProperty(this.listenerContext.Request.RemoteEndPoint);
}
base.OnAcceptWebSocketSuccess(context, remoteEndpointMessageProperty, this.webSocketInternalBuffer, true, requestMessage);
}
public override HttpOutput GetHttpOutput(Message message)
{
// work around http.sys keep alive bug with chunked requests, see MB 49676, this is fixed in Vista
if (listenerContext.Request.ContentLength64 == -1 && !OSEnvironmentHelper.IsVistaOrGreater)
{
listenerContext.Response.KeepAlive = false;
}
else
{
listenerContext.Response.KeepAlive = listener.KeepAliveEnabled;
}
ICompressedMessageEncoder compressedMessageEncoder = listener.MessageEncoderFactory.Encoder as ICompressedMessageEncoder;
if (compressedMessageEncoder != null && compressedMessageEncoder.CompressionEnabled)
{
string acceptEncoding = listenerContext.Request.Headers[HttpChannelUtilities.AcceptEncodingHeader];
compressedMessageEncoder.AddCompressedMessageProperties(message, acceptEncoding);
}
return HttpOutput.CreateHttpOutput(listenerContext.Response, Listener, message, this.HttpMethod);
}
protected override SecurityMessageProperty OnProcessAuthentication()
{
return Listener.ProcessAuthentication(listenerContext);
}
protected override HttpStatusCode ValidateAuthentication()
{
return Listener.ValidateAuthentication(listenerContext);
}
protected override void OnAbort()
{
listenerContext.Response.Abort();
// CSDMain 259910, we should remove this and call base.OnAbort() instead to improve maintainability
this.Cleanup();
}
protected override void OnClose(TimeSpan timeout)
{
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
base.OnClose(timeoutHelper.RemainingTime());
try
{
listenerContext.Response.Close();
}
catch (HttpListenerException listenerException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
HttpChannelUtilities.CreateCommunicationException(listenerException));
}
}
void HttpRequestMessageProperty.IHttpHeaderProvider.CopyHeaders(WebHeaderCollection headers)
{
HttpListenerRequest listenerRequest = this.listenerContext.Request;
headers.Add(listenerRequest.Headers);
// MB 57988 - System.Net strips off user-agent from the headers collection
if (listenerRequest.UserAgent != null && headers[HttpRequestHeader.UserAgent] == null)
{
headers.Add(HttpRequestHeader.UserAgent, listenerRequest.UserAgent);
}
}
class ListenerContextHttpInput : HttpInput
{
ListenerHttpContext listenerHttpContext;
string cachedContentType; // accessing the header in System.Net involves a native transition
byte[] preReadBuffer;
public ListenerContextHttpInput(ListenerHttpContext listenerHttpContext)
: base(listenerHttpContext.Listener, true, listenerHttpContext.listener.IsChannelBindingSupportEnabled)
{
this.listenerHttpContext = listenerHttpContext;
if (this.listenerHttpContext.listenerContext.Request.ContentLength64 == -1)
{
this.preReadBuffer = new byte[1];
if (this.listenerHttpContext.listenerContext.Request.InputStream.Read(preReadBuffer, 0, 1) == 0)
{
this.preReadBuffer = null;
}
}
}
public override long ContentLength
{
get
{
return this.listenerHttpContext.listenerContext.Request.ContentLength64;
}
}
protected override string ContentTypeCore
{
get
{
if (this.cachedContentType == null)
{
this.cachedContentType = this.listenerHttpContext.listenerContext.Request.ContentType;
}
return this.cachedContentType;
}
}
protected override bool HasContent
{
get { return (this.preReadBuffer != null || this.ContentLength > 0); }
}
protected override string SoapActionHeader
{
get
{
return this.listenerHttpContext.listenerContext.Request.Headers["SOAPAction"];
}
}
protected override ChannelBinding ChannelBinding
{
get
{
return ChannelBindingUtility.GetToken(this.listenerHttpContext.listenerContext.Request.TransportContext);
}
}
protected override void AddProperties(Message message)
{
HttpRequestMessageProperty requestProperty = new HttpRequestMessageProperty(this.listenerHttpContext);
requestProperty.Method = this.listenerHttpContext.listenerContext.Request.HttpMethod;
// Uri.Query always includes the '?'
if (this.listenerHttpContext.listenerContext.Request.Url.Query.Length > 1)
{
requestProperty.QueryString = this.listenerHttpContext.listenerContext.Request.Url.Query.Substring(1);
}
message.Properties.Add(HttpRequestMessageProperty.Name, requestProperty);
message.Properties.Via = this.listenerHttpContext.listenerContext.Request.Url;
RemoteEndpointMessageProperty remoteEndpointProperty = new RemoteEndpointMessageProperty(this.listenerHttpContext.listenerContext.Request.RemoteEndPoint);
message.Properties.Add(RemoteEndpointMessageProperty.Name, remoteEndpointProperty);
}
public override void ConfigureHttpRequestMessage(HttpRequestMessage message)
{
message.Method = new HttpMethod(this.listenerHttpContext.listenerContext.Request.HttpMethod);
message.RequestUri = this.listenerHttpContext.listenerContext.Request.Url;
foreach (string webHeaderKey in this.listenerHttpContext.listenerContext.Request.Headers.Keys)
{
message.AddHeader(webHeaderKey, this.listenerHttpContext.listenerContext.Request.Headers[webHeaderKey]);
}
message.Properties.Add(RemoteEndpointMessageProperty.Name, new RemoteEndpointMessageProperty(this.listenerHttpContext.listenerContext.Request.RemoteEndPoint));
}
protected override Stream GetInputStream()
{
if (this.preReadBuffer != null)
{
return new ListenerContextInputStream(listenerHttpContext, preReadBuffer);
}
else
{
return new ListenerContextInputStream(listenerHttpContext);
}
}
class ListenerContextInputStream : HttpDelayedAcceptStream
{
public ListenerContextInputStream(ListenerHttpContext listenerHttpContext)
: base(listenerHttpContext.listenerContext.Request.InputStream)
{
}
public ListenerContextInputStream(ListenerHttpContext listenerHttpContext, byte[] preReadBuffer)
: base(new PreReadStream(listenerHttpContext.listenerContext.Request.InputStream, preReadBuffer))
{
}
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
try
{
return base.BeginRead(buffer, offset, count, callback, state);
}
catch (HttpListenerException listenerException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
HttpChannelUtilities.CreateCommunicationException(listenerException));
}
}
public override int EndRead(IAsyncResult result)
{
try
{
return base.EndRead(result);
}
catch (HttpListenerException listenerException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
HttpChannelUtilities.CreateCommunicationException(listenerException));
}
}
public override int Read(byte[] buffer, int offset, int count)
{
try
{
return base.Read(buffer, offset, count);
}
catch (HttpListenerException listenerException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
HttpChannelUtilities.CreateCommunicationException(listenerException));
}
}
public override int ReadByte()
{
try
{
return base.ReadByte();
}
catch (HttpListenerException listenerException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
HttpChannelUtilities.CreateCommunicationException(listenerException));
}
}
}
}
}
class AcceptWebSocketAsyncResult : AsyncResult
{
static AsyncCallback onHandleAcceptWebSocketResult = Fx.ThunkCallback(new AsyncCallback(HandleAcceptWebSocketResult));
HttpRequestContext context;
SignalGate gate = new SignalGate();
HttpResponseMessage response;
public AcceptWebSocketAsyncResult(HttpRequestContext context, HttpResponseMessage response, string protocol, AsyncCallback callback, object state)
: base(callback, state)
{
Fx.Assert(context != null, "context should not be null.");
Fx.Assert(response != null, "response should not be null.");
this.context = context;
this.response = response;
IAsyncResult result = this.context.AcceptWebSocketCore(response, protocol).AsAsyncResult<WebSocketContext>(onHandleAcceptWebSocketResult, this);
if (this.gate.Unlock())
{
this.CompleteAcceptWebSocket(result);
base.Complete(true);
}
}
public static void End(IAsyncResult result)
{
AsyncResult.End<AcceptWebSocketAsyncResult>(result);
}
static void HandleAcceptWebSocketResult(IAsyncResult result)
{
AcceptWebSocketAsyncResult thisPtr = (AcceptWebSocketAsyncResult)result.AsyncState;
if (!thisPtr.gate.Signal())
{
return;
}
Exception pendingException = null;
try
{
thisPtr.CompleteAcceptWebSocket(result);
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
{
throw;
}
pendingException = ex;
}
thisPtr.Complete(false, pendingException);
}
void CompleteAcceptWebSocket(IAsyncResult result)
{
Task<WebSocketContext> acceptTask = result as Task<WebSocketContext>;
Fx.Assert(acceptTask != null, "acceptTask should not be null.");
if (acceptTask.IsFaulted)
{
this.context.OnAcceptWebSocketError();
throw FxTrace.Exception.AsError<WebSocketException>(acceptTask.Exception);
}
else if (acceptTask.IsCanceled)
{
this.context.OnAcceptWebSocketError();
//
throw FxTrace.Exception.AsError(new TimeoutException(SR.GetString(SR.AcceptWebSocketTimedOutError)));
}
this.context.SetReplySent();
this.context.OnAcceptWebSocketSuccess(acceptTask.Result, response.RequestMessage);
}
}
}
}
|