MemoryStream data corruption issue - c#

I have created a simple REST based WCF service which runs on BasicHttpBinding. In one of my webmethod, I am returning a Stream which points to a JSON response.
The Method looks like :
[OperationContract]
[FaultContract(typeof(ApplicationFault))]
[WebInvoke(Method = "POST", UriTemplate = "GetActiveCalls/{nurseid}")]
Stream GetActiveCalls(string nurseid);
From the body of the GetActiveCalls, I am creating an object of MemoryStream and returning the same as response. The code looks like
// Serialize the results as JSON
string jsonResult = new JavaScriptSerializer().Serialize(baseResponses);
// ContentType json
WebOperationContext.Current.OutgoingResponse.ContentType = "application/json";
WebOperationContext.Current.OutgoingResponse.Headers.Add("Cache-Control", "no-cache");
var bytes = Encoding.UTF8.GetBytes(jsonResult);
//Parse to memorystream
var ms = new MemoryStream(bytes);
ms.Seek(0, SeekOrigin.Begin);
ms.SetLength(bytes.LongLength);
return ms;
When trying this from client, I get result like
{"LastEvents":[{"FormatValues":"Klic 2 3 4","Icon":null,"Color":"Red","Acknowledged":false,"EventID":28566}],"Message":"","Status":true}
But sometimes after invoking the same method for multiple times, I start getting the response as :
{"LastEvents":[{"FormatValues":"Klic 2 3 4","Icon":null,"Color":"Red","Acknowledged":false,"EventID":28566}],"Message":"","Statu{"LastEv
You can see after "Statu on the JSON response, the stream gets reset and starts getting data from the beginning.
It looks strange to me.
*From server side, when I put breakpoint, it seems the MemoryStream has correct response.

Putting aside the question of using a memory stream or not, I encountered a similar issue just recently, where the memory stream response appeared corrupted, seemingly randomly. The solution to this problem was to remove the tracing sections from web.config, which I had turned on in dev mode. This may or may not be your issue, but it might be worth having a look at. Seems as though this problem is still present in .NET 4.5 as well.

Related

Proper way to read and write web api response stream

I'm having some trouble with finding the right incantation that will allow me to write to a response stream and then later read the contents in a test. Currently I have this
var res = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
var ms = new MemoryStream();
res.Content = new StreamContent(ms);
using (var sw = new StreamWriter(ms, System.Text.Encoding.UTF8))
using (var csv = new CsvHelper.CsvWriter(sw))
csv.WriteRecords(allData.ToList());
return res;
In my test I'm trying to read this response
var controller = appContainer().Resolve<MyController>();
var res = (await controller.Get()) as HttpResponseMessage;
res.ShouldNotEqual(null);
var csv = await res.Content.ReadAsStringAsync();
the last line generates an error
Error while copying content to a stream.
----> System.ObjectDisposedException : Cannot access a closed Stream.
So there's a couple things here
Why is this error happening and how can I prevent it properly in the test?
The use of MemoryStream doesn't sit right with me, shouldn't I be able to write directly to the content's stream? Isn't MemoryStream potentially hugely increasing my memory usage?
Just put this out there, though it's not perfect... Using PushStreamContent does a lot of the job but it comes with its own headaches - namely that any exceptions that your anonymous method may produce will get swallowed and be difficult to track down without a full repro of the problem. When the bomb goes off is well passed the point of the pipeline where web api unhandled exception handlers would come into effect, and the xmlhttprequest doesn't seem to recognize the closure.
E.g. something like
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new PushStreamContent((stream, content, context) =>
{
// write your output here
});
return response;
will get you what you want, provided that internal method never slips up or goes wrong in any way.
PushStreamContent flushes your http headers immediately before the anonymous method is called, so you're chunked and no way to reel it back in later.
You can add a try/catch in your anonymous method to leave yourself a note if something goes wrong, but in my experience XmlHttpRequest doesn't recognize when the remote server forcibly closes the request so it keeps on waiting. Only started to figure out what was going on when I put Fiddler in there, and Fiddler squawked.

How to handle URL-encoded POST request with self-hosted WCF?

I have self-hosted WCF service. It works as charm, when the input data format is known, but it's not my case.
My service would be invoked like a HTML form would be posted to it, but I don't know exact list of parameters which would be sent.
External web service would send a HTTP request with content "a=foo&b=bar", yes, URL-encoded. Parameters in content may vary. Their count may vary. Some parameters may or may not be present. So I'm perfectly fine with whole URL-encoded string, I'll decode it myself. I have to reply with ASCII string "OK".
How to access this "a=foo&b=bar" text from the request? Can I create my own encoder? How it's done?
Here's what I started with, the contract:
[OperationContract]
[WebInvoke(Method = "POST", UriTemplate = "test", BodyStyle = WebMessageBodyStyle.Bare)]
Stream TestPostRequest();
And here's the method:
public Stream TestPostRequest() {
var maxByteSize = 16384;
var messageStream = new MemoryStream(maxByteSize);
var messageBuffer = OperationContext.Current.RequestContext.RequestMessage.CreateBufferedCopy(maxByteSize);
messageBuffer.WriteMessage(messageStream);
var rawRequestData = new byte[maxByteSize];
messageStream.Read(rawRequestData, 0, maxByteSize);
var requestDataString = Encoding.UTF8.GetString(rawRequestData);
Console.WriteLine(requestDataString);
var responseData = new byte[2] { 79, 75 }; // OK
var responseStream = new MemoryStream(responseData);
return responseStream;
}
Obviously it doesn't work, because CreateBufferedCopy method expects XML as input data, but gets URL-encoded string. As all the other methods I found.
I'm not afraid of creating new class implementing IEndpointBehavior or IDispatchMessageInspector, but I just don't see where I could implement something allowing me to access the request data raw, as bytes, not XML.
Maybe there is a point where I could convert raw data to XML?
EDIT:
My fault, there was a broken behavior in my operation stack which messed with message content type badly. The moral is - test new features isolated, in a clean test project. The second moral is WCF can everything ;) And the third is: if there is no XML in message internal data - you broke it ;)

android - web api sending/receiving an image

I'm sure it's a common question, and I probably find solutions to this but I didn't understand them. Besides I'm doing this completely blind. Another point is: I don't want to use third party libraries.
I need to send an image from my Android app to my server via c# rest webservice.
I watched this method to convert the bitmap to a byte[].
public static byte[] getBitmapAsByteArray(Bitmap bitmap) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 0, outputStream);
return outputStream.toByteArray();
}
And here I have two (at least) problems:
How to send it as JSON? I tried Base64.encode() with COMMON and URLSAFE (or something like that) flags and I get error at server side: Not a valid Base64.
Then I suppose the client side it's ok, so how do I manage to receive and process the byte[]? Now it seems it try to convert automatically and it fails, maybe because client sends invalid data or well... I don't know.
I can't provide code right now (in fact I think I don't have code to do this at all), but I'll update with what I have if required.
Thanks and sorry for this horrible made question
Android :
byte[] content = getBitmapAsByteArray(bitmap);
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new ByteArrayEntity(content));
HttpResponse response = httpClient.execute(httpPost);
c#.net
byte[] fileData = null;
using (var binaryReader = new BinaryReader(Request.Files[0].InputStream))
{
fileData = binaryReader.ReadBytes(Request.Files[0].ContentLength);
}

Can I change the content of an incoming HTTP request using a HTTP Module?

I'm trying to extend my REST service (built using WCF/webHttpBinding) so that clients can upload gzipped data. I'm not sure what the best way to accomplish this but I thought it would be fairly easy by adding a HTTP module which will decompress the data if Content-Encoding for the incoming request is set to gzip.
So I created an class deriving from IHttpModule with the following implementation:
private void OnBeginRequest(object sender, EventArgs e)
{
var app = (HttpApplication) sender;
var context = app.Context;
var contentEncoding = context.Request.Headers["Content-Encoding"];
if (contentEncoding == "gzip")
{
// some debug code:
var decompressedStream = new GZipStream(context.Request.InputStream, CompressionMode.Decompress);
var memoryStream = new MemoryStream();
decompressedStream.CopyTo(memoryStream);
memoryStream.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(memoryStream);
string msg = streamReader.ReadToEnd();
context.Request.InputStream.Seek(0, SeekOrigin.Begin);
app.Request.Filter = //new TestFilterStream(app.Request.Filter);
new System.IO.Compression.GZipStream(
app.Request.Filter, CompressionMode.Decompress);
}
}
The issue I'm seeing is that the GZipStream decompression is never actually performed. I've confirmed that the incoming data is in fact gzip'd (the msg-variable contains the proper data). I've also tried creating my own stream class (TestFilterStream) above and assign that to app.Request.Filter and I've confirmed that no members on the stream class is actually called by ASP.NET. So it seems like while it's possible to specify a filter, that filter isn't actually used.
Isn't HttpApplication.Request.Filter actually used?
I tried setting the Request Filter in two ways:
Using a HttpModule
Setting it in the start of Application_BeginRequest() (Global.asax)
Both with the same results (VS2012 web project + IISExpress):
If there is no input data (GET request or similar), the Filter.Read is not invoked
In case of a POST with actual data, the filter is executed and the web service gets the filtered data
Even if I read from the Request.InputStream before the Filter is set, I still get the filter triggered from my service code.
I have no easy way of testing with Gzippet input, so I have not tried if the actual filter works. However, I know it is getting triggered, since I get an error from GZipStream while it attempts to look for the input.
Perhaps you are having other HttpModules or Filters that disrupt your input or control flow?
This post proposes a method similar to yours, but also states the following, which may be causing some side effects (my tests were not using WCF):
"It appears that this approach trigger a problem in WCF, as WCF relies on the original Content-Length and not the value obtained after decompressing."
I've just done a couple of tests, and my Request.Filter stream is called into, as long as there is a request body and the request body gets read by the http handler. I'm guessing you use either a PUT or a POST, and certainly read the request body, so that shouldn't be a problem.
I suspect Knaģis' comment is correct. Have you tried without the debug code? If I dig into the HttpRequest source, I see a variable _rawContent being written to exactly once; at that same time the request filters are applied. After that the _rawContent value just gets cached, and is never updated (nor reset when a filter is added).
So by calling Request.InputStream in your debug code you are definitely preventing your filter from being applied later on. Reading the Request.Headers collection is no problem.
Are you sure, that application itself should bother?
Usually that's handled per configuration of host (IIS). So, basically, you only need to implement custom GZip support, when you host the service yourself.
You can take a look here

Reading XML content from Body of WebAPI call is cut off at beginning

I'm creating a sort of proxy service that needs to process calls containing XML from the body of a POST to my WebAPI service and then POST it on to another service.
The odd thing is when I receive the XML message from the POST the 1st part of the XML from the body is cut off. Initially I thought maybe buffer size, or message was too big so I cut out a lot of the XML test message being sent reducing what was being sent. However the XML is still cut off in the same place.
I've tried the following (2) methods to read the XML BODY in the WebAPI service and the result is the same:
var reader = new StreamReader(Request.Content.ReadAsStreamAsync().Result);
string originalMessage = reader.ReadToEnd();
and:
var result = "";
Request.Content.ReadAsStreamAsync().ContinueWith(x =>
{
using (var sr = new StreamReader(x.Result))
{
result = sr.ReadToEnd();
}
});
Here is a snippet the original XML:
<Message version="123" release="001" xmlns="http://www.mysite.com/schema">
<Header>
<To Att1="A">001</To>
<From Att2="B">002</From>
<ID>9876</ID>
Here is the beginning of the content after reading it in the WebAPI controller POST:
</To>
<From Att2="B">002</From>
<ID>9876</ID>
See how it starts at the 'closing' tag of the <To> element? That's obviously not the beginning of the XML that it was sent.
The even more peculiar thing is the 'Content Size' when inspected before it is sent and after is 4188 on both sides. Something else interesting is that I have an old fashion .asmx tester (as opposed to a Web API service) that does the identical thing. When I read the incoming XML message in that app using the following:
// Get raw request body
Stream receiveStream = HttpContext.Current.Request.InputStream;
// Move to beginning of input stream and read
receiveStream.Position = 0;
using (StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8))
{
// Load into XML document
xmlSoapRequest.Load(readStream);
}
...I see the full XML message. So not sure why the .asmx can read it but not the WebAPI service fully.
What am I doing wrong in my WebAPI POST call to where I can't see the full XML message that was sent in the body of the request?
Well I figured out the issue, but not 100% sure on the reason. The parameter I was using for the POST was the one right out of the box:
public HttpResponseMessage Post([FromBody]string value)
I changed it to take the request as a parameter on the POST instead:
public HttpResponseMessage Post(HttpRequestMessage request)
When I did the 2nd option above, I began to get the entire XML body from the request as expected.

Categories

Resources