Example #1
0
        /// <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);
            }
        }
Example #2
0
        /// <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);
        }
Example #3
0
        /// <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);
        }
Example #4
0
        /// <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;
                };
        }
Example #5
0
        /// <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);
        }
Example #6
0
 /// <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;
 }
Example #7
0
        /// <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);
        }
Example #8
0
        /// <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);
        }
Example #9
0
        /// <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));
        }
Example #10
0
        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);
        }
Example #11
0
 /// <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;
 }
Example #12
0
        /// <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);
        }
Example #13
0
 /// <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;
 }
Example #14
0
        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())));
            }
        }
Example #15
0
        /// <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);
        }
Example #16
0
        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());
            }
        }
Example #17
0
        /// <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);
            }
        }
Example #18
0
        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.");
            }
        }
Example #19
0
        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));
        }
Example #20
0
            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;
                }
            }
Example #21
0
            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");
                }
            }
Example #22
0
            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");
                });
            }
        }
Example #25
0
        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.");
            }
        }
Example #26
0
 /// <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);
 }
Example #27
0
        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;
        }
Example #28
0
 /// <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;
 }
Example #29
0
 /// <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);
 }
Example #30
0
        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);
                    });
                }
            }
Example #32
0
 /// <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;
 }
Example #33
0
        /// <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));
        }
Example #34
0
            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);
                }
            }
Example #35
0
        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);
        }
Example #36
0
            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);
                        }
                    });
                }
            }