/// <summary>Writes the request into a stream (including the verb, headers and everything)</summary> /// <param name="stream">The stream to write the request into.</param> /// <param name="serviceRoot">The service root URI to target the request at. If null the request uri will be left as is.</param> public void WriteRequest(Stream stream, Uri serviceRoot) { StringBuilder sb = new StringBuilder(); string requestUri = this.RequestUriString; if (serviceRoot != null) { requestUri = this.RequestUriString; if (requestUri.StartsWith("/")) { requestUri = requestUri.Substring(1); } requestUri = new Uri(serviceRoot, requestUri).AbsoluteUri; } sb.AppendLine(this.HttpMethod + " " + requestUri + " HTTP/1.1"); InMemoryWebRequest.WriteHeadersToStringBuilder(sb, GetAllRequestHeaders(this)); sb.AppendLine(); InMemoryWebRequest.WriteStringToStream(stream, sb.ToString()); if (this.RequestStream != null) { this.RequestStream.Seek(0, SeekOrigin.Begin); TestUtil.CopyStream(this.RequestStream, stream); } }
/// <summary>Creates the in-memory response from a stream.</summary> /// <param name="stream">The stream to read.</param> /// <returns>The newly created request object with the response properties filled with the data from the stream.</returns> public static InMemoryWebRequest FromResponse(Stream stream) { InMemoryWebRequest response = new InMemoryWebRequest(); response.ParseResponse(stream); return(response); }
/// <summary>Writes the batch as an HTTP request into a stream (including the verb, headers and everything).</summary> /// <param name="stream">The stream to write the request into.</param> /// <param name="serviceRoot">The uri of the service root to send the request to.</param> public void WriteRequest(Stream stream, Uri serviceRoot) { InMemoryWebRequest request = new InMemoryWebRequest(); this.WriteRequest(request); request.WriteRequest(stream, serviceRoot); }
/// <summary>Returns a new ProcessRequestOverride func which will take the request, parse it as batch, /// unwrap the first part from it and run the passed in func on it, then wrapping the response in batch again.</summary> /// <param name="partProcessRequest">The func to run on the single part in the batch</param> public static Func<InMemoryWebRequest, InMemoryWebRequest> UnwrapSingleBatchPart(Func<InMemoryWebRequest, InMemoryWebRequest> partProcessRequest) { return (request) => { var batch = BatchWebRequest.FromRequest(request); if (batch.Parts.Count > 1 || (batch.Parts.Count == 0 && (batch.Changesets.Count != 1 || batch.Changesets[0].Parts.Count != 1))) throw new Exception("The batch has more than one part in it."); IList<InMemoryWebRequest> parts = null; if (batch.Parts.Count > 0) { parts = batch.Parts; } else { parts = batch.Changesets[0].Parts; } var part = parts[0]; string contentId; part.RequestHeaders.TryGetValue("Content-ID", out contentId); parts.Clear(); part = partProcessRequest(part); part.ResponseHeaders["Content-ID"] = contentId; parts.Add(part); var response = new InMemoryWebRequest(); batch.WriteResponse(response); return response; }; }
/// <summary>Creates the in-memory request from a stream.</summary> /// <param name="stream">The stream to read.</param> /// <param name="serviceRoot">The service root uri the request was targeted at (if null the request URI will be left as is).</param> /// <returns>The newly created request representation.</returns> public static InMemoryWebRequest FromRequest(Stream stream, Uri serviceRoot) { InMemoryWebRequest request = new InMemoryWebRequest(); request.ParseRequest(stream, serviceRoot); return(request); }
/// <summary>Creates a batch request from an in-memory request representation.</summary> /// <param name="request">The request to parse into a batch.</param> /// <returns>The newly created batch request.</returns> public static BatchWebRequest FromRequest(InMemoryWebRequest request) { BatchWebRequest batch = new BatchWebRequest(); Assert.AreEqual("/$batch", request.RequestUriString, "The request is not a batch request, it does not target the /$batch uri/"); batch.ParseBatchContent(request.GetRequestStream(), request.RequestContentType, false, false); return batch; }
/// <summary>Writes the batch as a response into a stream (including the status line, headers and everything)</summary> /// <param name="stream">The stream to write the response to.</param> public void WriteResponse(Stream stream) { InMemoryWebRequest response = new InMemoryWebRequest(); this.WriteResponse(response); response.WriteResponse(stream); }
/// <summary>Creates a batch request from an in-memory request representation.</summary> /// <param name="request">The request to parse into a batch.</param> /// <returns>The newly created batch request.</returns> public static BatchWebRequest FromRequest(InMemoryWebRequest request) { BatchWebRequest batch = new BatchWebRequest(); Assert.AreEqual("/$batch", request.RequestUriString, "The request is not a batch request, it does not target the /$batch uri/"); batch.ParseBatchContent(request.GetRequestStream(), request.RequestContentType, false, false); return(batch); }
/// <summary>Writes the batch as a response into an in-memory request (sets up properties and such).</summary> /// <param name="request">The request to apply the batch as a response to.</param> public void WriteResponse(InMemoryWebRequest request) { request.SetResponseStatusCode(200); string boundary = "boundary_" + Guid.NewGuid().ToString(); request.ResponseHeaders["Content-Type"] = String.Format("{0}; boundary={1}", UnitTestsUtil.MimeMultipartMixed, boundary); request.SetResponseStream(this.CreateBatchContent(true, boundary)); }
private bool ParseBatchPart(bool isResponse, TextReader reader, InMemoryWebRequest part, string boundary, string endboundary) { bool result = false; string line; if (isResponse) { part.ParseResponseStatus(reader); } else { part.ParseRequestVerb(reader); } if (isResponse) { part.ResponseHeaders.Clear(); InMemoryWebRequest.ParseHeaders(reader, part.ResponseHeaders); } else { InMemoryWebRequest.ParseHeaders(reader, part.RequestHeaders); InMemoryWebRequest.ApplyHeadersToProperties(part); } StringBuilder sb = new StringBuilder(); string lastLine = null; while ((line = reader.ReadLine()) != null) { if (line == boundary) { break; } if (line == endboundary) { result = true; break; } if (lastLine != null) { sb.AppendLine(lastLine); } lastLine = line; } // The last line must not end with a newline - the batch adds it there, but it's not actually part of the content sb.Append(lastLine); if (isResponse) { part.SetResponseStream(new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()))); } else { part.SetRequestStreamAsText(sb.ToString()); } return(result); }
/// <summary>Creates a batch response from an in-memory response representation.</summary> /// <param name="response">The in-memory request from which to read the response.</param> /// <returns>The newly created batch response.</returns> public static BatchWebRequest FromResponse(InMemoryWebRequest response) { BatchWebRequest batch = new BatchWebRequest(); if (response.ResponseStatusCode != 200 && response.ResponseStatusCode != 202) { Assert.Fail("The batch response must be 200 or 202, otherwise its content will not be a batch response."); } batch.ParseBatchContent(response.GetResponseStream(), response.ResponseContentType, true, false); return batch; }
/// <summary>Creates a batch response from an in-memory response representation.</summary> /// <param name="response">The in-memory request from which to read the response.</param> /// <returns>The newly created batch response.</returns> public static BatchWebRequest FromResponse(InMemoryWebRequest response) { BatchWebRequest batch = new BatchWebRequest(); if (response.ResponseStatusCode != 200 && response.ResponseStatusCode != 202) { Assert.Fail("The batch response must be 200 or 202, otherwise its content will not be a batch response."); } batch.ParseBatchContent(response.GetResponseStream(), response.ResponseContentType, true, false); return(batch); }
/// <summary>Creates the in-memory response from another TestWebRequest, copying its reponse.</summary> /// <param name="sourceResponse">The request to read the response from.</param> /// <returns>The newly create request object with the response proeprties filled with the data from the request response.</returns> public static InMemoryWebRequest FromResponse(TestWebRequest sourceResponse) { InMemoryWebRequest response = new InMemoryWebRequest(); response.SetResponseStatusCode(sourceResponse.ResponseStatusCode); foreach (var header in GetAllResponseHeaders(sourceResponse)) { response.ResponseHeaders[header.Key] = header.Value; } using (Stream responseStream = sourceResponse.GetResponseStream()) { response.SetResponseStream(responseStream); } return response; }
private void ParseResponse(Stream stream) { MemoryStream memoryStream = new MemoryStream(); TestUtil.CopyStream(stream, memoryStream); memoryStream.Position = 0; using (TextReader reader = new StreamReader(memoryStream)) { this.ParseResponseStatus(reader); this.ResponseHeaders.Clear(); InMemoryWebRequest.ParseHeaders(reader, this.ResponseHeaders); reader.ReadLine(); this.SetResponseStream(new MemoryStream(Encoding.UTF8.GetBytes(reader.ReadToEnd()))); } }
/// <summary>Creates the in-memory response from another TestWebRequest, copying its reponse.</summary> /// <param name="sourceResponse">The request to read the response from.</param> /// <returns>The newly create request object with the response proeprties filled with the data from the request response.</returns> public static InMemoryWebRequest FromResponse(TestWebRequest sourceResponse) { InMemoryWebRequest response = new InMemoryWebRequest(); response.SetResponseStatusCode(sourceResponse.ResponseStatusCode); foreach (var header in GetAllResponseHeaders(sourceResponse)) { response.ResponseHeaders[header.Key] = header.Value; } using (Stream responseStream = sourceResponse.GetResponseStream()) { response.SetResponseStream(responseStream); } return(response); }
private void ParseRequest(Stream stream, Uri serviceRoot) { MemoryStream memoryStream = new MemoryStream(); TestUtil.CopyStream(stream, memoryStream); memoryStream.Position = 0; using (TextReader reader = new StreamReader(memoryStream)) { this.ParseRequestVerb(reader); this.RequestHeaders.Clear(); InMemoryWebRequest.ParseHeaders(reader, this.RequestHeaders); ApplyHeadersToProperties(this); reader.ReadLine(); this.SetRequestStreamAsText(reader.ReadToEnd()); } }
/// <summary>Writes the response part of the request into a stream (including the status, headers and everything)</summary> /// <param name="stream">The stream to write the response into.</param> public void WriteResponse(Stream stream) { System.Net.HttpStatusCode responseStatusCode = (System.Net.HttpStatusCode) this.ResponseStatusCode; StringBuilder sb = new StringBuilder(); sb.AppendLine("HTTP/1.1 " + this.ResponseStatusCode.ToString() + " " + responseStatusCode.ToString()); InMemoryWebRequest.WriteHeadersToStringBuilder(sb, this.ResponseHeaders); sb.AppendLine(); InMemoryWebRequest.WriteStringToStream(stream, sb.ToString()); if (this.responseStream != null) { TestUtil.CopyStream(this.GetResponseStream(), stream); } }
private void ParseBatchContent(Stream contentStream, string contentType, bool isResponse, bool matchToExisting) { int partIndex = 0; int changesetIndex = 0; if (!matchToExisting) { this.changesets = new List <Changeset>(); this.parts = new List <InMemoryWebRequest>(); } MemoryStream memoryStream = new MemoryStream(); // Create a copy since StreamReader will Close the stream, which might not be what we want TestUtil.CopyStream(contentStream, memoryStream); memoryStream.Position = 0; using (TextReader reader = new StreamReader(memoryStream)) { Assert.IsTrue(contentType.StartsWith("multipart/mixed; "), "Response is not a batch response. Expecting 'multipart.mixed', got '" + contentType + "'."); string boundary = "--" + contentType.Substring(contentType.IndexOf("; boundary=") + 11).Trim(); string endboundary = boundary + "--"; string line; while ((line = reader.ReadLine()) != null) { if (line == boundary) { break; } if (line == endboundary) { Assert.IsTrue(partIndex == this.parts.Count, "The response didn't contain enough parts."); return; } } while (true) { line = reader.ReadLine(); // Content-Type Assert.IsTrue(line.StartsWith("Content-Type:"), "The batch part doesn't specify its content type."); contentType = line.Substring("Content-Type:".Length).Trim(); if (contentType.StartsWith("multipart/mixed; ")) { string changesetBoundary = "--" + contentType.Substring(contentType.IndexOf("; boundary=") + 11).Trim(); string changesetEndBoundary = changesetBoundary + "--"; int cpartIndex = 0; Changeset changeset; if (matchToExisting) { Assert.IsTrue(this.changesets.Count > changesetIndex, "The batch response contains more changesets than the number of changesets sent."); changeset = this.changesets[changesetIndex]; } else { changeset = new Changeset(); this.changesets.Add(changeset); } changesetIndex++; while ((line = reader.ReadLine()) != null) { if (line.Trim().Length == 0) { break; } } while ((line = reader.ReadLine()) != null) { if (line == changesetBoundary) { break; } if (line == changesetEndBoundary) { goto ChangesetEnd; } } while (true) { reader.ReadLine(); // Content-Type reader.ReadLine(); // Content-Transfer-Encoding InMemoryWebRequest part; if (matchToExisting) { Assert.IsTrue(changeset.Parts.Count > cpartIndex, "The batch response contains more parts than the number of request parts sent."); part = changeset.Parts[cpartIndex]; } else { part = new InMemoryWebRequest(); changeset.Parts.Add(part); } line = reader.ReadLine(); if (!string.IsNullOrEmpty(line)) { part.RequestHeaders["Content-ID"] = line.Split(' ')[1]; reader.ReadLine(); } cpartIndex++; if (ParseBatchPart(isResponse, reader, part, changesetBoundary, changesetEndBoundary)) { break; } } ChangesetEnd: if (cpartIndex < changeset.Parts.Count) { int lastStatusCode = changeset.Parts[cpartIndex - 1].ResponseStatusCode; if (!UnitTestsUtil.IsSuccessStatusCode(lastStatusCode)) { for (; cpartIndex < changeset.Parts.Count; cpartIndex++) { changeset.Parts[cpartIndex].SetResponseStatusCode(lastStatusCode); } } } Assert.IsTrue(cpartIndex == changeset.Parts.Count, "The response didn't contain enough parts."); ; while (true) { line = reader.ReadLine(); if (line == boundary) { break; } // We can hit another exception after the changeset. For example if the changeset fails // and after the server serializes out the error for the changeset, writes the endboundary // for the changeset, and call IUpdatable.ClearChanges(), ClearChanges() can throw. // When that happens we will see the second error but there won't be any boundary after it. // We need to goto End when we either see the end boundary or reached the end of stream. if (line == null || line == endboundary) { goto End; } } } else { Assert.IsTrue(contentType.StartsWith("application/http"), "Batch part is neither changeset nor HTTP."); reader.ReadLine(); // Content-Transfer-Encoding reader.ReadLine(); // InMemoryWebRequest part; if (matchToExisting) { Assert.IsTrue(this.parts.Count > partIndex, "The batch response contains more parts than the number of request parts sent."); part = this.parts[partIndex]; partIndex++; } else { part = new InMemoryWebRequest(); this.parts.Add(part); partIndex++; } if (ParseBatchPart(isResponse, reader, part, boundary, endboundary)) { goto End; } } } End: Assert.IsTrue(partIndex == this.parts.Count, "The response didn't contain enough parts."); Assert.IsTrue(changesetIndex == this.changesets.Count, "The response didn't contain enough parts."); } }
public Stream ProcessRequestForMessage(Stream messageBody) { this.response = new StringBuilder(); WebOperationContext c = WebOperationContext.Current; if (InternalProcessRequestOverride != null) { InMemoryWebRequest request = new InMemoryWebRequest(); InMemoryWebRequest response = null; try { request.HttpMethod = c.IncomingRequest.Method; request.RequestUriString = c.IncomingRequest.UriTemplateMatch.RequestUri.AbsoluteUri.Substring(c.IncomingRequest.UriTemplateMatch.BaseUri.AbsoluteUri.Length); foreach (string headerKey in c.IncomingRequest.Headers.AllKeys) { request.RequestHeaders[headerKey] = c.IncomingRequest.Headers[headerKey]; } InMemoryWebRequest.ApplyHeadersToProperties(request); request.SetRequestStream(messageBody); response = InternalProcessRequestOverride(request); } catch (Exception e) { response = new InMemoryWebRequest(); response.SetResponseStatusCode(500); response.SetResponseStreamAsText(CreateErrorPayload(e)); } c.OutgoingResponse.StatusCode = (HttpStatusCode)response.ResponseStatusCode; foreach (var header in response.ResponseHeaders) { c.OutgoingResponse.Headers[header.Key] = header.Value; } if (response.GetResponseStream() != null) { MemoryStream body = new MemoryStream(); System.Data.Test.Astoria.TestUtil.CopyStream(response.GetResponseStream(), body); body.Position = 0; return body; } else { return null; } } if (InternalInspectRequestPayload != null) { MemoryStream ms = new MemoryStream(); test.TestUtil.CopyStream(messageBody, ms); messageBody = ms; messageBody.Position = 0; InternalInspectRequestPayload(messageBody); messageBody.Position = 0; } Append("<error xmlns='http://docs.oasis-open.org/odata/ns/metadata'>"); Append("<message>"); Append(c.IncomingRequest.Method + " " + c.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString); Append("\r\n"); Append(c.IncomingRequest.Headers.ToString()); Append("\r\n"); AppendXmlStream(messageBody); Append("</message>"); Append("</error>"); lastPlayback = this.response.ToString(); string result = lastPlayback; if (InternalOverridingPlayback != null) { result = ProcessOverridingPlayback(c); // For batch request, i need to send the exact content id otherwise the client chokes int startIndex = lastPlayback.IndexOf("Content-ID: "); if (startIndex != -1) { // advance the index to the start of the content id startIndex += "Content-ID: ".Length; int endIndex = lastPlayback.IndexOf(Environment.NewLine, startIndex); int contentID = Int32.Parse(lastPlayback.Substring(startIndex, endIndex - startIndex)); int resultStartIndex = result.IndexOf("Content-ID: ") + "Content-ID: ".Length; int resultEndIndex = result.IndexOf(Environment.NewLine, resultStartIndex); result = result.Substring(0, resultStartIndex) + contentID + result.Substring(resultEndIndex); } } else { if (c.IncomingRequest.ContentType != null) { c.OutgoingResponse.ContentType = c.IncomingRequest.ContentType; } else { // Took a breaking change, since the content type now must be correct. // In V1/V2, astoria client used to parse error payloads even when response content type value was application/atom+xml // After integration, it must be application/xml. c.OutgoingResponse.ContentType = AstoriaUnitTests.Data.SerializationFormatData.Atom.MimeTypes[2]; } if (c.IncomingRequest.Method == "POST") { c.OutgoingResponse.Location = "http://www.microsoft.com/"; } } return (result == null) ? null : new MemoryStream(Encoding.ASCII.GetBytes(result)); }
public InMemoryWebRequest ProcessRequestOverride(InMemoryWebRequest request) { if (IsTextPayloadType(request.RequestContentType)) { string payload = ReplaceUriOccurences( this.playbackServiceBaseUri, this.underlyingServiceBaseUri, new StreamReader(request.GetRequestStream()).ReadToEnd()); // Remove all Content-Length headers (if it's a batch since we just changed the length of the requests by replacing strings) StringBuilder sb = new StringBuilder(); TextReader reader = new StringReader(payload); string line; while((line = reader.ReadLine()) != null) { if (!line.StartsWith("Content-Length")) sb.AppendLine(line); } request.SetRequestStreamAsText(sb.ToString()); } // Copy the request to the server request request.WriteRequest(this.underlyingService); // Send the request try { this.underlyingService.SendRequest(); // Copy the response to our in-memory representation var response = InMemoryWebRequest.FromResponse(this.underlyingService); if (IsTextPayloadType(response.ResponseContentType)) { response.SetResponseStreamAsText( ReplaceUriOccurences( this.underlyingServiceBaseUri, this.playbackServiceBaseUri, response.GetResponseStreamAsText())); } var headersToReplace = new string[] { "Location", "OData-EntityId" }; foreach (var headerName in headersToReplace) { string value; if (response.ResponseHeaders.TryGetValue(headerName, out value)) { response.ResponseHeaders[headerName] = ReplaceUriOccurences( this.underlyingServiceBaseUri, this.playbackServiceBaseUri, value); } } return response; } catch (Exception exception) { // Translate everything into a 500, it's easier and we don't need correct error reporting on the client anyway (for versioning tests) var response = new InMemoryWebRequest(); response.SetResponseStatusCode(500); response.ResponseHeaders["Content-Type"] = UnitTestsUtil.MimeTextPlain; response.SetResponseStreamAsText(exception.ToString()); return response; } }
public void SendMoreThan100RequestsInBatch() { using (TestWebRequest request = TestWebRequest.CreateForInProcessWcf()) { request.DataServiceType = typeof(CustomDataContext); BatchWebRequest batchRequest = new BatchWebRequest(); for (int i = 0; i < 101; i++) { InMemoryWebRequest getRequest = new InMemoryWebRequest(); getRequest.RequestUriString = "Customers(1)"; batchRequest.Parts.Add(getRequest); } batchRequest.SendRequest(request); Assert.IsFalse(batchRequest.Parts.Any(p => p.ResponseStatusCode != 200), "All the requests should succeed"); } }
public void BatchContentTypeTest() { var testCases = new BatchContentTypeTestCase[] { // Completely wrong content type new BatchContentTypeTestCase { ContentType = "text/plain", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = DataServicesClientResourceUtil.GetString("Batch_ExpectedContentType", "text/plain") }, // Just type is correct, subtype is wrong new BatchContentTypeTestCase { ContentType = "multipart/text", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = DataServicesClientResourceUtil.GetString("Batch_ExpectedContentType", "multipart/text") }, // No boundary - still wrong new BatchContentTypeTestCase { ContentType = "multipart/mixed", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed", "boundary") }, // Some other parameter but no boundary new BatchContentTypeTestCase { ContentType = "multipart/mixed;param=value", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed;param=value", "boundary") }, // Empty boundary - fails new BatchContentTypeTestCase { ContentType = "multipart/mixed;boundary=", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("ValidationUtils_InvalidBatchBoundaryDelimiterLength", string.Empty, "70") }, new BatchContentTypeTestCase { ContentType = "multipart/mixed;boundary=;param=value", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("ValidationUtils_InvalidBatchBoundaryDelimiterLength", string.Empty, "70") }, // Two boundary parameters - wrong new BatchContentTypeTestCase { ContentType = "multipart/mixed;boundary=one;boundary=two", ExpectedErrorStatusCode = 400, ExpectedClientErrorMessage = ODataLibResourceUtil.GetString("MediaTypeUtils_BoundaryMustBeSpecifiedForBatchPayloads", "multipart/mixed;boundary=one;boundary=two", "boundary") }, // Valid simple boundary new BatchContentTypeTestCase { ContentType = "multipart/mixed;boundary=batchboundary", PayloadBatchBoundary = "batchboundary" }, // Valid simple boundary - mimetype using different casing new BatchContentTypeTestCase { ContentType = "MultiPart/mIxed;boundary=batchboundary", PayloadBatchBoundary = "batchboundary" }, // Valid simple boundary - boundary parameter name different casing new BatchContentTypeTestCase { ContentType = "multipart/mixed;BounDary=batchboundary", PayloadBatchBoundary = "batchboundary" }, }; OpenWebDataServiceDefinition serverService = new OpenWebDataServiceDefinition() { DataServiceType = typeof(CustomDataContext) }; PlaybackServiceDefinition clientService = new PlaybackServiceDefinition(); TestUtil.RunCombinations( testCases, (testCase) => { using (TestWebRequest request = serverService.CreateForInProcess()) { request.RequestContentType = testCase.ContentType; request.SetRequestStreamAsText(string.Format( "--{0}\r\n" + "Content-Type: multipart/mixed; boundary=changesetresponse_00000001-0000-0000-0000-000000000000\r\n\r\n" + "--changesetresponse_00000001-0000-0000-0000-000000000000\r\n" + "--changesetresponse_00000001-0000-0000-0000-000000000000--\r\n" + "--{0}--\r\n", testCase.PayloadBatchBoundary)); request.RequestUriString = "/$batch"; request.HttpMethod = "POST"; Exception exception = TestUtil.RunCatching(request.SendRequest); int actualStatusCode = 0; if (exception != null) { actualStatusCode = request.ResponseStatusCode; } else { Assert.AreEqual(202, request.ResponseStatusCode, "Wrong response code for no-exception request."); BatchWebRequest batchResponse = BatchWebRequest.FromResponse(InMemoryWebRequest.FromResponse(request)); if (batchResponse.Parts.Count > 0) { actualStatusCode = batchResponse.Parts[0].ResponseStatusCode; if (actualStatusCode == 200) actualStatusCode = 0; } } Assert.AreEqual(testCase.ExpectedErrorStatusCode, actualStatusCode, "Wrong status code."); } using (TestWebRequest request = clientService.CreateForInProcessWcf()) { request.StartService(); clientService.ProcessRequestOverride = clientRequest => { var clientResponse = new InMemoryWebRequest(); clientResponse.SetResponseStatusCode(202); clientResponse.ResponseHeaders["Content-Type"] = testCase.ContentType; clientResponse.SetResponseStreamAsText(string.Format( "--{0}\r\n" + "Content-Type: application/http\r\n" + "Content-Transfer-Encoding: binary\r\n" + "\r\n" + "200 OK\r\n" + "<feed xmlns='http://www.w3.org/2005/Atom'/>\r\n" + "--{0}--\r\n", testCase.PayloadBatchBoundary)); return clientResponse; }; DataServiceContext ctx = new DataServiceContext(request.ServiceRoot); Exception exception = TestUtil.RunCatching(() => ctx.ExecuteBatch(ctx.CreateQuery<Customer>("/Customers"))); if (exception != null) { exception = ((DataServiceRequestException)exception).InnerException; Assert.AreEqual(testCase.ExpectedClientErrorMessage, exception.Message, "Unexpected error message."); } else { Assert.IsNull(testCase.ExpectedClientErrorMessage, "Expected exception, but none was thrown."); } } }); }
public void CreateODataWriterDelegateTestForOpenProvider() { var testInfo = new[] { new { Query = "/Customers?$format=atom", CustomerCount = 3, NavLinkCount = 3, OrderCount = 0 }, new { Query = "/Customers?$format=atom&$expand=Orders", CustomerCount = 3, NavLinkCount = 3, OrderCount = 6 }, new { Query = "/Customers(1)?$format=atom", CustomerCount = 1, NavLinkCount = 1, OrderCount = 0 }, new { Query = "/Customers(1)?$format=atom&$expand=Orders", CustomerCount = 1, NavLinkCount = 1, OrderCount = 2 }, new { Query = "/Customers(1)/Orders?$format=atom", CustomerCount = 0, NavLinkCount = 0, OrderCount = 2 }, }; using (OpenWebDataServiceHelper.CreateODataWriterDelegate.Restore()) using (MyODataWriter.WriteEntryStart.Restore()) using (MyODataWriter.WriteLinkStart.Restore()) using (var request = TestWebRequest.CreateForInProcess()) { request.HttpMethod = "GET"; request.DataServiceType = typeof(CustomRowBasedOpenTypesContext); OpenWebDataServiceHelper.CreateODataWriterDelegate.Value = (odataWriter) => { return new MyODataWriter(odataWriter); }; test.TestUtil.RunCombinations(testInfo, UnitTestsUtil.BooleanValues, (ti, batchMode) => { int CustomerCount = 0; int NavigationLink = 0; int OrderCount = 0; MyODataWriter.WriteEntryStart.Value = (args) => { Assert.IsTrue(args.Instance.GetType() == typeof(RowComplexType), "Making sure the right provider type is exposed"); var instance = (RowComplexType)args.Instance; if (args.Entry.TypeName.Contains("Customer")) { Assert.IsTrue(instance.TypeName.Contains("Customer"), "Make sure the instance is customer or customerwithbirthday"); CustomerCount++; } else if (args.Entry.TypeName.Contains("Order")) { Assert.IsTrue(instance.TypeName.Contains("Order"), "Make sure the instance is order"); OrderCount++; } return false; }; MyODataWriter.WriteLinkStart.Value = (args) => { if (args.NavigationLink.Name == "Orders") { Assert.IsTrue(args.NavigationLink.IsCollection.Value, "orders must be collection"); NavigationLink++; } return false; }; if (!batchMode) { request.RequestUriString = ti.Query; request.SendRequest(); } else { BatchWebRequest batchRequest = new BatchWebRequest(); InMemoryWebRequest getRequest = new InMemoryWebRequest(); getRequest.RequestUriString = ti.Query; batchRequest.Parts.Add(getRequest); batchRequest.SendRequest(request); } Assert.AreEqual(CustomerCount, ti.CustomerCount, "CustomerCount should match"); Assert.AreEqual(OrderCount, ti.OrderCount, "OrderCount should match"); Assert.AreEqual(NavigationLink, ti.NavLinkCount, "NavigationCount should match"); }); } }
public void CreateODataWriterDelegateTest() { int createODataWriterDelegateCount = 0; var testInfo = new[] { new { Query = "/Customers?$format=atom", CustomerCount = 3, NavLinkCount = 6, OrderCount = 0 }, new { Query = "/Customers?$format=atom&$expand=BestFriend", CustomerCount = 5, NavLinkCount = 10, OrderCount = 0 }, new { Query = "/Customers?$format=atom&$expand=Orders", CustomerCount = 3, NavLinkCount = 18, OrderCount = 6 }, new { Query = "/Customers(1)?$format=atom", CustomerCount = 1, NavLinkCount = 2, OrderCount = 0 }, new { Query = "/Customers(1)?$format=atom&$expand=Orders", CustomerCount = 1, NavLinkCount = 6, OrderCount = 2 }, new { Query = "/Customers(1)/Orders?$format=atom", CustomerCount = 0, NavLinkCount = 4, OrderCount = 2 }, }; using (OpenWebDataServiceHelper.CreateODataWriterDelegate.Restore()) using (MyODataWriter.WriteEntryStart.Restore()) using (MyODataWriter.WriteLinkStart.Restore()) using (var request = TestWebRequest.CreateForInProcess()) { createODataWriterDelegateCount = 0; request.HttpMethod = "GET"; request.DataServiceType = typeof(CustomDataContext); OpenWebDataServiceHelper.CreateODataWriterDelegate.Value = (odataWriter) => { createODataWriterDelegateCount++; return new MyODataWriter(odataWriter); }; test.TestUtil.RunCombinations(testInfo, UnitTestsUtil.BooleanValues, (ti, batchMode) => { createODataWriterDelegateCount = 0; int CustomerCount = 0; int NavigationLink = 0; int OrderCount = 0; MyODataWriter.WriteEntryStart.Value = (args) => { if (args.Entry != null) { if (args.Entry.TypeName.Contains("Customer")) { Assert.IsTrue(typeof(Customer).IsAssignableFrom(args.Instance.GetType()), "Make sure the instance is customer or customerwithbirthday"); CustomerCount++; } else if (args.Entry.TypeName.Contains("Order")) { Assert.IsTrue(typeof(Order).IsAssignableFrom(args.Instance.GetType()), "Make sure the instance is order"); OrderCount++; } } return false; }; MyODataWriter.WriteLinkStart.Value = (args) => { NavigationLink++; return false; }; if (!batchMode) { request.RequestUriString = ti.Query; request.SendRequest(); Assert.AreEqual(1, createODataWriterDelegateCount, "NavigationCount should match"); } else { BatchWebRequest batchRequest = new BatchWebRequest(); InMemoryWebRequest getRequest = new InMemoryWebRequest(); getRequest.RequestUriString = ti.Query; batchRequest.Parts.Add(getRequest); batchRequest.SendRequest(request); Assert.AreEqual(batchRequest.Parts.Count, createODataWriterDelegateCount, "NavigationCount should match"); } Assert.AreEqual(ti.CustomerCount, CustomerCount, "CustomerCount should match"); Assert.AreEqual(ti.OrderCount, OrderCount, "OrderCount should match"); Assert.AreEqual(ti.NavLinkCount, NavigationLink, "NavigationCount should match"); }); } }
private void ParseBatchContent(Stream contentStream, string contentType, bool isResponse, bool matchToExisting) { int partIndex = 0; int changesetIndex = 0; if (!matchToExisting) { this.changesets = new List<Changeset>(); this.parts = new List<InMemoryWebRequest>(); } MemoryStream memoryStream = new MemoryStream(); // Create a copy since StreamReader will Close the stream, which might not be what we want TestUtil.CopyStream(contentStream, memoryStream); memoryStream.Position = 0; using (TextReader reader = new StreamReader(memoryStream)) { Assert.IsTrue(contentType.StartsWith("multipart/mixed; "), "Response is not a batch response. Expecting 'multipart.mixed', got '" + contentType + "'."); string boundary = "--" + contentType.Substring(contentType.IndexOf("; boundary=") + 11).Trim(); string endboundary = boundary + "--"; string line; while ((line = reader.ReadLine()) != null) { if (line == boundary) break; if (line == endboundary) { Assert.IsTrue(partIndex == this.parts.Count, "The response didn't contain enough parts."); return; } } while (true) { line = reader.ReadLine(); // Content-Type Assert.IsTrue(line.StartsWith("Content-Type:"), "The batch part doesn't specify its content type."); contentType = line.Substring("Content-Type:".Length).Trim(); if (contentType.StartsWith("multipart/mixed; ")) { string changesetBoundary = "--" + contentType.Substring(contentType.IndexOf("; boundary=") + 11).Trim(); string changesetEndBoundary = changesetBoundary + "--"; int cpartIndex = 0; Changeset changeset; if (matchToExisting) { Assert.IsTrue(this.changesets.Count > changesetIndex, "The batch response contains more changesets than the number of changesets sent."); changeset = this.changesets[changesetIndex]; } else { changeset = new Changeset(); this.changesets.Add(changeset); } changesetIndex++; while ((line = reader.ReadLine()) != null) { if (line.Trim().Length == 0) break; } while ((line = reader.ReadLine()) != null) { if (line == changesetBoundary) break; if (line == changesetEndBoundary) { goto ChangesetEnd; } } while (true) { reader.ReadLine(); // Content-Type reader.ReadLine(); // Content-Transfer-Encoding InMemoryWebRequest part; if (matchToExisting) { Assert.IsTrue(changeset.Parts.Count > cpartIndex, "The batch response contains more parts than the number of request parts sent."); part = changeset.Parts[cpartIndex]; } else { part = new InMemoryWebRequest(); changeset.Parts.Add(part); } line = reader.ReadLine(); if (!string.IsNullOrEmpty(line)) { part.RequestHeaders["Content-ID"] = line.Split(' ')[1]; reader.ReadLine(); } cpartIndex++; if (ParseBatchPart(isResponse, reader, part, changesetBoundary, changesetEndBoundary)) { break; } } ChangesetEnd: if (cpartIndex < changeset.Parts.Count) { int lastStatusCode = changeset.Parts[cpartIndex - 1].ResponseStatusCode; if (!UnitTestsUtil.IsSuccessStatusCode(lastStatusCode)) { for (; cpartIndex < changeset.Parts.Count; cpartIndex++) { changeset.Parts[cpartIndex].SetResponseStatusCode(lastStatusCode); } } } Assert.IsTrue(cpartIndex == changeset.Parts.Count, "The response didn't contain enough parts."); ; while (true) { line = reader.ReadLine(); if (line == boundary) break; // We can hit another exception after the changeset. For example if the changeset fails // and after the server serializes out the error for the changeset, writes the endboundary // for the changeset, and call IUpdatable.ClearChanges(), ClearChanges() can throw. // When that happens we will see the second error but there won't be any boundary after it. // We need to goto End when we either see the end boundary or reached the end of stream. if (line == null || line == endboundary) { goto End; } } } else { Assert.IsTrue(contentType.StartsWith("application/http"), "Batch part is neither changeset nor HTTP."); reader.ReadLine(); // Content-Transfer-Encoding reader.ReadLine(); // InMemoryWebRequest part; if (matchToExisting) { Assert.IsTrue(this.parts.Count > partIndex, "The batch response contains more parts than the number of request parts sent."); part = this.parts[partIndex]; partIndex++; } else { part = new InMemoryWebRequest(); this.parts.Add(part); partIndex++; } if (ParseBatchPart(isResponse, reader, part, boundary, endboundary)) { goto End; } } } End: Assert.IsTrue(partIndex == this.parts.Count, "The response didn't contain enough parts."); Assert.IsTrue(changesetIndex == this.changesets.Count, "The response didn't contain enough parts."); } }
/// <summary>Writes the batch as a response into a stream (including the status line, headers and everything)</summary> /// <param name="stream">The stream to write the response to.</param> public void WriteResponse(Stream stream) { InMemoryWebRequest response = new InMemoryWebRequest(); this.WriteResponse(response); response.WriteResponse(stream); }
private bool ParseBatchPart(bool isResponse, TextReader reader, InMemoryWebRequest part, string boundary, string endboundary) { bool result = false; string line; if (isResponse) { part.ParseResponseStatus(reader); } else { part.ParseRequestVerb(reader); } if (isResponse) { part.ResponseHeaders.Clear(); InMemoryWebRequest.ParseHeaders(reader, part.ResponseHeaders); } else { InMemoryWebRequest.ParseHeaders(reader, part.RequestHeaders); InMemoryWebRequest.ApplyHeadersToProperties(part); } StringBuilder sb = new StringBuilder(); string lastLine = null; while ((line = reader.ReadLine()) != null) { if (line == boundary) { break; } if (line == endboundary) { result = true; break; } if (lastLine != null) { sb.AppendLine(lastLine); } lastLine = line; } // The last line must not end with a newline - the batch adds it there, but it's not actually part of the content sb.Append(lastLine); if (isResponse) { part.SetResponseStream(new MemoryStream(Encoding.UTF8.GetBytes(sb.ToString()))); } else { part.SetRequestStreamAsText(sb.ToString()); } return result; }
/// <summary>Creates the in-memory request from a stream.</summary> /// <param name="stream">The stream to read.</param> /// <param name="serviceRoot">The service root uri the request was targeted at (if null the request URI will be left as is).</param> /// <returns>The newly created request representation.</returns> public static InMemoryWebRequest FromRequest(Stream stream, Uri serviceRoot) { InMemoryWebRequest request = new InMemoryWebRequest(); request.ParseRequest(stream, serviceRoot); return request; }
/// <summary>Writes the batch as an HTTP request into a stream (including the verb, headers and everything).</summary> /// <param name="stream">The stream to write the request into.</param> /// <param name="serviceRoot">The uri of the service root to send the request to.</param> public void WriteRequest(Stream stream, Uri serviceRoot) { InMemoryWebRequest request = new InMemoryWebRequest(); this.WriteRequest(request); request.WriteRequest(stream, serviceRoot); }
public Stream ProcessRequestForMessage(Stream messageBody) { this.response = new StringBuilder(); WebOperationContext c = WebOperationContext.Current; if (InternalProcessRequestOverride != null) { InMemoryWebRequest request = new InMemoryWebRequest(); InMemoryWebRequest response = null; try { request.HttpMethod = c.IncomingRequest.Method; request.RequestUriString = c.IncomingRequest.UriTemplateMatch.RequestUri.AbsoluteUri.Substring(c.IncomingRequest.UriTemplateMatch.BaseUri.AbsoluteUri.Length); foreach (string headerKey in c.IncomingRequest.Headers.AllKeys) { request.RequestHeaders[headerKey] = c.IncomingRequest.Headers[headerKey]; } InMemoryWebRequest.ApplyHeadersToProperties(request); request.SetRequestStream(messageBody); response = InternalProcessRequestOverride(request); } catch (Exception e) { response = new InMemoryWebRequest(); response.SetResponseStatusCode(500); response.SetResponseStreamAsText(CreateErrorPayload(e)); } c.OutgoingResponse.StatusCode = (HttpStatusCode)response.ResponseStatusCode; foreach (var header in response.ResponseHeaders) { c.OutgoingResponse.Headers[header.Key] = header.Value; } if (response.GetResponseStream() != null) { MemoryStream body = new MemoryStream(); System.Data.Test.Astoria.TestUtil.CopyStream(response.GetResponseStream(), body); body.Position = 0; return(body); } else { return(null); } } if (InternalInspectRequestPayload != null) { MemoryStream ms = new MemoryStream(); test.TestUtil.CopyStream(messageBody, ms); messageBody = ms; messageBody.Position = 0; InternalInspectRequestPayload(messageBody); messageBody.Position = 0; } Append("<error xmlns='http://docs.oasis-open.org/odata/ns/metadata'>"); Append("<message>"); Append(c.IncomingRequest.Method + " " + c.IncomingRequest.UriTemplateMatch.RequestUri.OriginalString); Append("\r\n"); Append(c.IncomingRequest.Headers.ToString()); Append("\r\n"); AppendXmlStream(messageBody); Append("</message>"); Append("</error>"); lastPlayback = this.response.ToString(); string result = lastPlayback; if (InternalOverridingPlayback != null) { result = ProcessOverridingPlayback(c); // For batch request, i need to send the exact content id otherwise the client chokes int startIndex = lastPlayback.IndexOf("Content-ID: "); if (startIndex != -1) { // advance the index to the start of the content id startIndex += "Content-ID: ".Length; int endIndex = lastPlayback.IndexOf(Environment.NewLine, startIndex); int contentID = Int32.Parse(lastPlayback.Substring(startIndex, endIndex - startIndex)); int resultStartIndex = result.IndexOf("Content-ID: ") + "Content-ID: ".Length; int resultEndIndex = result.IndexOf(Environment.NewLine, resultStartIndex); result = result.Substring(0, resultStartIndex) + contentID + result.Substring(resultEndIndex); } } else { if (c.IncomingRequest.ContentType != null) { c.OutgoingResponse.ContentType = c.IncomingRequest.ContentType; } else { // Took a breaking change, since the content type now must be correct. // In V1/V2, astoria client used to parse error payloads even when response content type value was application/atom+xml // After integration, it must be application/xml. c.OutgoingResponse.ContentType = AstoriaUnitTests.Data.SerializationFormatData.Atom.MimeTypes[2]; } if (c.IncomingRequest.Method == "POST") { c.OutgoingResponse.Location = "http://www.microsoft.com/"; } } return((result == null) ? null : new MemoryStream(Encoding.ASCII.GetBytes(result))); }
public void Collection_ProcessingPipeline() { var metadata = CreateMetadataForXFeatureEntity(); Func<int, DSPResource> CreateNewXFeatureEntityResource = (id) => { DSPResourceWithCollectionProperty newResource = new DSPResourceWithCollectionProperty(metadata.GetResourceType("XFeatureTestsEntity")); newResource.SetRawValue("ID", id); newResource.SetRawValue("Description", "Second"); newResource.SetRawValue("Strings", new List<string>() { "One", "Two" }); newResource.SetRawValue("Structs", new List<DSPResource>()); return newResource; }; var actualCallCount = new ProcessingPipelineCallCount(); DSPServiceDefinition service = new DSPServiceDefinition() { Metadata = metadata, CreateDataSource = (m) => { DSPContext context = new DSPContext(); context.GetResourceSetEntities("Entities").Add(CreateNewXFeatureEntityResource(0)); return context; }, Writable = true, }; service.ProcessingPipeline.ProcessingRequest = (sender, args) => { actualCallCount.ProcessingRequestCallCount++; }; service.ProcessingPipeline.ProcessedRequest = (sender, args) => { actualCallCount.ProcessedRequestCallCount++; }; service.ProcessingPipeline.ProcessingChangeset = (sender, args) => { actualCallCount.ProcessingChangesetCallCount++; }; service.ProcessingPipeline.ProcessedChangeset = (sender, args) => { actualCallCount.ProcessedChangesetCallCount++; }; var testCases = new ProcessingPipelineTestCase[] { new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "GET"; r.RequestUriString = "/Entities"; r.Accept = format; }, }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "GET"; r.RequestUriString = "/Entities(0)/Strings"; r.Accept = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; }, }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "GET"; r.RequestUriString = "/Entities(0)/Structs"; r.Accept = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; }, }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "POST"; r.RequestUriString = "/Entities"; r.Accept = format; r.RequestContentType = format; r.SetRequestStreamAsText(DSPResourceSerializer.WriteEntity( CreateNewXFeatureEntityResource(1), DSPResourceSerializer.SerializerFormatFromMimeType(format))); }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "PUT"; r.RequestUriString = "/Entities(0)"; r.Accept = format; r.RequestContentType = format; r.SetRequestStreamAsText(DSPResourceSerializer.WriteEntity( CreateNewXFeatureEntityResource(0), DSPResourceSerializer.SerializerFormatFromMimeType(format))); }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "PATCH"; r.RequestUriString = "/Entities(0)"; r.Accept = format; r.RequestContentType = format; r.SetRequestStreamAsText(DSPResourceSerializer.WriteEntity( CreateNewXFeatureEntityResource(0), DSPResourceSerializer.SerializerFormatFromMimeType(format))); }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "DELETE"; r.RequestUriString = "/Entities(0)"; r.Accept = format; }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "PUT"; r.RequestUriString = "/Entities(0)/Strings"; r.Accept = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; r.RequestContentType = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; r.SetRequestStreamAsText(DSPResourceSerializer.WriteProperty( metadata.GetResourceType("XFeatureTestsEntity").Properties.First(p => p.Name == "Strings"), new List<string> { "Foo", "Bar" }, DSPResourceSerializer.SerializerFormatFromMimeType(format))); }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, new ProcessingPipelineTestCase() { SetupRequest = (r, format) => { r.HttpMethod = "PUT"; r.RequestUriString = "/Entities(0)/Structs"; r.Accept = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; r.RequestContentType = format == UnitTestsUtil.AtomFormat ? UnitTestsUtil.MimeApplicationXml : format; r.SetRequestStreamAsText(DSPResourceSerializer.WriteProperty( metadata.GetResourceType("XFeatureTestsEntity").Properties.First(p => p.Name == "Structs"), new List<DSPResource>(), DSPResourceSerializer.SerializerFormatFromMimeType(format))); }, ExpectedCallCount = new ProcessingPipelineCallCount() { ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 } }, }; using (DSPResourceWithCollectionProperty.CollectionPropertiesResettable.Restore()) using (TestWebRequest request = service.CreateForInProcess()) { DSPResourceWithCollectionProperty.CollectionPropertiesResettable.Value = true; TestUtil.RunCombinations(testCases, UnitTestsUtil.BooleanValues, UnitTestsUtil.ResponseFormats, (testCase, batch, format) => { service.ClearChanges(); actualCallCount.Clear(); var expectedCallCount = new ProcessingPipelineCallCount(testCase.ExpectedCallCount); // Each request must fire at least one ProcessingRequest and ProcessedRequest expectedCallCount.ProcessingRequestCallCount++; expectedCallCount.ProcessedRequestCallCount++; if (batch) { InMemoryWebRequest batchPart = new InMemoryWebRequest(); testCase.SetupRequest(batchPart, format); BatchWebRequest batchRequest = new BatchWebRequest(); if (batchPart.HttpMethod == "GET") { batchRequest.Parts.Add(batchPart); } else { var changeset = new BatchWebRequest.Changeset(); changeset.Parts.Add(batchPart); batchRequest.Changesets.Add(changeset); } batchRequest.SendRequest(request); } else { request.RequestContentType = null; request.RequestStream = null; testCase.SetupRequest(request, format); request.SendRequest(); } actualCallCount.AssertEquals(expectedCallCount); }); } }
/// <summary>Creates the in-memory response from a stream.</summary> /// <param name="stream">The stream to read.</param> /// <returns>The newly created request object with the response properties filled with the data from the stream.</returns> public static InMemoryWebRequest FromResponse(Stream stream) { InMemoryWebRequest response = new InMemoryWebRequest(); response.ParseResponse(stream); return response; }
/// <summary>Writes the batch as a response into an in-memory request (sets up properties and such).</summary> /// <param name="request">The request to apply the batch as a response to.</param> public void WriteResponse(InMemoryWebRequest request) { request.SetResponseStatusCode(200); string boundary = "boundary_" + Guid.NewGuid().ToString(); request.ResponseHeaders["Content-Type"] = String.Format("{0}; boundary={1}", UnitTestsUtil.MimeMultipartMixed, boundary); request.SetResponseStream(this.CreateBatchContent(true, boundary)); }
public InMemoryWebRequest ProcessRequestOverride(InMemoryWebRequest request) { if (IsTextPayloadType(request.RequestContentType)) { string payload = ReplaceUriOccurences( this.playbackServiceBaseUri, this.underlyingServiceBaseUri, new StreamReader(request.GetRequestStream()).ReadToEnd()); // Remove all Content-Length headers (if it's a batch since we just changed the length of the requests by replacing strings) StringBuilder sb = new StringBuilder(); TextReader reader = new StringReader(payload); string line; while ((line = reader.ReadLine()) != null) { if (!line.StartsWith("Content-Length")) { sb.AppendLine(line); } } request.SetRequestStreamAsText(sb.ToString()); } // Copy the request to the server request request.WriteRequest(this.underlyingService); // Send the request try { this.underlyingService.SendRequest(); // Copy the response to our in-memory representation var response = InMemoryWebRequest.FromResponse(this.underlyingService); if (IsTextPayloadType(response.ResponseContentType)) { response.SetResponseStreamAsText( ReplaceUriOccurences( this.underlyingServiceBaseUri, this.playbackServiceBaseUri, response.GetResponseStreamAsText())); } var headersToReplace = new string[] { "Location", "OData-EntityId" }; foreach (var headerName in headersToReplace) { string value; if (response.ResponseHeaders.TryGetValue(headerName, out value)) { response.ResponseHeaders[headerName] = ReplaceUriOccurences( this.underlyingServiceBaseUri, this.playbackServiceBaseUri, value); } } return(response); } catch (Exception exception) { // Translate everything into a 500, it's easier and we don't need correct error reporting on the client anyway (for versioning tests) var response = new InMemoryWebRequest(); response.SetResponseStatusCode(500); response.ResponseHeaders["Content-Type"] = UnitTestsUtil.MimeTextPlain; response.SetResponseStreamAsText(exception.ToString()); return(response); } }
private MemoryStream CreateBatchContent(bool isResponse, string boundary) { MemoryStream payload = new MemoryStream(); foreach (var r in this.parts) { StringBuilder header = new StringBuilder(); header.AppendLine("--" + boundary); header.AppendLine("Content-Type: application/http"); header.AppendLine("Content-Transfer-Encoding: binary"); header.AppendLine(); InMemoryWebRequest.WriteStringToStream(payload, header.ToString()); if (isResponse) { r.WriteResponse(payload); } else { r.WriteRequest(payload, null); } } int contentId = 0; foreach (var c in this.changesets) { string changesetBoundary = "changeset_" + Guid.NewGuid().ToString(); StringBuilder header = new StringBuilder(); header.AppendLine("--" + boundary); header.AppendLine("Content-Type: multipart/mixed; boundary=" + changesetBoundary); MemoryStream changesetPayload = new MemoryStream(); foreach (var r in c.Parts) { StringBuilder cheader = new StringBuilder(); cheader.AppendLine("--" + changesetBoundary); cheader.AppendLine("Content-Type: application/http"); cheader.AppendLine("Content-Transfer-Encoding: binary"); string contentIdStr; r.ResponseHeaders.TryGetValue("Content-ID", out contentIdStr); cheader.AppendLine("Content-Id: " + contentIdStr ?? (++contentId).ToString()); cheader.AppendLine(); InMemoryWebRequest.WriteStringToStream(changesetPayload, cheader.ToString()); if (isResponse) { r.WriteResponse(changesetPayload); } else { r.WriteRequest(changesetPayload, null); } } InMemoryWebRequest.WriteStringToStream(changesetPayload, Environment.NewLine + "--" + changesetBoundary + "--" + Environment.NewLine); changesetPayload.Position = 0; header.AppendLine("Content-Length: " + changesetPayload.Length.ToString()); header.AppendLine(); InMemoryWebRequest.WriteStringToStream(payload, header.ToString()); TestUtil.CopyStream(changesetPayload, payload); } InMemoryWebRequest.WriteStringToStream(payload, Environment.NewLine + "--" + boundary + "--" + Environment.NewLine); payload.Position = 0; return(payload); }
public void PreferHeader_CrossFeature() { DSPMetadata metadata = PreferHeader_CreateMetadata(); DSPSelfmodifyingResource existingItem = new DSPSelfmodifyingResource(metadata.GetResourceType("Item")); existingItem.SetRawValue("ID", 0); existingItem.SetRawValue("ETagProperty", 1); DSPServiceDefinition service = PreferHeader_CreateService(metadata, existingItem, null, null, null); ProcessingPipelineCallCount callCount = new ProcessingPipelineCallCount(); service.ProcessingPipeline.ProcessingRequest += (sender, args) => { callCount.ProcessingRequestCallCount++; }; service.ProcessingPipeline.ProcessedRequest += (sender, args) => { callCount.ProcessedRequestCallCount++; }; service.ProcessingPipeline.ProcessingChangeset += (sender, args) => { callCount.ProcessingChangesetCallCount++; }; service.ProcessingPipeline.ProcessedChangeset += (sender, args) => { callCount.ProcessedChangesetCallCount++; }; service.DataServiceType = typeof(PreferHeader_InterceptorsService); ProcessingPipelineCallCount expectedCallCount = new ProcessingPipelineCallCount() { ProcessingRequestCallCount = 1, ProcessedRequestCallCount = 1, ProcessingChangesetCallCount = 1, ProcessedChangesetCallCount = 1 }; // Can't get the payload from the service since we sometimes don't allow read rights XDocument existingItemAtom = XDocument.Parse("<entry xmlns:d='http://docs.oasis-open.org/odata/ns/data' xmlns:m='http://docs.oasis-open.org/odata/ns/metadata' xmlns='http://www.w3.org/2005/Atom'>" + "<id>http://host/Items(0)</id>" + "<title type='text'></title>" + "<updated>2010-07-19T14:20:32Z</updated>" + "<author><name /></author>" + "<category term='TestNS.Item' scheme='http://docs.oasis-open.org/odata/ns/scheme' />" + "<content type='application/xml'><m:properties>" + "<d:ID m:type='Edm.Int32'>0</d:ID>" + "<d:ETagProperty m:type='Edm.Int32'>1</d:ETagProperty>" + "</m:properties></content></entry>"); TestUtil.RunCombinations( UnitTestsUtil.BooleanValues, UnitTestsUtil.BooleanValues, (includeRelationshipLinks, allowReadAccess) => { service.DataServiceBehavior.IncludeRelationshipLinksInResponse = includeRelationshipLinks; service.EntitySetAccessRule = new Dictionary<string,EntitySetRights>() { {"Items", EntitySetRights.AllWrite | (allowReadAccess ? EntitySetRights.AllRead : EntitySetRights.None)} }; using (TestWebRequest request = service.CreateForInProcess()) { request.RegisterForDispose(PreferHeader_InterceptorsService.InterceptItems.Restore()); TestUtil.RunCombinations( new string[] { "POST", "PUT", "PATCH" }, new string[] { null, "return=representation", "return=minimal" }, new string[] { UnitTestsUtil.AtomFormat }, UnitTestsUtil.BooleanValues, UnitTestsUtil.BooleanValues, (httpMethod, preferHeader, format, batch, interceptItems) => { PreferHeader_InterceptorsService.InterceptItems.Value = interceptItems; PreferHeader_InterceptorsService.LastItemsUpdateOperation = UpdateOperations.None; PreferHeader_InterceptorsService.ItemsQueryInterceptorInvoked = false; service.ClearChanges(); callCount.Clear(); TestWebRequest r = request; if (batch) { r = new InMemoryWebRequest(); } r.RequestVersion = "4.0;"; r.RequestMaxVersion = "4.0;"; r.RequestHeaders["Prefer"] = preferHeader; PreferHeader_SetupRequest(r, httpMethod, format, existingItemAtom, existingItem, null); if (batch) { var batchRequest = new BatchWebRequest(); var changeset = new BatchWebRequest.Changeset(); changeset.Parts.Add((InMemoryWebRequest)r); batchRequest.Changesets.Add(changeset); batchRequest.SendRequest(request); } else { TestUtil.RunCatching(request.SendRequest); } Assert.AreEqual(httpMethod != "POST", PreferHeader_InterceptorsService.ItemsQueryInterceptorInvoked, "The query interceptor fire or didn't fire when it should have."); if (!allowReadAccess && httpMethod != "POST") { Assert.AreEqual(403, r.ResponseStatusCode, "The request should have failed due to access rights."); } else if (interceptItems && httpMethod != "POST") { Assert.AreEqual(404, r.ResponseStatusCode, "The request should not have found any resource."); } else { PreferHeader_VerifyResponse(r, request.ServiceRoot.ToString(), true, (response) => { var relationshipLinks = response.Root.Elements(UnitTestsUtil.AtomNamespace + "link") .Where(e => e.Attribute("rel").Value.Contains("http://docs.oasis-open.org/odata/ns/relatedlinks/")); if (includeRelationshipLinks) { Assert.AreEqual(1, relationshipLinks.Count(), "The response should contain a relationship link."); } else { Assert.AreEqual(0, relationshipLinks.Count(), "The resounrce should not contain any relationship links."); } }); callCount.AssertEquals(expectedCallCount); Assert.AreEqual(httpMethod == "POST" ? UpdateOperations.Add : UpdateOperations.Change, PreferHeader_InterceptorsService.LastItemsUpdateOperation, "The change interceptor didn't fire correctly."); } }); } }); }
public void Projections_Batch() { using (TestUtil.MetadataCacheCleaner()) using (ocs.PopulateData.CreateTableAndPopulateData()) using (TestWebRequest request = TestWebRequest.CreateForInProcess()) { TestUtil.RunCombinations( new Type[] { typeof(CustomDataContext), typeof(ocs.CustomObjectContext), typeof(CustomRowBasedContext), typeof(CustomRowBasedOpenTypesContext) }, CustomerSelects.Variations(2), (dataServiceType, selects) => { request.DataServiceType = dataServiceType; BatchWebRequest batchRequest = new BatchWebRequest(); foreach (var select in selects) { InMemoryWebRequest part = new InMemoryWebRequest(); part.Accept = "application/atom+xml,application/xml"; part.RequestUriString = "/Customers?$select=" + select.Select; batchRequest.Parts.Add(part); } batchRequest.SendRequest(request); for (int i = 0; i < selects.Length; i++) { UnitTestsUtil.VerifyXPathExists(UnitTestsUtil.GetResponseAsAtomXLinq(batchRequest.Parts[i]), selects[i].VerificationXPaths); } }); } }