/// <summary> /// Validates an <see cref="ODataFeed"/> to ensure all required information is specified and valid. /// </summary> /// <param name="feed">The feed to validate.</param> /// <param name="writingRequest">Flag indicating whether the feed is written as part of a request or a response.</param> /// <param name="version">The version of the OData protocol used for checking.</param> internal static void ValidateFeed(ODataFeed feed, bool writingRequest, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(feed != null, "feed != null"); // Verify non-empty ID if (string.IsNullOrEmpty(feed.Id)) { throw new ODataException(Strings.ODataWriter_FeedsMustHaveNonEmptyId); } // Verify next link if (feed.NextPageLink != null) { // Check that NextPageLink is not set for requests if (writingRequest) { throw new ODataException(Strings.ODataWriterCore_NextPageLinkInRequest); } // Verify version requirements ODataVersionChecker.CheckServerPaging(version); } }
/// <summary> /// Validates an <see cref="ODataAssociationLink"/> to ensure all required information is specified and valid. /// </summary> /// <param name="associationLink">The association link to validate.</param> /// <param name="version">The version of the OData protocol used for checking.</param> internal static void ValidateAssociationLink(ODataAssociationLink associationLink, ODataVersion version) { DebugUtils.CheckNoExternalCallers(); ODataVersionChecker.CheckAssociationLinks(version); // null link can not appear in the enumeration if (associationLink == null) { throw new ODataException(Strings.ODataWriter_AssociationLinkMustNotBeNull); } // Association link must have a non-empty name if (string.IsNullOrEmpty(associationLink.Name)) { throw new ODataException(Strings.ODataWriter_AssociationLinkMustSpecifyName); } // Association link must specify the Url if (associationLink.Url == null) { throw new ODataException(Strings.ODataWriter_AssociationLinkMustSpecifyUrl); } }
/// <summary> /// Returns a value of an HTTP header. /// </summary> /// <param name="headerName">The name of the header to get.</param> /// <returns>The value of the HTTP header, or null if no such header was present on the message.</returns> internal override string GetHeader(string headerName) { DebugUtils.CheckNoExternalCallers(); return(this.requestMessage.GetHeader(headerName)); }
/// <summary>Converts the specified value to a serializable string in ATOM format.</summary> /// <param name="value">Non-null value to convert.</param> /// <param name="result">The specified value converted to an ATOM string.</param> /// <param name="preserveWhitespace">A flag indicating whether whitespace needs to be preserved.</param> /// <returns>boolean value indicating conversion successful conversion</returns> internal static bool TryConvertPrimitiveToString(object value, out string result, out bool preserveWhitespace) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(value != null, "value != null"); result = null; preserveWhitespace = false; TypeCode typeCode = Type.GetTypeCode(value.GetType()); switch (typeCode) { case TypeCode.Boolean: result = ODataAtomConvert.ToString((bool)value); break; case TypeCode.Byte: result = ODataAtomConvert.ToString((byte)value); break; case TypeCode.DateTime: result = ODataAtomConvert.ToString((DateTime)value); break; case TypeCode.Decimal: result = ODataAtomConvert.ToString((decimal)value); break; case TypeCode.Double: result = ODataAtomConvert.ToString((double)value); break; case TypeCode.Int16: result = ODataAtomConvert.ToString((Int16)value); break; case TypeCode.Int32: result = ODataAtomConvert.ToString((Int32)value); break; case TypeCode.Int64: result = ODataAtomConvert.ToString((Int64)value); break; case TypeCode.SByte: result = ODataAtomConvert.ToString((SByte)value); break; case TypeCode.String: result = (string)value; preserveWhitespace = true; break; case TypeCode.Single: result = ODataAtomConvert.ToString((Single)value); break; default: byte[] bytes = value as byte[]; if (bytes != null) { result = ODataAtomConvert.ToString(bytes); break; } if (value is DateTimeOffset) { result = ODataAtomConvert.ToString((DateTimeOffset)value); break; } if (value is Guid) { result = ODataAtomConvert.ToString((Guid)value); break; } if (value is TimeSpan) { // Edm.Time result = ODataAtomConvert.ToString((TimeSpan)value); break; } return(false); } Debug.Assert(result != null, "result != null"); return(true); }
/// <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 ODataException(Strings.HttpUtils_MissingSeparatorBetweenCharsets(headerValue)); } headerStart = headerIndex; headerNameEnd = headerStart; bool endReached = ReadToken(headerValue, ref headerNameEnd); if (headerNameEnd == headerIndex) { // Invalid (empty) charset name. throw new ODataException(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 ODataException(Strings.HttpUtils_UnexpectedEndOfQValue(headerValue)); } headerEnd = headerNameEnd + 3; ReadQualityValue(headerValue, ref headerEnd, out qualityValue); } else { qualityValue = 1000; headerEnd = headerNameEnd; } } else { // Invalid separator character. throw new ODataException(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> /// Parses query options from a specified URI into a dictionary. /// </summary> /// <param name="uri">The uri to get the query options from.</param> /// <returns>The parsed query options.</returns> /// <remarks>This method returns <see cref="List<QueryOptionQueryToken>"/> with all the query options. /// Note that it is valid to include multiple query options with the same name.</remarks> internal static List <QueryOptionQueryToken> ParseQueryOptions(Uri uri) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(uri != null, "uri != null"); List <QueryOptionQueryToken> queryOptions = new List <QueryOptionQueryToken>(); string queryString = uri.Query; int length; if (queryString != null) { if (queryString.Length > 0 && queryString[0] == '?') { queryString = queryString.Substring(1); } length = queryString.Length; } else { length = 0; } for (int i = 0; i < length; i++) { int startIndex = i; int equalSignIndex = -1; while (i < length) { char ch = queryString[i]; if (ch == '=') { if (equalSignIndex < 0) { equalSignIndex = i; } } else if (ch == '&') { break; } i++; } string queryOptionsName = null; string queryOptionValue = null; if (equalSignIndex >= 0) { queryOptionsName = queryString.Substring(startIndex, equalSignIndex - startIndex); queryOptionValue = queryString.Substring(equalSignIndex + 1, (i - equalSignIndex) - 1); } else { queryOptionValue = queryString.Substring(startIndex, i - startIndex); } queryOptionsName = queryOptionsName == null ? null : Uri.UnescapeDataString(queryOptionsName); queryOptionValue = queryOptionValue == null ? null : Uri.UnescapeDataString(queryOptionValue); queryOptions.Add(new QueryOptionQueryToken { Name = queryOptionsName, Value = queryOptionValue }); if ((i == (length - 1)) && (queryString[i] == '&')) { queryOptions.Add(new QueryOptionQueryToken { Name = null, Value = string.Empty }); } } return(queryOptions); }
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> /// Does an ordinal ignore case comparision of the given media type names. /// </summary> /// <param name="mediaTypeName1">First media type name.</param> /// <param name="mediaTypeName2">Second media type name.</param> /// <returns>returns true if the media type names are the same.</returns> internal static bool CompareMediaTypeNames(string mediaTypeName1, string mediaTypeName2) { DebugUtils.CheckNoExternalCallers(); return(String.Equals(mediaTypeName1, mediaTypeName2, StringComparison.OrdinalIgnoreCase)); }
/// <summary> /// Indicates that the headers and request/response line have been written. /// Can be called only once per batch message and headers cannot be modified /// anymore after this method was called. /// </summary> internal void WriteMessageDataCompleted() { DebugUtils.CheckNoExternalCallers(); this.outputStream = null; }
/// <summary> /// Constructor. Creates a request message for an operation of a batch response. /// </summary> /// <param name="outputStream">The underlying stream to write the message to.</param> /// <param name="operationListener">Listener interface to be notified of operation changes.</param> internal ODataBatchOperationResponseMessage(Stream outputStream, IODataBatchOperationListener operationListener) { DebugUtils.CheckNoExternalCallers(); this.message = new ODataBatchOperationMessage(outputStream, operationListener); }
/// <summary> /// Initializes a new <see cref="MediaType"/> read-only instance. /// </summary> /// <param name="type">Type specification (for example, 'text').</param> /// <param name="subType">Sub-type specification (for example, 'plain').</param> /// <param name="parameter">A single parameter specified on the media type.</param> internal MediaType(string type, string subType, KeyValuePair <string, string> parameter) : this(type, subType, new KeyValuePair <string, string>[] { parameter }) { DebugUtils.CheckNoExternalCallers(); }
/// <summary> /// Initializes a new <see cref="MediaType"/> read-only instance. /// </summary> /// <param name="type">Type specification (for example, 'text').</param> /// <param name="subType">Sub-type specification (for example, 'plain').</param> internal MediaType(string type, string subType) : this(type, subType, (IList <KeyValuePair <string, string> >)null) { DebugUtils.CheckNoExternalCallers(); }
/// <summary> /// Gets a number of non-* matching type name parts and parameters; returns the number of matching type name parts or -1 if not matching at all. /// The <paramref name="matchingParameters"/> represent the number of matched parameters or -1 if there are no parameters to match and the candidate /// type also has no parameters. /// </summary> /// <param name="candidate">Candidate media type to match.</param> /// <param name="matchingParameters">The number of matched parameters or -1 if there are no parameters to match and the <paramref name="candidate"/> does not have parameters either.</param> /// <param name="qualityValue">The quality value of the candidate (or -1 if none is specified).</param> /// <param name="parameterCount">The number of parameters of this type that are not accept-parameters (and thus ignored).</param> /// <returns>Returns the number of matching type name parts or -1 if not matching at all. The <paramref name="matchingParameters"/> /// represent the number of matched parameters or -1 if there are no parameters to match and the candidate /// type also has no parameters. /// </returns> internal int GetMatchingParts(MediaType candidate, out int matchingParameters, out int qualityValue, out int parameterCount) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(candidate != null, "candidate must not be null."); int matchedNameParts = -1; if (this.type == "*") { matchedNameParts = 0; } else { if (HttpUtils.CompareMediaTypeNames(this.type, candidate.type)) { if (this.subType == "*") { // only type matches matchedNameParts = 1; } else if (HttpUtils.CompareMediaTypeNames(this.subType, candidate.subType)) { // both type and subtype match matchedNameParts = 2; } } } qualityValue = DefaultQualityValue; parameterCount = 0; matchingParameters = 0; // NOTE: we know that the candidates don't have accept parameters so we can rely // on the total parameter count. IList <KeyValuePair <string, string> > candidateParameters = candidate.Parameters; bool candidateHasParams = candidateParameters != null && candidateParameters.Count > 0; bool thisHasParams = this.parameters != null && this.parameters.Count > 0; if (thisHasParams) { for (int i = 0; i < this.parameters.Count; ++i) { string parameterName = this.parameters[i].Key; if (IsQualityValueParameter(parameterName)) { // once we hit the q-value in the parameters we know that only accept-params will follow // that don't contribute to the matching. Parse the quality value but don't continue processing // parameters. qualityValue = ParseQualityValue(this.parameters[i].Value.Trim()); break; } parameterCount = i + 1; if (candidateHasParams) { // find the current parameter name in the set of parameters of the candidate and compare the value; // if they match increase the result count string parameterValue; if (TryFindMediaTypeParameter(candidateParameters, parameterName, out parameterValue) && string.Compare(this.parameters[i].Value.Trim(), parameterValue.Trim(), StringComparison.OrdinalIgnoreCase) == 0) { matchingParameters++; } } } } // if we have no non-accept parameters special rules apply if (!thisHasParams || parameterCount == 0) { if (candidateHasParams) { if (matchedNameParts == 0 || matchedNameParts == 1) { // this is a media range specification using the '*' wildcard. // we assume that all parameters are matched for such ranges so the server // will pick the most specific one matchingParameters = candidateParameters.Count; } else { // the candidate type has parameters while this type has not; // return 0 to indicate that none of the candidate's parameters match. matchingParameters = 0; } } else { // neither this type nor the candidate have parameters; in this case we return -1 to indicate // that the candidate parameters are a perfect. matchingParameters = -1; } } return(matchedNameParts); }
/// <summary> /// Constructor /// </summary> /// <param name="stream">The underlying async stream to wrap. Note that only asynchronous write operation will be invoked on this stream.</param> internal AsyncBufferedStream(Stream stream) : this(stream, false) { DebugUtils.CheckNoExternalCallers(); }
/// <summary> /// Sets the value of an HTTP header. /// </summary> /// <param name="headerName">The name of the header to set.</param> /// <param name="headerValue">The value of the HTTP header or 'null' if the header should be removed.</param> internal override void SetHeader(string headerName, string headerValue) { DebugUtils.CheckNoExternalCallers(); this.requestMessage.SetHeader(headerName, headerValue); }
/// <summary> /// Try to determine the primitive type of the <paramref name="value"/> argument and return the name of the primitive type. /// </summary> /// <param name="value">The value to determine the type for.</param> /// <param name="typeName">The name of the primitive type of the <paramref name="value"/>.</param> /// <returns>True if the value is of a known primitive type; otherwise false.</returns> internal static bool TryGetPrimitiveTypeName(object value, out string typeName) { DebugUtils.CheckNoExternalCallers(); Debug.Assert(value != null, "value != null"); typeName = null; TypeCode typeCode = Type.GetTypeCode(value.GetType()); switch (typeCode) { case TypeCode.Boolean: typeName = EdmConstants.EdmBooleanTypeName; break; case TypeCode.Byte: typeName = EdmConstants.EdmByteTypeName; break; case TypeCode.DateTime: typeName = EdmConstants.EdmDateTimeTypeName; break; case TypeCode.Decimal: typeName = EdmConstants.EdmDecimalTypeName; break; case TypeCode.Double: typeName = EdmConstants.EdmDoubleTypeName; break; case TypeCode.Int16: typeName = EdmConstants.EdmInt16TypeName; break; case TypeCode.Int32: typeName = EdmConstants.EdmInt32TypeName; break; case TypeCode.Int64: typeName = EdmConstants.EdmInt64TypeName; break; case TypeCode.SByte: typeName = EdmConstants.EdmSByteTypeName; break; case TypeCode.String: typeName = EdmConstants.EdmStringTypeName; break; case TypeCode.Single: typeName = EdmConstants.EdmSingleTypeName; break; default: byte[] bytes = value as byte[]; if (bytes != null) { typeName = EdmConstants.EdmBinaryTypeName; break; } if (value is DateTimeOffset) { typeName = EdmConstants.EdmDateTimeOffsetTypeName; break; } if (value is Guid) { typeName = EdmConstants.EdmGuidTypeName; break; } if (value is TimeSpan) { // Edm.Time typeName = EdmConstants.EdmTimeTypeName; break; } return(false); } Debug.Assert(typeName != null, "typeName != null"); return(true); }
/// <summary> /// Returns true if the specified kind enum has the specified flags set. /// </summary> /// <param name="kind">The kind enum to test.</param> /// <param name="flag">The flag to look for.</param> /// <returns>true if the flag is set; false otherwise.</returns> internal static bool HasFlag(this ODataResourcePropertyKind kind, ODataResourcePropertyKind flag) { DebugUtils.CheckNoExternalCallers(); return((kind & flag) == flag); }
/// <summary> /// Constructs a new ODataMessage. /// </summary> protected ODataMessage() { DebugUtils.CheckNoExternalCallers(); }