/// <summary> /// Completes an asynchronous operation to send a request over the secure channel. /// </summary> public void OnGetRequestStreamComplete(IAsyncResult result) { AsyncResult result2 = result.AsyncState as AsyncResult; if (result2 == null) { return; } try { Stream ostrm = result2.WebRequest.EndGetRequestStream(result); MemoryStream mstrm = new MemoryStream(); if (m_settings.Configuration.UseBinaryEncoding) { BinaryEncoder encoder = new BinaryEncoder(mstrm, this.MessageContext); encoder.EncodeMessage(result2.Request); } else { WriteSoapMessage( mstrm, result2.Request.GetType().Name, result2.Request, this.MessageContext); } int bytesToRead = (int)mstrm.Position; mstrm.Position = 0; int bytesRead = 0; int blockSize = 0; byte[] buffer = new byte[4096]; do { blockSize = mstrm.Read(buffer, 0, buffer.Length); bytesRead += blockSize; if (bytesRead > bytesToRead) { blockSize -= (bytesRead - bytesToRead); } ostrm.Write(buffer, 0, blockSize); } while (blockSize >= 0 && bytesRead < bytesToRead); ostrm.Close(); result2.InnerResult = result2.WebRequest.BeginGetResponse(OnBeginGetResponseComplete, result2); } catch (Exception exception) { result2.Exception = exception; result2.OperationCompleted(); } }
private Stream EndProcessRequest(IAsyncResult result) { MemoryStream ostrm = new MemoryStream(); try { if (m_callback != null) { IServiceResponse response = m_callback.EndProcessRequest(result); string contentType = WebOperationContext.Current.IncomingRequest.ContentType; if (contentType == "application/octet-stream") { BinaryEncoder encoder = new BinaryEncoder(ostrm, this.m_quotas.MessageContext); encoder.EncodeMessage(response); } else { HttpsTransportChannel.WriteSoapMessage( ostrm, response.GetType().Name, response, this.m_quotas.MessageContext); } ostrm.Position = 0; } } catch (Exception e) { Utils.Trace(e, "TCPLISTENER - Unexpected error sending result."); } return ostrm; }
/// <summary> /// Handles requests arriving from a channel. /// </summary> public async Task SendAsync(HttpContext context) { IAsyncResult result = null; try { if (m_callback == null) { context.Response.ContentLength = 0; context.Response.ContentType = "text/plain"; context.Response.StatusCode = (int)HttpStatusCode.NotImplemented; await context.Response.WriteAsync(string.Empty).ConfigureAwait(false); return; } if (context.Request.ContentType != "application/octet-stream") { context.Response.ContentLength = 0; context.Response.ContentType = "text/plain"; context.Response.StatusCode = (int)HttpStatusCode.BadRequest; await context.Response.WriteAsync("HTTPSLISTENER - Unsupported content type.").ConfigureAwait(false); return; } int length = (int)context.Request.ContentLength; byte[] buffer = await ReadBodyAsync(context.Request).ConfigureAwait(false); if (buffer.Length != length) { context.Response.ContentLength = 0; context.Response.ContentType = "text/plain"; context.Response.StatusCode = (int)HttpStatusCode.BadRequest; await context.Response.WriteAsync("HTTPSLISTENER - Couldn't decode buffer.").ConfigureAwait(false); return; } IServiceRequest input = (IServiceRequest)BinaryDecoder.DecodeMessage(buffer, null, m_quotas.MessageContext); // extract the JWT token from the HTTP headers. if (input.RequestHeader == null) { input.RequestHeader = new RequestHeader(); } if (NodeId.IsNull(input.RequestHeader.AuthenticationToken) && input.TypeId != DataTypeIds.CreateSessionRequest) { if (context.Request.Headers.Keys.Contains("Authorization")) { foreach (string value in context.Request.Headers["Authorization"]) { if (value.StartsWith("Bearer")) { input.RequestHeader.AuthenticationToken = new NodeId(value.Substring("Bearer ".Length).Trim()); } } } } EndpointDescription endpoint = null; foreach (var ep in m_descriptions) { if (ep.EndpointUrl.StartsWith(Utils.UriSchemeHttps)) { endpoint = ep; break; } } result = m_callback.BeginProcessRequest( m_listenerId, endpoint, input as IServiceRequest, null, null); IServiceResponse output = m_callback.EndProcessRequest(result); byte[] response = BinaryEncoder.EncodeMessage(output, m_quotas.MessageContext); context.Response.ContentLength = response.Length; context.Response.ContentType = context.Request.ContentType; context.Response.StatusCode = (int)HttpStatusCode.OK; #if NETSTANDARD2_1 await context.Response.Body.WriteAsync(response.AsMemory(0, response.Length)).ConfigureAwait(false); #else await context.Response.Body.WriteAsync(response, 0, response.Length).ConfigureAwait(false); #endif } catch (Exception e) { Utils.Trace(e, "HTTPSLISTENER - Unexpected error processing request."); context.Response.ContentLength = e.Message.Length; context.Response.ContentType = "text/plain"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await context.Response.WriteAsync(e.Message).ConfigureAwait(false); } }
/// <summary> /// Secures the message using the security token. /// </summary> protected BufferCollection WriteSymmetricMessage( uint messageType, uint requestId, ChannelToken token, object messageBody, bool isRequest, out bool limitsExceeded) { limitsExceeded = false; bool success = false; BufferCollection chunksToProcess = null; try { // calculate chunk sizes. int maxCipherTextSize = SendBufferSize - TcpMessageLimits.SymmetricHeaderSize; int maxCipherBlocks = maxCipherTextSize / EncryptionBlockSize; int maxPlainTextSize = maxCipherBlocks * EncryptionBlockSize; int maxPayloadSize = maxPlainTextSize - SymmetricSignatureSize - 1 - TcpMessageLimits.SequenceHeaderSize; int headerSize = TcpMessageLimits.SymmetricHeaderSize + TcpMessageLimits.SequenceHeaderSize; // write the body to stream. ArraySegmentStream ostrm = new ArraySegmentStream( BufferManager, SendBufferSize, headerSize, maxPayloadSize); // check for encodeable body. IEncodeable encodeable = messageBody as IEncodeable; if (encodeable != null) { // debug code used to verify that message aborts are handled correctly. // int maxMessageSize = Quotas.MessageContext.MaxMessageSize; // Quotas.MessageContext.MaxMessageSize = Int32.MaxValue; BinaryEncoder.EncodeMessage(encodeable, ostrm, Quotas.MessageContext); // Quotas.MessageContext.MaxMessageSize = maxMessageSize; } // check for raw bytes. ArraySegment <byte>?rawBytes = messageBody as ArraySegment <byte>?; if (rawBytes != null) { BinaryEncoder encoder = new BinaryEncoder(ostrm, Quotas.MessageContext); encoder.WriteRawBytes(rawBytes.Value.Array, rawBytes.Value.Offset, rawBytes.Value.Count); encoder.Close(); } chunksToProcess = ostrm.GetBuffers("WriteSymmetricMessage"); // ensure there is at least one chunk. if (chunksToProcess.Count == 0) { byte[] buffer = BufferManager.TakeBuffer(SendBufferSize, "WriteSymmetricMessage"); chunksToProcess.Add(new ArraySegment <byte>(buffer, 0, 0)); } BufferCollection chunksToSend = new BufferCollection(chunksToProcess.Capacity); int messageSize = 0; for (int ii = 0; ii < chunksToProcess.Count; ii++) { ArraySegment <byte> chunkToProcess = chunksToProcess[ii]; // nothing more to do if limits exceeded. if (limitsExceeded) { BufferManager.ReturnBuffer(chunkToProcess.Array, "WriteSymmetricMessage"); continue; } MemoryStream strm = new MemoryStream(chunkToProcess.Array, 0, SendBufferSize); BinaryEncoder encoder = new BinaryEncoder(strm, Quotas.MessageContext); // check if the message needs to be aborted. if (MessageLimitsExceeded(isRequest, messageSize + chunkToProcess.Count - headerSize, ii + 1)) { encoder.WriteUInt32(null, messageType | TcpMessageType.Abort); // replace the body in the chunk with an error message. BinaryEncoder errorEncoder = new BinaryEncoder( chunkToProcess.Array, chunkToProcess.Offset, chunkToProcess.Count, Quotas.MessageContext); WriteErrorMessageBody(errorEncoder, (isRequest) ? StatusCodes.BadRequestTooLarge : StatusCodes.BadResponseTooLarge); int size = errorEncoder.Close(); chunkToProcess = new ArraySegment <byte>(chunkToProcess.Array, chunkToProcess.Offset, size); limitsExceeded = true; } // check if the message is complete. else if (ii == chunksToProcess.Count - 1) { encoder.WriteUInt32(null, messageType | TcpMessageType.Final); } // more chunks to follow. else { encoder.WriteUInt32(null, messageType | TcpMessageType.Intermediate); } int count = 0; count += TcpMessageLimits.SequenceHeaderSize; count += chunkToProcess.Count; count += SymmetricSignatureSize; // calculate the padding. int padding = 0; if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // reserve one byte for the padding size. count++; if (count % EncryptionBlockSize != 0) { padding = EncryptionBlockSize - (count % EncryptionBlockSize); } count += padding; } count += TcpMessageLimits.SymmetricHeaderSize; encoder.WriteUInt32(null, (uint)count); encoder.WriteUInt32(null, ChannelId); encoder.WriteUInt32(null, token.TokenId); uint sequenceNumber = GetNewSequenceNumber(); encoder.WriteUInt32(null, sequenceNumber); encoder.WriteUInt32(null, requestId); // skip body. strm.Seek(chunkToProcess.Count, SeekOrigin.Current); // update message size count. messageSize += chunkToProcess.Count; // write padding. if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { for (int jj = 0; jj <= padding; jj++) { encoder.WriteByte(null, (byte)padding); } } if (SecurityMode != MessageSecurityMode.None) { // calculate and write signature. byte[] signature = Sign(token, new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position), isRequest); if (signature != null) { encoder.WriteRawBytes(signature, 0, signature.Length); } } if (SecurityMode == MessageSecurityMode.SignAndEncrypt) { // encrypt the data. ArraySegment <byte> dataToEncrypt = new ArraySegment <byte>(chunkToProcess.Array, TcpMessageLimits.SymmetricHeaderSize, encoder.Position - TcpMessageLimits.SymmetricHeaderSize); Encrypt(token, dataToEncrypt, isRequest); } // add the header into chunk. chunksToSend.Add(new ArraySegment <byte>(chunkToProcess.Array, 0, encoder.Position)); } // ensure the buffers don't get cleaned up on exit. success = true; return(chunksToSend); } finally { if (!success) { if (chunksToProcess != null) { chunksToProcess.Release(BufferManager, "WriteSymmetricMessage"); } } } }
/// <summary> /// Invokes the _NAME_ service. /// </summary> public IAsyncResult Begin_NAME_(_NAME_Request request, AsyncCallback callback, object asyncState) { byte[] buffer = BinaryEncoder.EncodeMessage(request, CreateContext()); return(Channel.BeginInvokeService(new InvokeServiceMessage(buffer), callback, asyncState)); }