/// <summary>
        /// Creates an operation response message that can be used to write the operation content to.
        /// </summary>
        /// <param name="outputStream">The output stream underlying the operation message.</param>
        /// <param name="operationListener">The operation listener.</param>
        /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param>
        /// <returns>An <see cref="ODataBatchOperationResponseMessage"/> that can be used to write the operation content.</returns>
        internal static ODataBatchOperationResponseMessage CreateWriteMessage(
            Stream outputStream,
            IODataBatchOperationListener operationListener,
            IODataUrlResolver urlResolver)
        {
            Func <Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationWriteStream(outputStream, operationListener);

            return(new ODataBatchOperationResponseMessage(streamCreatorFunc, /*headers*/ null, operationListener, /*contentId*/ null, urlResolver, /*writing*/ true));
        }
예제 #2
0
        /// <summary>
        /// Parses the request line of a batch operation request.
        /// </summary>
        /// <param name="requestLine">The request line as a string.</param>
        /// <param name="httpMethod">The parsed HTTP method of the request.</param>
        /// <param name="requestUri">The parsed <see cref="Uri"/> of the request.</param>
        private void ParseRequestLine(string requestLine, out string httpMethod, out Uri requestUri)
        {
            Debug.Assert(!this.inputContext.ReadingResponse, "Must only be called for requests.");

            // Batch Request: POST /Customers HTTP/1.1
            // Since the uri can contain spaces, the only way to read the request url, is to
            // check for first space character and last space character and anything between
            // them.
            int firstSpaceIndex = requestLine.IndexOf(' ');

            // Check whether there are enough characters after the first space for the 2nd and 3rd segments
            // (and a whitespace in between)
            if (firstSpaceIndex <= 0 || requestLine.Length - 3 <= firstSpaceIndex)
            {
                // only 1 segment or empty first segment or not enough left for 2nd and 3rd segments
                throw new ODataException(Strings.ODataBatchReaderStream_InvalidRequestLine(requestLine));
            }

            int lastSpaceIndex = requestLine.LastIndexOf(' ');

            if (lastSpaceIndex < 0 || lastSpaceIndex - firstSpaceIndex - 1 <= 0 || requestLine.Length - 1 <= lastSpaceIndex)
            {
                // only 2 segments or empty 2nd or 3rd segments
                // only 1 segment or empty first segment or not enough left for 2nd and 3rd segments
                throw new ODataException(Strings.ODataBatchReaderStream_InvalidRequestLine(requestLine));
            }

            httpMethod = requestLine.Substring(0, firstSpaceIndex);                                                       // Request - Http method
            string uriSegment         = requestLine.Substring(firstSpaceIndex + 1, lastSpaceIndex - firstSpaceIndex - 1); // Request - Request uri
            string httpVersionSegment = requestLine.Substring(lastSpaceIndex + 1);                                        // Request - Http version

            // Validate HttpVersion
            if (string.CompareOrdinal(ODataConstants.HttpVersionInBatching, httpVersionSegment) != 0)
            {
                throw new ODataException(Strings.ODataBatchReaderStream_InvalidHttpVersionSpecified(httpVersionSegment, ODataConstants.HttpVersionInBatching));
            }

            // NOTE: this method will throw if the method is not recognized.
            HttpUtils.ValidateHttpMethod(httpMethod);

            // Validate the HTTP method when reading a request
            if (this.batchStream.ChangeSetBoundary != null)
            {
                // allow all methods except for GET
                if (HttpUtils.IsQueryMethod(httpMethod))
                {
                    throw new ODataException(Strings.ODataBatch_InvalidHttpMethodForChangeSetRequest(httpMethod));
                }
            }

            requestUri = new Uri(uriSegment, UriKind.RelativeOrAbsolute);
            requestUri = ODataBatchUtils.CreateOperationRequestUri(requestUri, this.inputContext.MessageReaderSettings.BaseUri, this.urlResolver);
        }
예제 #3
0
        /// <summary>
        /// Creates an operation request message that can be used to write the operation content to.
        /// </summary>
        /// <param name="outputStream">The output stream underlying the operation message.</param>
        /// <param name="method">The HTTP method to use for the message to create.</param>
        /// <param name="requestUrl">The request URL for the message to create.</param>
        /// <param name="operationListener">The operation listener.</param>
        /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param>
        /// <returns>An <see cref="ODataBatchOperationRequestMessage"/> to write the request content to.</returns>
        internal static ODataBatchOperationRequestMessage CreateWriteMessage(
            Stream outputStream,
            string method,
            Uri requestUrl,
            IODataBatchOperationListener operationListener,
            IODataUrlResolver urlResolver)
        {
            Debug.Assert(outputStream != null, "outputStream != null");
            Debug.Assert(operationListener != null, "operationListener != null");

            Func <Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationWriteStream(outputStream, operationListener);

            return(new ODataBatchOperationRequestMessage(streamCreatorFunc, method, requestUrl, /*headers*/ null, operationListener, /*contentId*/ null, urlResolver, /*writing*/ true));
        }
예제 #4
0
        /// <summary>
        /// Creates an operation request message that can be used to read the operation content from.
        /// </summary>
        /// <param name="batchReaderStream">The batch stream underyling the operation response message.</param>
        /// <param name="method">The HTTP method to use for the message to create.</param>
        /// <param name="requestUrl">The request URL for the message to create.</param>
        /// <param name="headers">The headers to use for the operation request message.</param>
        /// <param name="operationListener">The operation listener.</param>
        /// <param name="contentId">The content-ID for the operation request message.</param>
        /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param>
        /// <returns>An <see cref="ODataBatchOperationRequestMessage"/> to read the request content from.</returns>
        internal static ODataBatchOperationRequestMessage CreateReadMessage(
            ODataBatchReaderStream batchReaderStream,
            string method,
            Uri requestUrl,
            ODataBatchOperationHeaders headers,
            IODataBatchOperationListener operationListener,
            string contentId,
            IODataUrlResolver urlResolver)
        {
            Debug.Assert(batchReaderStream != null, "batchReaderStream != null");
            Debug.Assert(operationListener != null, "operationListener != null");

            Func <Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationReadStream(batchReaderStream, headers, operationListener);

            return(new ODataBatchOperationRequestMessage(streamCreatorFunc, method, requestUrl, headers, operationListener, contentId, urlResolver, /*writing*/ false));
        }
        /// <summary>
        /// Creates an operation response message that can be used to read the operation content from.
        /// </summary>
        /// <param name="batchReaderStream">The batch stream underyling the operation response message.</param>
        /// <param name="statusCode">The status code to use for the operation response message.</param>
        /// <param name="headers">The headers to use for the operation response message.</param>
        /// <param name="contentId">The content-ID for the operation response message.</param>
        /// <param name="operationListener">The operation listener.</param>
        /// <param name="urlResolver">The (optional) URL resolver for the message to create.</param>
        /// <returns>An <see cref="ODataBatchOperationResponseMessage"/> that can be used to read the operation content.</returns>
        internal static ODataBatchOperationResponseMessage CreateReadMessage(
            ODataBatchReaderStream batchReaderStream,
            int statusCode,
            ODataBatchOperationHeaders headers,
            string contentId,
            IODataBatchOperationListener operationListener,
            IODataUrlResolver urlResolver)
        {
            Debug.Assert(batchReaderStream != null, "batchReaderStream != null");
            Debug.Assert(operationListener != null, "operationListener != null");

            Func <Stream> streamCreatorFunc = () => ODataBatchUtils.CreateBatchOperationReadStream(batchReaderStream, headers, operationListener);
            ODataBatchOperationResponseMessage responseMessage =
                new ODataBatchOperationResponseMessage(streamCreatorFunc, headers, operationListener, contentId, urlResolver, /*writing*/ false);

            responseMessage.statusCode = statusCode;
            return(responseMessage);
        }
예제 #6
0
        /// <summary>
        /// Reads a line (all bytes until a line feed) from the underlying stream.
        /// </summary>
        /// <returns>Returns the string that was read from the underyling stream (not including a terminating line feed), or null if the end of input was reached.</returns>
        private string ReadLine()
        {
            Debug.Assert(this.batchEncoding != null, "Batch encoding should have been established on first call to SkipToBoundary.");
            Debug.Assert(this.lineBuffer != null && this.lineBuffer.Length == LineBufferLength, "Line buffer should have been created.");

            // The number of bytes in the line buffer that make up the line.
            int lineBufferSize = 0;

            // Start with the pre-allocated line buffer array.
            byte[] bytesForString = this.lineBuffer;

            ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch;

            while (scanResult != ODataBatchReaderStreamScanResult.Match)
            {
                int byteCount, lineEndStartPosition, lineEndEndPosition;
                scanResult = this.batchBuffer.ScanForLineEnd(out lineEndStartPosition, out lineEndEndPosition);

                switch (scanResult)
                {
                case ODataBatchReaderStreamScanResult.NoMatch:
                    // Copy all the bytes in the batchBuffer into the result byte[] and then continue
                    byteCount = this.batchBuffer.NumberOfBytesInBuffer;
                    if (byteCount > 0)
                    {
                        // TODO: [Design] Consider security limits for data being read
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    if (this.underlyingStreamExhausted)
                    {
                        if (lineBufferSize == 0)
                        {
                            // If there's nothing more to pull from the underlying stream, and we didn't read anything
                            // in this invocation of ReadLine(), return null to indicate end of input.
                            return(null);
                        }

                        // Nothing more to read; stop looping
                        scanResult = ODataBatchReaderStreamScanResult.Match;
                        this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + byteCount);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength);
                    }

                    break;

                case ODataBatchReaderStreamScanResult.PartialMatch:
                    // We found the start of a line end in the buffer but could not verify whether we saw all of it.
                    // This can happen if a line end is represented as \r\n and we found \r at the very last position in the buffer.
                    // In this case we copy the bytes into the result byte[] and continue at the start of the line end; this will guarantee
                    // that the next scan will find the full line end, not find any additional bytes and then skip the full line end.
                    // It is safe to copy the string right here because we will also accept \r as a line end; we are just not sure whether there
                    // will be a subsequent \n.
                    // This can also happen if the last byte in the stream is \r.
                    byteCount = lineEndStartPosition - this.batchBuffer.CurrentReadPosition;
                    if (byteCount > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    if (this.underlyingStreamExhausted)
                    {
                        // Nothing more to read; stop looping
                        scanResult = ODataBatchReaderStreamScanResult.Match;
                        this.batchBuffer.SkipTo(lineEndStartPosition + 1);
                    }
                    else
                    {
                        this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ lineEndStartPosition);
                    }

                    break;

                case ODataBatchReaderStreamScanResult.Match:
                    // We found a line end in the buffer
                    Debug.Assert(lineEndStartPosition >= this.batchBuffer.CurrentReadPosition, "Line end must be at or after current position.");
                    Debug.Assert(lineEndEndPosition < this.batchBuffer.CurrentReadPosition + this.batchBuffer.NumberOfBytesInBuffer, "Line end must finish withing buffer range.");

                    byteCount = lineEndStartPosition - this.batchBuffer.CurrentReadPosition;
                    if (byteCount > 0)
                    {
                        ODataBatchUtils.EnsureArraySize(ref bytesForString, lineBufferSize, byteCount);
                        Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, bytesForString, lineBufferSize, byteCount);
                        lineBufferSize += byteCount;
                    }

                    this.batchBuffer.SkipTo(lineEndEndPosition + 1);
                    break;

                default:
                    throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadLine));
                }
            }

            Debug.Assert(bytesForString != null, "bytesForString != null");

            return(this.CurrentEncoding.GetString(bytesForString, 0, lineBufferSize));
        }