/// <summary> /// Constructor. /// </summary> /// <param name="headers">The headers.</param> /// <param name="statusCode">The status code.</param> /// <param name="getResponseStream">A function returning the response stream.</param> /// <param name="underlyingResponseMessage">The underlying response message that should be disposed together with this instance.</param> internal HttpWebResponseMessage(HeaderCollection headers, int statusCode, Func <Stream> getResponseStream, IODataResponseMessage underlyingResponseMessage) : this(headers, statusCode, getResponseStream) { this.underlyingResponseMessage = underlyingResponseMessage; }
/// <summary> /// Synchronizely get the query set count from the server by executing the $count=value query /// </summary> /// <param name="context">The context</param> /// <returns>The server side count of the query set</returns> internal long GetQuerySetCount(DataServiceContext context) { Debug.Assert(null != context, "context is null"); Version requestVersion = this.QueryComponents(context.Model).Version; if (requestVersion == null || requestVersion.Major < 2) { // minimum DSV for $count is V2. requestVersion = Util.DataServiceVersion2; } QueryResult response = null; QueryComponents qc = this.QueryComponents(context.Model); Uri requestUri = qc.Uri; DataServiceRequest <long> serviceRequest = new DataServiceRequest <long>(requestUri, qc, null); HeaderCollection headers = new HeaderCollection(); // Validate and set the request DSV header headers.SetRequestVersion(requestVersion, context.MaxProtocolVersionAsVersion); context.Format.SetRequestAcceptHeaderForCount(headers); string httpMethod = XmlConstants.HttpMethodGet; ODataRequestMessageWrapper request = context.CreateODataRequestMessage( context.CreateRequestArgsAndFireBuildingRequest(httpMethod, requestUri, headers, context.HttpStack, null /*descriptor*/), new string[] { XmlConstants.HttpRequestAccept } /*headersToReset*/, null /*descriptor*/); response = new QueryResult(this, Util.ExecuteMethodName, serviceRequest, request, new RequestInfo(context), null, null); response.AllowDirectNetworkStreamReading = context.AllowDirectNetworkStreamReading; IODataResponseMessage responseMessage = null; try { responseMessage = response.ExecuteQuery(); if (HttpStatusCode.NoContent != response.StatusCode) { StreamReader sr = new StreamReader(response.GetResponseStream()); long r = -1; try { r = XmlConvert.ToInt64(sr.ReadToEnd()); } finally { sr.Close(); } return(r); } else { throw new DataServiceQueryException(Strings.DataServiceRequest_FailGetCount, response.Failure); } } catch (InvalidOperationException ex) { QueryOperationResponse operationResponse = null; operationResponse = response.GetResponse <long>(MaterializeAtom.EmptyResults); if (null != operationResponse) { operationResponse.Error = ex; throw new DataServiceQueryException(Strings.DataServiceException_GeneralError, ex, operationResponse); } throw; } finally { WebUtil.DisposeMessage(responseMessage); } }
/// <summary> /// Creates the result object for the specified query parameters. /// </summary> /// <param name="source">The source object for the request.</param> /// <param name="context">The data service context.</param> /// <param name="callback">The AsyncCallback delegate.</param> /// <param name="state">The state object for the callback.</param> /// <param name="method">async method name at the source.</param> /// <returns>Result representing the create request. The request has not been initiated yet.</returns> private QueryResult CreateExecuteResult(object source, DataServiceContext context, AsyncCallback callback, object state, string method) { Debug.Assert(null != context, "context is null"); QueryComponents qc = this.QueryComponents(context.Model); RequestInfo requestInfo = new RequestInfo(context); Debug.Assert( string.CompareOrdinal(XmlConstants.HttpMethodPost, qc.HttpMethod) == 0 || string.CompareOrdinal(XmlConstants.HttpMethodGet, qc.HttpMethod) == 0, "Only get and post are supported in the execute pipeline, which should have been caught earlier"); if (qc.UriOperationParameters != null) { Debug.Assert(qc.UriOperationParameters.Any(), "qc.UriOperationParameters.Any()"); Serializer serializer = new Serializer(requestInfo); this.RequestUri = serializer.WriteUriOperationParametersToUri(this.RequestUri, qc.UriOperationParameters); } HeaderCollection headers = new HeaderCollection(); if (string.CompareOrdinal(XmlConstants.HttpMethodPost, qc.HttpMethod) == 0) { if (qc.BodyOperationParameters == null) { // set the content length to be 0 if there are no operation parameters. headers.SetHeader(XmlConstants.HttpContentLength, "0"); } else { context.Format.SetRequestContentTypeForOperationParameters(headers); } } // Validate and set the request DSV and MDSV header headers.SetRequestVersion(qc.Version, requestInfo.MaxProtocolVersionAsVersion); requestInfo.Format.SetRequestAcceptHeaderForQuery(headers, qc); // We currently do not have a descriptor to expose to the user for invoking something through Execute. Ideally we could expose an OperationDescriptor. ODataRequestMessageWrapper requestMessage = new RequestInfo(context).WriteHelper.CreateRequestMessage(context.CreateRequestArgsAndFireBuildingRequest(qc.HttpMethod, this.RequestUri, headers, context.HttpStack, null /*descriptor*/)); requestMessage.FireSendingRequest2(null /*descriptor*/); QueryResult queryResult = null; if (qc.BodyOperationParameters != null) { Debug.Assert( string.CompareOrdinal(XmlConstants.HttpMethodPost, qc.HttpMethod) == 0, "qc.HttpMethod == XmlConstants.HttpMethodPost"); Debug.Assert(qc.BodyOperationParameters.Any(), "unexpected body operation parameter count of zero."); Serializer serializer = new Serializer(requestInfo); serializer.WriteBodyOperationParameters(qc.BodyOperationParameters, requestMessage); // pass in the request stream so that request payload can be written to the http webrequest. queryResult = new QueryResult(source, method, this, requestMessage, requestInfo, callback, state, requestMessage.CachedRequestStream); } else { queryResult = new QueryResult(source, method, this, requestMessage, requestInfo, callback, state); } return(queryResult); }
/// <summary> /// Fire SendingRequest event if its conditions are met. /// If the user has a handler for BuildingRequest, we will throw. /// If the user has no BuildingRequest handlers but does have a SendingRequest2 handler, we silently do not fire this event (this is shipped 5.0 behavior). /// </summary> private void FireSendingRequest() { // Do not fire SendingRequest event when user tries to wrap the HttpWebRequestMessage if (this.fireSendingRequestMethodCalled || this.requestInfo == null) { return; } // We need to set this before SendingRequest event is fired so that // GetStream method can throw if it is called from SendingRequest event. this.fireSendingRequestMethodCalled = true; HeaderCollection cachedHeaders = null; if (this.requestInfo.HasSendingRequestEventHandlers) { // Before firing SendingRequest event, we need to cache all the header values so that // we can reset them to the original values after SendingRequest event has been fired. cachedHeaders = new HeaderCollection(); foreach (var header in this.Headers) { cachedHeaders.SetHeader(header.Key, header.Value); } #if PORTABLELIB cachedHeaders.SetHeader(XmlConstants.HttpContentLength, this.httpRequest.Headers[XmlConstants.HttpContentLength]); #endif #if !ASTORIA_LIGHT && !PORTABLELIB // Content-Length and accept header does not show up in the header collection at all. // Hence adding it explicitly, since we reset the content length header // after firing SendingRequest event cachedHeaders.SetHeader(XmlConstants.HttpContentLength, this.httpRequest.ContentLength.ToString(CultureInfo.InvariantCulture)); #endif } // Fires whenever a new HttpWebRequest has been created // The event fires early - before the client library sets many of its required property values. // This ensures the client library has the last say on the value of mandated properties // such as the HTTP verb being used for the request. if (this.requestInfo.HasSendingRequestEventHandlers) { System.Net.WebHeaderCollection requestHeaders; #if !ASTORIA_LIGHT requestHeaders = this.httpRequest.Headers; SendingRequestEventArgs args = new SendingRequestEventArgs(this.httpRequest, requestHeaders); #else requestHeaders = this.httpRequest.CreateEmptyWebHeaderCollection(); /* Also set header for SL, MaxDataServcieVersion is required header */ foreach (var head in this.Headers) { if (head.Key == XmlConstants.HttpMaxDataServiceVersion) { requestHeaders[XmlConstants.HttpMaxDataServiceVersion] = head.Value; break; } } SendingRequestEventArgs args = new SendingRequestEventArgs(null, requestHeaders); #endif this.requestInfo.FireSendingRequest(args); #if !ASTORIA_LIGHT if (!Object.ReferenceEquals(args.Request, this.httpRequest)) { this.httpRequest = (System.Net.HttpWebRequest)args.Request; } #else // apply all headers to the request foreach (string key in requestHeaders.AllKeys) { this.httpRequest.Headers[key] = requestHeaders[key]; } #endif HttpWebRequestMessage.SetHeaderValues(this, cachedHeaders, this.Method); } }
/// <summary> /// The reason for adding this method is that we have seen a couple of asserts that we were not able to figure out why they are getting fired. /// So added this method which returns the current headers as well as cached headers as string and we display that in the assert message. /// </summary> /// <param name="currentHeaders">current header values.</param> /// <param name="cachedHeaders">cached header values.</param> /// <returns>returns a string which contains both current and cached header names.</returns> private static string GetHeaderValues(IEnumerable <KeyValuePair <string, string> > currentHeaders, HeaderCollection cachedHeaders) { StringBuilder sb = new StringBuilder(); string separator = String.Empty; sb.Append("Current Headers: "); foreach (var header in currentHeaders) { sb.Append(separator); sb.Append(header.Key); separator = ", "; } sb.Append(". Headers fired in SendingRequest: "); separator = String.Empty; foreach (string name in cachedHeaders.HeaderNames) { sb.Append(separator); sb.Append(name); separator = ", "; } return(sb.ToString()); }
/// <summary> /// Sets the value of the Accept header to the appropriate value for the current format. /// </summary> /// <param name="headers">The headers to modify.</param> internal void SetRequestAcceptHeader(HeaderCollection headers) { this.SetAcceptHeaderAndCharset(headers, this.ChooseMediaType(/*valueIfUsingAtom*/ MimeApplicationAtomOrXml, false)); }
/// <summary> /// Set the header values on the request. /// </summary> /// <param name="requestMessage">IODataRequestMessage instance containing all the headers.</param> /// <param name="cachedHeaders">Dictionary of cached headers.</param> /// <param name="effectiveHttpMethod">The Effective http method.</param> private static void SetHeaderValues(HttpWebRequestMessage requestMessage, HeaderCollection cachedHeaders, string effectiveHttpMethod) { // DevNote(shank): We used to set request.AllowWriteStreamBuffering to true here. This is now removed // as it prevents customers from enabling HTTP streaming by setting it to false in the // SendingRequest event. This is because this method is called twice by CreateGetRequest(), // before *and* after the SendingRequest event (see DevNote in CreateGetRequest()). // We also do *not* set it to false by default for media resource requests. Enabling // streaming requires additional actions to be performed by the customer, e.g., sending // chunks if content-length is not specified, and pre-authenticating with a HEAD request. #if !ASTORIA_LIGHT && !PORTABLELIB bool removeXMethod = true; #endif Debug.Assert(requestMessage.requestInfo != null, "This method is called from FireSendingRequest. Hence requestInfo should never be null."); HttpWebRequest request = requestMessage.httpRequest; string method = requestMessage.Method; string contentType = null; cachedHeaders.TryGetHeader(XmlConstants.HttpContentType, out contentType); if (string.CompareOrdinal(effectiveHttpMethod, XmlConstants.HttpMethodGet) != 0) { if (string.CompareOrdinal(effectiveHttpMethod, XmlConstants.HttpMethodDelete) == 0) { // In V2, we always use to over-write content type after sendingRequest event has been called. // So doing the same for delete requests. // TODO: no need to override this if it was set in building request; just override it was changed elsewhere? Debug.Assert(String.IsNullOrEmpty(contentType), "Content-Type must not be specified for delete operation"); request.ContentType = null; SetHttpWebRequestContentLength(request, 0); } else { Debug.Assert(!String.IsNullOrEmpty(contentType), "Content-Type must be specified for non get and non delete operations"); request.ContentType = contentType; } if (requestMessage.requestInfo.UsePostTunneling && (string.CompareOrdinal(effectiveHttpMethod, XmlConstants.HttpMethodPost) != 0)) { Debug.Assert(effectiveHttpMethod != null, "expected an effectiveHttpMethod"); request.Headers[XmlConstants.HttpXMethod] = effectiveHttpMethod; method = XmlConstants.HttpMethodPost; #if !ASTORIA_LIGHT && !PORTABLELIB removeXMethod = false; #endif } } // We cannot assert that the content type for GET request is null, since in some scenarios (e.g. GetReadStream), // the user can pass the content type in the API and they can set the content type to something. In those // scenarios, we set the content type as specified by the user and not check for anything. #if !ASTORIA_LIGHT && !PORTABLELIB // Removing the If-Match header so that if it was accidently written when it should not // have, it will get removed after the event. The correct value will be written again // when reseting the headers. request.Headers.Remove(HttpRequestHeader.IfMatch); // alternate HttpXMethod header doesn't work if (removeXMethod) { request.Headers.Remove(XmlConstants.HttpXMethod); } #endif request.Method = method; // Reset the list of headers, if any if (requestMessage.headersToReset != null) { foreach (string headerName in requestMessage.headersToReset) { #if ASTORIA_LIGHT || PORTABLELIB // Ignoring content length header since its not supported in silverlight if (string.Equals(headerName, XmlConstants.HttpContentLength, StringComparison.OrdinalIgnoreCase)) { continue; } #endif HttpWebRequestMessage.SetHeaderValue(request, headerName, cachedHeaders.GetHeader(headerName)); } } }
/// <summary> /// Sets the value of the ContentType header on the specified links request to the appropriate value for the current format. /// </summary> /// <param name="headers">Dictionary of request headers.</param> internal void SetRequestContentTypeForLinks(HeaderCollection headers) { this.SetRequestContentTypeHeader(headers, this.ChooseMediaType(/*valueIfUsingAtom*/ MimeApplicationXml, false)); }
/// <summary> /// Asks the context to Fire the BuildingRequest event and get RequestMessageArgs. /// </summary> /// <param name="method">Http method for the request.</param> /// <param name="requestUri">Base Uri for the request.</param> /// <param name="headers">Request headers.</param> /// <param name="httpStack">HttpStack to use.</param> /// <param name="descriptor">Descriptor for the request, if there is one.</param> /// <returns>A new RequestMessageArgs object for building the request message.</returns> internal BuildingRequestEventArgs CreateRequestArgsAndFireBuildingRequest(string method, Uri requestUri, HeaderCollection headers, HttpStack httpStack, Descriptor descriptor) { return(this.Context.CreateRequestArgsAndFireBuildingRequest(method, requestUri, headers, httpStack, descriptor)); }
/// <summary> /// Sets the value of the Accept header for a count request (will set it to 'multipart/mixed'). /// </summary> /// <param name="headers">The headers to modify.</param> internal void SetRequestAcceptHeaderForBatch(HeaderCollection headers) { this.SetAcceptHeaderAndCharset(headers, MimeMultiPartMixed); }
/// <summary> /// Sets the value of the Content-Type header a request with operation parameters to the appropriate value for the current format. /// </summary> /// <param name="headers">Dictionary of request headers.</param> internal void SetRequestContentTypeForOperationParameters(HeaderCollection headers) { // Note: There has never been an atom or xml format for parameters, so atom really means V3 default here. this.SetRequestContentTypeHeader(headers, this.ChooseMediaType(/*valueIfUsingAtom*/ MimeApplicationJsonODataVerbose, false)); }
/// <summary> /// Sets the value of the Accept header for a count request (will set it to 'text/plain'). /// </summary> /// <param name="headers">The headers to modify.</param> internal void SetRequestAcceptHeaderForCount(HeaderCollection headers) { this.SetAcceptHeaderAndCharset(headers, XmlConstants.MimeTextPlain); }
/// <summary> /// Sets the value of the Accept header for a stream request (will set it to '*/*'). /// </summary> /// <param name="headers">The headers to modify.</param> internal void SetRequestAcceptHeaderForStream(HeaderCollection headers) { this.SetAcceptHeaderAndCharset(headers, XmlConstants.MimeAny); }
/// <summary> /// Sets the value of the Accept header for a query. /// </summary> /// <param name="headers">The headers to modify.</param> /// <param name="components">The query components for the request.</param> internal void SetRequestAcceptHeaderForQuery(HeaderCollection headers, QueryComponents components) { this.SetAcceptHeaderAndCharset(headers, this.ChooseMediaType(/*valueIfUsingAtom*/ MimeApplicationAtomOrXml, components.HasSelectQueryOption)); }
/// <summary> /// constructor /// </summary> /// <param name="headers">HTTP headers</param> internal OperationResponse(HeaderCollection headers) { Debug.Assert(null != headers, "null headers"); this.headers = headers; }
/// <summary>Initializes a new instance of the <see cref="T:System.Data.Services.Client.ChangeOperationResponse" /> class. </summary> /// <param name="headers">HTTP headers</param> /// <param name="descriptor">response object containing information about resources that got changed.</param> internal ChangeOperationResponse(HeaderCollection headers, Descriptor descriptor) : base(headers) { Debug.Assert(descriptor != null, "descriptor != null"); this.descriptor = descriptor; }
/// <summary> /// constructor /// </summary> /// <param name="headers">HTTP headers</param> /// <param name="query">original query</param> /// <param name="results">retrieved objects</param> internal QueryOperationResponse(HeaderCollection headers, DataServiceRequest query, MaterializeAtom results) : base(headers, query, results) { }
/// <summary> /// constructor /// </summary> /// <param name="headers">HTTP headers</param> /// <param name="query">original query</param> /// <param name="results">retrieved objects</param> internal QueryOperationResponse(HeaderCollection headers, DataServiceRequest query, MaterializeAtom results) : base(headers) { this.query = query; this.results = results; }