/// <summary> /// Creates an <see cref="ODataWriter" /> to write an entry. /// </summary> /// <returns>The created writer.</returns> /// <remarks>The write must flush the output when it's finished (inside the last Write call).</remarks> internal virtual ODataWriter CreateODataEntryWriter() { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Entry); }
internal static string GetStatusMessage(int statusCode) { DebugUtils.CheckNoExternalCallers(); // Non-localized messages for status codes. // These are the recommended reason phrases as per HTTP RFC 2616, Section 6.1.1 switch (statusCode) { case 100: return("Continue"); case 101: return("Switching Protocols"); case 200: return("OK"); case 201: return("Created"); case 202: return("Accepted"); case 203: return("Non-Authoritative Information"); case 204: return("No Content"); case 205: return("Reset Content"); case 206: return("Partial Content"); case 300: return("Multiple Choices"); case 301: return("Moved Permanently"); case 302: return("Found"); case 303: return("See Other"); case 304: return("Not Modified"); case 305: return("Use Proxy"); case 307: return("Temporary Redirect"); case 400: return("Bad Request"); case 401: return("Unauthorized"); case 402: return("Payment Required"); case 403: return("Forbidden"); case 404: return("Not Found"); case 405: return("Method Not Allowed"); case 406: return("Not Acceptable"); case 407: return("Proxy Authentication Required"); case 408: return("Request Time-out"); case 409: return("Conflict"); case 410: return("Gone"); case 411: return("Length Required"); case 412: return("Precondition Failed"); case 413: return("Request Entity Too Large"); case 414: return("Request-URI Too Large"); case 415: return("Unsupported Media Type"); case 416: return("Requested range not satisfiable"); case 417: return("Expectation Failed"); case 500: return("Internal Server Error"); case 501: return("Not Implemented"); case 502: return("Bad Gateway"); case 503: return("Service Unavailable"); case 504: return("Gateway Time-out"); case 505: return("HTTP Version not supported"); default: return("Unknown Status Code"); } }
/// <summary> /// Enumerates each charset part in the specified Accept-Charset header. /// </summary> /// <param name="headerValue">Non-null and non-empty header value for Accept-Charset.</param> /// <returns> /// A (non-sorted) enumeration of CharsetPart elements, which include /// a charset name and a quality (preference) value, normalized to 0-1000. /// </returns> private static IEnumerable <CharsetPart> AcceptCharsetParts(string headerValue) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(!String.IsNullOrEmpty(headerValue), "!String.IsNullOrEmpty(headerValuer)"); // PERF: optimize for common patterns. bool commaRequired = false; // Whether a comma should be found int headerIndex = 0; // Index of character being procesed on headerValue. int headerStart; // Index into headerValue for the start of the charset name. int headerNameEnd; // Index into headerValue for the end of the charset name (+1). int headerEnd; // Index into headerValue for this charset part (+1). int qualityValue; // Normalized qvalue for this charset. while (headerIndex < headerValue.Length) { if (SkipWhitespace(headerValue, ref headerIndex)) { yield break; } if (headerValue[headerIndex] == ',') { commaRequired = false; headerIndex++; continue; } if (commaRequired) { // Comma missing between charset elements. throw new ODataContentTypeException(Strings.HttpUtils_MissingSeparatorBetweenCharsets(headerValue)); } headerStart = headerIndex; headerNameEnd = headerStart; bool endReached = ReadToken(headerValue, ref headerNameEnd); if (headerNameEnd == headerIndex) { // Invalid (empty) charset name. throw new ODataContentTypeException(Strings.HttpUtils_InvalidCharsetName(headerValue)); } if (endReached) { qualityValue = 1000; headerEnd = headerNameEnd; } else { char afterNameChar = headerValue[headerNameEnd]; if (IsHttpSeparator(afterNameChar)) { if (afterNameChar == ';') { if (ReadLiteral(headerValue, headerNameEnd, ";q=")) { // Unexpected end of qvalue. throw new ODataContentTypeException(Strings.HttpUtils_UnexpectedEndOfQValue(headerValue)); } headerEnd = headerNameEnd + 3; ReadQualityValue(headerValue, ref headerEnd, out qualityValue); } else { qualityValue = 1000; headerEnd = headerNameEnd; } } else { // Invalid separator character. throw new ODataContentTypeException(Strings.HttpUtils_InvalidSeparatorBetweenCharsets(headerValue)); } } yield return(new CharsetPart(headerValue.Substring(headerStart, headerNameEnd - headerStart), qualityValue)); // Prepare for next charset; we require at least one comma before we process it. commaRequired = true; headerIndex = headerEnd; } }
/// <summary> /// Adds a property to the Properties collection which must have been previously populated by CreateNewEntry. /// </summary> /// <param name="properties">The value of the Properties property to add the property to.</param> /// <param name="propertyToAdd">The property to add.</param> internal static void AddPropertyToPropertiesList(IEnumerable <ODataProperty> properties, ODataProperty propertyToAdd) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(propertyToAdd != null, "propertyToAdd != null"); ReaderUtils.GetPropertiesList(properties).Add(propertyToAdd); }
/// <summary> /// Does an ordinal ignore case comparision of the given MIME type parameter name. /// </summary> /// <param name="parameterName1">First parameter name.</param> /// <param name="parameterName2">Second parameter name.</param> /// <returns>returns true if the parameter names are the same.</returns> internal static bool CompareMediaTypeParameterNames(string parameterName1, string parameterName2) { DebugUtils.CheckNoExternalCallers(); return(string.Equals(parameterName1, parameterName2, StringComparison.OrdinalIgnoreCase)); }
/// <summary> /// Asynchronous flush operation. This will flush all buffered bytes to the underlying stream through asynchronous writes. /// </summary> /// <returns>The task representing the asynchronous flush operation.</returns> internal Task FlushAsync() { DebugUtils.CheckNoExternalCallers(); return(this.FlushAsyncInternal()); }
/// <summary> /// Reads from the batch stream while ensuring that we stop reading at each boundary. /// </summary> /// <param name="userBuffer">The byte array to read bytes into.</param> /// <param name="userBufferOffset">The offset in the buffer where to start reading bytes into.</param> /// <param name="count">The number of bytes to read.</param> /// <returns>The number of bytes actually read.</returns> internal int ReadWithDelimiter(byte[] userBuffer, int userBufferOffset, int count) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(userBuffer != null, "userBuffer != null"); Debug.Assert(userBufferOffset >= 0 && userBufferOffset < userBuffer.Length, "Offset must be within the range of the user buffer."); Debug.Assert(count >= 0, "count >= 0"); Debug.Assert(this.batchEncoding != null, "Batch encoding should have been established on first call to SkipToBoundary."); if (count == 0) { // Nothing to read. return(0); } int remainingNumberOfBytesToRead = count; ODataBatchReaderStreamScanResult scanResult = ODataBatchReaderStreamScanResult.NoMatch; while (remainingNumberOfBytesToRead > 0 && scanResult != ODataBatchReaderStreamScanResult.Match) { int boundaryStartPosition, boundaryEndPosition; bool isEndBoundary, isParentBoundary; scanResult = this.batchBuffer.ScanForBoundary( this.CurrentBoundaries, remainingNumberOfBytesToRead, out boundaryStartPosition, out boundaryEndPosition, out isEndBoundary, out isParentBoundary); int bytesBeforeBoundaryStart; switch (scanResult) { case ODataBatchReaderStreamScanResult.NoMatch: // The boundary was not found in the buffer or after the required number of bytes to be read; // Check whether we can satisfy the full read request from the buffer // or whether we have to split the request and read more data into the buffer. if (this.batchBuffer.NumberOfBytesInBuffer >= remainingNumberOfBytesToRead) { // we can satisfy the full read request from the buffer Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, remainingNumberOfBytesToRead); this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + remainingNumberOfBytesToRead); return(count); } else { // we can only partially satisfy the read request int availableBytesToRead = this.batchBuffer.NumberOfBytesInBuffer; Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, availableBytesToRead); remainingNumberOfBytesToRead -= availableBytesToRead; userBufferOffset += availableBytesToRead; // we exhausted the buffer; if the underlying stream is not exceeded, refill the buffer if (this.underlyingStreamExhausted) { // We cannot fully satisfy the read request since there are not enough bytes in the stream. // Return the number of bytes we read. this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + availableBytesToRead); return(count - remainingNumberOfBytesToRead); } else { this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ ODataBatchReaderStreamBuffer.BufferLength); } } break; case ODataBatchReaderStreamScanResult.PartialMatch: // A partial match for the boundary was found at the end of the buffer. // If the underlying stream is not exceeded, refill the buffer. Otherwise return // the available bytes. if (this.underlyingStreamExhausted) { // We cannot fully satisfy the read request since there are not enough bytes in the stream. // Return the remaining bytes in the buffer independently of where a portentially boundary // start was detected since no full boundary can ever be detected if the stream is exhausted. int bytesToReturn = Math.Min(this.batchBuffer.NumberOfBytesInBuffer, remainingNumberOfBytesToRead); Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesToReturn); this.batchBuffer.SkipTo(this.batchBuffer.CurrentReadPosition + bytesToReturn); remainingNumberOfBytesToRead -= bytesToReturn; return(count - remainingNumberOfBytesToRead); } else { // Copy the bytes prior to the potential boundary start into the user buffer, refill the buffer and continue. bytesBeforeBoundaryStart = boundaryStartPosition - this.batchBuffer.CurrentReadPosition; Debug.Assert(bytesBeforeBoundaryStart < remainingNumberOfBytesToRead, "When reporting a partial match we should never have read all the remaining bytes to read (or more)."); Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesBeforeBoundaryStart); remainingNumberOfBytesToRead -= bytesBeforeBoundaryStart; userBufferOffset += bytesBeforeBoundaryStart; this.underlyingStreamExhausted = this.batchBuffer.RefillFrom(this.inputContext.Stream, /*preserveFrom*/ boundaryStartPosition); } break; case ODataBatchReaderStreamScanResult.Match: // We found the full boundary match; copy everything before the boundary to the buffer bytesBeforeBoundaryStart = boundaryStartPosition - this.batchBuffer.CurrentReadPosition; Debug.Assert(bytesBeforeBoundaryStart <= remainingNumberOfBytesToRead, "When reporting a full match we should never have read more than the remaining bytes to read."); Buffer.BlockCopy(this.batchBuffer.Bytes, this.batchBuffer.CurrentReadPosition, userBuffer, userBufferOffset, bytesBeforeBoundaryStart); remainingNumberOfBytesToRead -= bytesBeforeBoundaryStart; userBufferOffset += bytesBeforeBoundaryStart; // position the reader on the position of the boundary start this.batchBuffer.SkipTo(boundaryStartPosition); // return the number of bytes that were read return(count - remainingNumberOfBytesToRead); default: break; } } throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStream_ReadWithDelimiter)); }
/// <summary> /// Asynchronously writes an <see cref="ODataError"/> as the message payload. /// </summary> /// <param name="error">The error to write.</param> /// <param name="includeDebugInformation"> /// A flag indicating whether debug information (e.g., the inner error from the <paramref name="error"/>) should /// be included in the payload. This should only be used in debug scenarios. /// </param> /// <returns>A task representing the asynchronous operation of writing the error.</returns> /// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks> internal virtual Task WriteErrorAsync(ODataError error, bool includeDebugInformation) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Error); }
/// <summary> /// Asynchronously writes a singleton result of a $links query as the message payload. /// </summary> /// <param name="link">The link result to write as message payload.</param> /// <returns>A running task representing the writing of the link.</returns> /// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks> internal virtual Task WriteEntityReferenceLinkAsync(ODataEntityReferenceLink link) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.EntityReferenceLink); }
/// <summary> /// Asynchronously writes a service document with the specified <paramref name="defaultWorkspace"/> /// as message payload. /// </summary> /// <param name="defaultWorkspace">The default workspace to write in the service document.</param> /// <returns>A task representing the asynchronous operation of writing the service document.</returns> /// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks> internal virtual Task WriteServiceDocumentAsync(ODataWorkspace defaultWorkspace) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.ServiceDocument); }
/// <summary> /// Asynchronously writes an <see cref="ODataProperty"/> as message payload. /// </summary> /// <param name="property">The property to write</param> /// <returns>A task representing the asynchronous operation of writing the property.</returns> /// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks> internal virtual Task WritePropertyAsync(ODataProperty property) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Property); }
/// <summary> /// Asynchronously creates an <see cref="ODataParameterWriter" /> to write a parameter payload. /// </summary> /// <param name="functionImport">The function import whose parameters will be written.</param> /// <returns>A running task for the created parameter writer.</returns> /// <remarks>The write must flush the output when it's finished (inside the last Write call).</remarks> internal virtual Task <ODataParameterWriter> CreateODataParameterWriterAsync(IEdmFunctionImport functionImport) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Error); }
/// <summary> /// Asynchronously creates an <see cref="ODataBatchWriter" /> to write a batch of requests or responses. /// </summary> /// <param name="batchBoundary">The boundary string for the batch structure itself.</param> /// <returns>A running task for the created batch writer.</returns> /// <remarks>We don't plan to make this public!</remarks> /// <remarks> /// The write must flush the output when it's finished (inside the last Write call). /// Since we don't want to support batch format extensibility (at least not yet) this method should remain internal. /// </remarks> internal virtual Task <ODataBatchWriter> CreateODataBatchWriterAsync(string batchBoundary) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Batch); }
/// <summary> /// Asynchronously creates an <see cref="ODataCollectionWriter" /> to write a collection of primitive or complex values (as result of a service operation invocation). /// </summary> /// <returns>A running task for the created collection writer.</returns> /// <remarks>The write must flush the output when it's finished (inside the last Write call).</remarks> internal virtual Task <ODataCollectionWriter> CreateODataCollectionWriterAsync() { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Collection); }
/// <summary> /// Constructs an instance of the stream wrapper class. /// </summary> /// <param name="innerStream">Stream that is being wrapped.</param> internal NonDisposingStream(Stream innerStream) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(innerStream != null, "innerStream != null"); this.innerStream = innerStream; }
/// <summary> /// Asynchronously writes a single value as the message body. /// </summary> /// <param name="value">The value to write.</param> /// <returns>A running task representing the writing of the value.</returns> /// <remarks>It is the responsibility of this method to flush the output before the task finishes.</remarks> internal virtual Task WriteValueAsync(object value) { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.Value); }
/// <summary> /// Gets the metadata builder for this stream reference value. /// </summary> /// <returns>The metadata builder used to compute links.</returns> internal ODataEntityMetadataBuilder GetMetadataBuilder() { DebugUtils.CheckNoExternalCallers(); return(this.metadataBuilder); }
/// <summary> /// Writes the metadata document as the message body. /// </summary> /// <remarks>It is the responsibility of this method to flush the output before the method returns.</remarks> internal virtual void WriteMetadataDocument() { DebugUtils.CheckNoExternalCallers(); throw this.CreatePayloadKindNotSupportedException(ODataPayloadKind.MetadataDocument); }
/// <summary> /// Scans the current buffer for the specified boundary. /// </summary> /// <param name="boundaries">The boundary strings to search for; this enumerable is sorted from the inner-most boundary /// to the top-most boundary. The boundary strings don't include the leading line terminator or the leading dashes.</param> /// <param name="maxDataBytesToScan">Stop if no boundary (or boundary start) is found after this number of bytes.</param> /// <param name="boundaryStartPosition">The start position of the boundary or -1 if not found. /// Note that the start position is the first byte of the leading line terminator.</param> /// <param name="boundaryEndPosition">The end position of the boundary or -1 if not found. /// Note that the end position is the last byte of the trailing line terminator.</param> /// <param name="isEndBoundary">true if the boundary is an end boundary (followed by two dashes); otherwise false.</param> /// <param name="isParentBoundary">true if the detected boundary is the parent boundary; otherwise false.</param> /// <returns>An enumeration value indicating whether the boundary was completely, partially or not found in the buffer.</returns> internal ODataBatchReaderStreamScanResult ScanForBoundary( IEnumerable <string> boundaries, int maxDataBytesToScan, out int boundaryStartPosition, out int boundaryEndPosition, out bool isEndBoundary, out bool isParentBoundary) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(boundaries != null, "boundaries != null"); boundaryStartPosition = -1; boundaryEndPosition = -1; isEndBoundary = false; isParentBoundary = false; int lineScanStartIndex = this.currentReadPosition; while (true) { // NOTE: a boundary has to start with a line terminator followed by two dashes ('-'), // the actual boundary string, another two dashes (for an end boundary), // optional whitespace characters and another line terminator. // NOTE: for WCF DS compatibility we do not require the leading line terminator. int lineEndStartPosition, boundaryDelimiterStartPosition; ODataBatchReaderStreamScanResult lineEndScanResult = this.ScanForBoundaryStart( lineScanStartIndex, maxDataBytesToScan, out lineEndStartPosition, out boundaryDelimiterStartPosition); switch (lineEndScanResult) { case ODataBatchReaderStreamScanResult.NoMatch: // Did not find a line end or boundary delimiter in the buffer or after reading maxDataBytesToScan bytes. // Report no boundary match. return(ODataBatchReaderStreamScanResult.NoMatch); case ODataBatchReaderStreamScanResult.PartialMatch: // Found a partial line end or boundary delimiter at the end of the buffer but before reading maxDataBytesToScan. // Report a partial boundary match. boundaryStartPosition = lineEndStartPosition < 0 ? boundaryDelimiterStartPosition : lineEndStartPosition; return(ODataBatchReaderStreamScanResult.PartialMatch); case ODataBatchReaderStreamScanResult.Match: // Found a full line end or boundary delimiter start ('--') before reading maxDataBytesToScan or // hitting the end of the buffer. Start matching the boundary delimiters. // // Start with the expected boundary (the first one in the enumerable): // * if we find a full match - return match because we are done // * if we find a partial match - return partial match because we have to continue checking the expected boundary. // * if we find no match - we know that it is not the expected boundary; check the parent boundary (if it exists). isParentBoundary = false; foreach (string boundary in boundaries) { ODataBatchReaderStreamScanResult boundaryMatch = this.MatchBoundary( lineEndStartPosition, boundaryDelimiterStartPosition, boundary, out boundaryStartPosition, out boundaryEndPosition, out isEndBoundary); switch (boundaryMatch) { case ODataBatchReaderStreamScanResult.Match: return(ODataBatchReaderStreamScanResult.Match); case ODataBatchReaderStreamScanResult.PartialMatch: boundaryEndPosition = -1; isEndBoundary = false; return(ODataBatchReaderStreamScanResult.PartialMatch); case ODataBatchReaderStreamScanResult.NoMatch: // try the parent boundary (if any) or continue scanning boundaryStartPosition = -1; boundaryEndPosition = -1; isEndBoundary = false; isParentBoundary = true; break; default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStreamBuffer_ScanForBoundary)); } } // If we could not match the boundary, try again starting at the current boundary start index. Or if we already did that // move one character ahead. lineScanStartIndex = lineScanStartIndex == boundaryDelimiterStartPosition ? boundaryDelimiterStartPosition + 1 : boundaryDelimiterStartPosition; break; default: throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataBatchReaderStreamBuffer_ScanForBoundary)); } } }
/// <summary>Specifies whether the WCF data services server behavior is enabled.</summary> /// <param name="usesV1Provider">true if the server uses V1 provider, otherwise, false.</param> /// <param name="alwaysUseDefaultXmlNamespaceForRootElement">true if the server is configured to leave prefixes off all root elements and anything else in the same namespace, otherwise, false.</param> public void EnableWcfDataServicesServerBehavior(bool usesV1Provider, bool alwaysUseDefaultXmlNamespaceForRootElement) { DebugUtils.CheckNoExternalCallers(); this.EnableWcfDataServicesServerBehavior(usesV1Provider); this.alwaysUseDefaultXmlNamespaceForRootElement = alwaysUseDefaultXmlNamespaceForRootElement; }
/// <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).</returns> internal string ReadLine() { DebugUtils.CheckNoExternalCallers(); 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) { 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(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)); } } if (bytesForString == null) { return(string.Empty); } return(this.CurrentEncoding.GetString(bytesForString, 0, lineBufferSize)); }
/// <summary> /// Determines if there is a JSON padding function defined. /// </summary> /// <returns>True if the JsonPCallback property is not null or emtpy.</returns> internal bool HasJsonPaddingFunction() { DebugUtils.CheckNoExternalCallers(); return(!string.IsNullOrEmpty(this.JsonPCallback)); }
/// <summary> /// Given the value of the Properties collection previously populated by CreateNewEntry /// this methods returns the internal List of properties which can be modified to add new properties. /// </summary> /// <param name="properties">The value of the Properties property to get the list for.</param> /// <returns>The underlying list of properties to modify.</returns> internal static List <ODataProperty> GetPropertiesList(IEnumerable <ODataProperty> properties) { DebugUtils.CheckNoExternalCallers(); return(GetSourceListOfEnumerable(properties, "Properties")); }
/// <summary> /// Returns true to indicate that the annotation with the name <paramref name="annotationName"/> should not be writen, false otherwise. /// </summary> /// <param name="annotationName">The name of the annotation in question.</param> /// <returns>Returns true to indicate that the annotation with the name <paramref name="annotationName"/> should not be writen, false otherwise.</returns> internal bool ShouldSkipAnnotation(string annotationName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(this.Version.HasValue, "The version should be set by now."); return(this.Version.Value < ODataVersion.V3 || this.ShouldIncludeAnnotation != null && !this.ShouldIncludeAnnotation(annotationName)); }
/// <summary> /// Determines whether the given HTTP method is one that is accepted for queries. GET is accepted for queries. /// </summary> /// <param name="httpMethod">The HTTP method to check.</param> /// <returns>True if the given httpMethod is GET.</returns> internal static bool IsQueryMethod(string httpMethod) { DebugUtils.CheckNoExternalCallers(); return(string.CompareOrdinal(httpMethod, ODataConstants.MethodGet) == 0); }
/// <summary> /// Validates a collection item that was read to make sure it is valid (i.e., has the correct /// type name and type kind) with respect to the other items in the collection. /// </summary> /// <param name="collectionItemTypeName">The type name of the item from the payload.</param> /// <param name="collectionItemTypeKind">The type kind of the item from the payload.</param> internal void ValidateCollectionItem(string collectionItemTypeName, EdmTypeKind collectionItemTypeKind) { DebugUtils.CheckNoExternalCallers(); // Only primitive and complex values are allowed in collections if (collectionItemTypeKind != EdmTypeKind.Primitive && collectionItemTypeKind != EdmTypeKind.Complex) { throw new ODataException(Strings.CollectionWithoutExpectedTypeValidator_InvalidItemTypeKind(collectionItemTypeKind)); } if (this.itemTypeDerivedFromCollectionValue) { Debug.Assert(this.itemTypeName != null, "this.itemType != null"); // If the collection has a type name assign missing item type names from it. collectionItemTypeName = collectionItemTypeName ?? this.itemTypeName; // If we have a type name from the collection, make sure the type names of all items match this.ValidateCollectionItemTypeNameAndKind(collectionItemTypeName, collectionItemTypeKind); } else { // If we don't have a type name from the collection, store the type name and type kind of the first non-null item. if (this.itemTypeKind == EdmTypeKind.None) { // Compute the kind from the specified type name if available. this.itemTypeKind = collectionItemTypeName == null ? collectionItemTypeKind : ComputeExpectedTypeKind(collectionItemTypeName, out this.primitiveItemType); // If no payload type name is specified either default to Edm.String (for primitive type kinds) or leave the type name // null (for complex items without type name) if (collectionItemTypeName == null) { this.itemTypeKind = collectionItemTypeKind; if (this.itemTypeKind == EdmTypeKind.Primitive) { this.itemTypeName = Metadata.EdmConstants.EdmStringTypeName; this.primitiveItemType = EdmCoreModel.Instance.GetString(/*isNullable*/ false).PrimitiveDefinition(); } else { this.itemTypeName = null; this.primitiveItemType = null; } } else { this.itemTypeKind = ComputeExpectedTypeKind(collectionItemTypeName, out this.primitiveItemType); this.itemTypeName = collectionItemTypeName; } } if (collectionItemTypeName == null && collectionItemTypeKind == EdmTypeKind.Primitive) { // Default to Edm.String if no payload type is specified and the type kind is 'Primitive' collectionItemTypeName = Metadata.EdmConstants.EdmStringTypeName; } // Validate the expected and actual type names and type kinds. // Note that we compute the expected type kind from the expected type name and thus the payload // type kind (passed to this method) might be different from the computed expected type kind. this.ValidateCollectionItemTypeNameAndKind(collectionItemTypeName, collectionItemTypeKind); } }
/// <summary> /// Reads a token or quoted-string value from the header. /// </summary> /// <param name="headerName">Name of the header.</param> /// <param name="headerText">Header text.</param> /// <param name="textIndex">Parsing index in <paramref name="headerText"/>.</param> /// <param name="isQuotedString">Returns true if the value is a quoted-string, false if the value is a token.</param> /// <param name="createException">Func to create the appropriate exception to throw from the given error message.</param> /// <returns>The token or quoted-string value that was read from the header.</returns> internal static string ReadTokenOrQuotedStringValue(string headerName, string headerText, ref int textIndex, out bool isQuotedString, Func <string, Exception> createException) { DebugUtils.CheckNoExternalCallers(); //// NOTE: See RFC 2616, Sections 3.6 and 2.2 for the full grammar for HTTP parameter values //// //// parameter-value = token | quoted-string //// token = 1*<any CHAR except CTLs or separators> //// CHAR = <any US-ASCII character (octets 0 - 127)> //// CTL = <any US-ASCII control character //// (octets 0 - 31) and DEL (127)> //// separators = "(" | ")" | "<" | ">" | "@" //// | "," | ";" | ":" | "\" | <"> //// | "/" | "[" | "]" | "?" | "=" //// | "{" | "}" | SP | HT //// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) //// qdtext = <any TEXT except <">> //// TEXT = <any OCTET except CTLs, but including LWS> //// quoted-pair = "\" CHAR StringBuilder parameterValue = new StringBuilder(); // Check if the value is quoted. isQuotedString = false; if (textIndex < headerText.Length) { if (headerText[textIndex] == '\"') { textIndex++; isQuotedString = true; } } char currentChar = default(char); while (textIndex < headerText.Length) { currentChar = headerText[textIndex]; if (currentChar == '\\' || currentChar == '\"') { if (!isQuotedString) { throw createException(Strings.HttpUtils_EscapeCharWithoutQuotes(headerName, headerText, textIndex, currentChar)); } textIndex++; // End of quoted parameter value. if (currentChar == '\"') { break; } if (textIndex >= headerText.Length) { throw createException(Strings.HttpUtils_EscapeCharAtEnd(headerName, headerText, textIndex, currentChar)); } currentChar = headerText[textIndex]; } else { if (!isQuotedString && !IsHttpToken(currentChar)) { // If the given character is special, we stop processing. break; } if (isQuotedString && !IsValidInQuotedHeaderValue(currentChar)) { throw createException(Strings.HttpUtils_InvalidCharacterInQuotedParameterValue(headerName, headerText, textIndex, currentChar)); } } parameterValue.Append(currentChar); textIndex++; } if (isQuotedString && currentChar != '\"') { throw createException(Strings.HttpUtils_ClosingQuoteNotFound(headerName, headerText, textIndex)); } return(parameterValue.ToString()); }
/// <summary> /// Constructor. /// </summary> internal FeedWithoutExpectedTypeValidator() { DebugUtils.CheckNoExternalCallers(); }
/// <summary> /// Constructor which initializes the enumerable with an empty list storage. /// </summary> internal ReadOnlyEnumerable() : this(new List <T>()) { DebugUtils.CheckNoExternalCallers(); }
/// <summary> /// Creates an instance of the output context for the specified format. /// </summary> /// <param name="format">The format to create the context for.</param> /// <param name="message">The message to use.</param> /// <param name="encoding">The encoding to use.</param> /// <param name="messageWriterSettings">Configuration settings of the OData writer.</param> /// <param name="writingResponse">true if writing a response message; otherwise false.</param> /// <param name="model">The model to use.</param> /// <param name="urlResolver">The optional URL resolver to perform custom URL resolution for URLs written to the payload.</param> /// <returns>The newly created output context.</returns> internal static ODataOutputContext CreateOutputContext( ODataFormat format, ODataMessage message, Encoding encoding, ODataMessageWriterSettings messageWriterSettings, bool writingResponse, IEdmModel model, IODataUrlResolver urlResolver) { DebugUtils.CheckNoExternalCallers(); if (format == ODataFormat.Atom) { return(ODataAtomOutputContext.Create( format, message, encoding, messageWriterSettings, writingResponse, model, urlResolver)); } if (format == ODataFormat.VerboseJson) { return(ODataJsonOutputContext.Create( format, message, encoding, messageWriterSettings, writingResponse, model, urlResolver)); } if (format == ODataFormat.Metadata) { return(ODataMetadataOutputContext.Create( format, message, encoding, messageWriterSettings, writingResponse, model, urlResolver)); } if (format == ODataFormat.Batch || format == ODataFormat.RawValue) { return(ODataRawOutputContext.Create( format, message, encoding, messageWriterSettings, writingResponse, model, urlResolver)); } throw new ODataException(Strings.General_InternalError(InternalErrorCodes.ODataOutputContext_CreateOutputContext_UnrecognizedFormat)); }