I'm running into a wall when trying to read underlying streams of either HttpWebRequest and HttpWebResponse classes. As it turns out, these are not memory streams; they are of type ConnectStream. The problem with this type of stream is that it doesn't support reading, writing, seeking, nothing. Every time I try to do something with this type of stream I get not supported exceptions.
Is there a way to use some other type of stream in place of ConnectStream that would actually be readable?
Code:
public class BaseAsmxProxy : SoapHttpClientProtocol
{
protected override System.Xml.XmlReader GetReaderForMessage(SoapClientMessage message, int bufferSize)
{
string responseXml = GetResponseDataFromStream(message.Stream);
return base.GetReaderForMessage(message, bufferSize);
}
private string GetResponseDataFromStream(System.IO.Stream stream)
{
string returnValue = null;
long initialPosition = stream.Position;
stream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(stream);
returnValue = reader.ReadToEnd();
stream.Seek(initialPosition, SeekOrigin.Begin);
return returnValue;
}
}
Note that when I use a SoapExtension (for some other functionality) it switches stream types and I inadvertently get MemoryStream here that is actually readable - which is exactly what I need. However, I'll have to turn off SoapExtentensions at some point and that's where the problems start to occur: ConnectStream is simply not readable.
The response stream can only be read once. You need to use a MemoryStream for all processing.
Related
I'm trying to intercept .NET Remoting Request/Responses by implementing a ServerChannelSink.
All is well, apart from the fact that I can't seem to decode the stream into a string. How do I do this?
Basically, in the watch window I can see that a value has been assigned to my variable after running the code: -
But if I open the Text Visualizer it is empty.
Similarly if I try to write the string to the Output window I don't get any lines written.
Here is the code that I'm using:
private static void PrintStream(TextWriter output, ref Stream stream)
{
// If we can't reset the stream's position after printing its content,
// we must make a copy.
if (!stream.CanSeek)
stream = CopyStream(stream);
long startPosition = stream.Position;
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, (int)stream.Length);
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
string request = enc.GetString(buffer, 0, buffer.Length);
output.WriteLine(request);
output.WriteLine();
// set stream to previous position for message processing
stream.Seek(startPosition, SeekOrigin.Begin);
}
I've also tried using a StreamReader with the same result:
private static void PrintStream(TextWriter output, ref Stream stream)
{
// If we can't reset the stream's position after printing its content,
// we must make a copy.
if (!stream.CanSeek)
stream = CopyStream(stream);
long startPosition = stream.Position;
StreamReader sr = new StreamReader(stream);
String line;
while ((line = sr.ReadLine()) != null)
{
output.WriteLine(line);
}
stream.Position = startPosition;
}
application/octet-stream means binary. Your request variable contains binary data only some of which converts to human readable text so you cannot convert it to a string.
The best you could do is use Convert.ToBase64String to convert it to base 64 but it won't be human readable. Converting it to ASCII will corrupt the data.
Thanks to paqogomez answer about \0's being interpreted as the end of the string, I just added the following: -
request = request.Replace("\0", "");
I now get this in the output window which is perfect for my purposes, thanks.
----------Request Headers-----------
__ConnectionId: 16
__IPAddress: 127.0.0.1
__RequestUri: /VisaOM.Server.ClientServices.Services
Content-Type: application/octet-stream
__CustomErrorsEnabled: False
----------Request Message-----------
get_SecurityServiceszVisaOM.Client.Services.IServices, VisaOM.Client.Services.Interfaces,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
------End of Request Message--------
I have the following C# code which is supposed to serialize arbitrary objects to a string, and then of course deserialize it.
public static string Pack(Message _message)
{
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream original = new MemoryStream();
MemoryStream outputStream = new MemoryStream();
formatter.Serialize(original, _message);
original.Seek(0, SeekOrigin.Begin);
DeflateStream deflateStream = new DeflateStream(outputStream, CompressionMode.Compress);
original.CopyTo(deflateStream);
byte[] bytearray = outputStream.ToArray();
UTF8Encoding encoder = new UTF8Encoding();
string packed = encoder.GetString(bytearray);
return packed;
}
public static Message Unpack(string _packed_message)
{
UTF8Encoding encoder = new UTF8Encoding();
byte[] bytearray = encoder.GetBytes(_packed_message);
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream input = new MemoryStream(bytearray);
MemoryStream decompressed = new MemoryStream();
DeflateStream deflateStream = new DeflateStream(input, CompressionMode.Decompress);
deflateStream.CopyTo(decompressed); // EXCEPTION
decompressed.Seek(0, SeekOrigin.Begin);
var message = (Message)formatter.Deserialize(decompressed); // EXCEPTION 2
return message;
}
But the problem is that any time the code is ran, I am experiencing an exception. Using the above code and invoking it as shown below, I am receiving InvalidDataException: Unknown block type. Stream might be corrupted. at the marked // EXCEPTION line.
After searching for this issue I have attempted to ditch the deflation. This was only a small change: in Pack, bytearray gets created from original.ToArray() and in Unpack, I Seek() input instead of decompressed and use Deserialize(input) instead of decompressed too. The only result which changed: the exception position and body is different, yet it still happens. I receive a SerializationException: No map for object '201326592'. at // EXCEPTION 2.
I don't seem to see what is the problem. Maybe it is the whole serialization idea... the problem is that somehow managing to pack the Message instances is necessary because these objects hold the information that travel between the server and the client application. (Serialization logic is in a .Shared DLL project which is referenced on both ends, however, right now, I'm only developing the server-side first.) It also has to be told, that I am only using string outputs because right now, the TCP connection between the servers and clients are based on string read-write on the ends. So somehow it has to be brought down to the level of strings.
This is how the Message object looks like:
[Serializable]
public class Message
{
public MessageType type;
public Client from;
public Client to;
public string content;
}
(Client right now is an empty class only having the Serializable attribute, no properties or methods.)
This is how the pack-unpack gets invoked (from Main()...):
Shared.Message msg = Shared.MessageFactory.Build(Shared.MessageType.DEFAULT, new Shared.Client(), new Shared.Client(), "foobar");
string message1 = Shared.MessageFactory.Pack(msg);
Console.WriteLine(message1);
Shared.Message mess2 = Shared.MessageFactory.Unpack(message1); // Step into... here be exceptions
Console.Write(mess2.content);
Here is an image showing what happens in the IDE. The output in the console window is the value of message1.
Some investigation unfortunately also revealed that the problem could lie around the bytearray variable. When running Pack(), after the encoder creates the string, the array contains 152 values, however, after it gets decoded in Unpack(), the array has 160 values instead.
I am appreciating any help as I am really out of ideas and having this problem the progress is crippled. Thank you.
(Update) The final solution:
I would like to thank everyone answering and commenting, as I have reached the solution. Thank you.
Marc Gravell was right, I missed the closing of deflateStream and because of this, the result was either empty or corrupted. I have taken my time and rethought and rewrote the methods and now it works flawlessly. And even the purpose of sending these bytes over the networked stream is working too.
Also, as Eric J. suggested, I have switched to using ASCIIEnconding for the change between string and byte[] when the data is flowing in the Stream.
The fixed code lies below:
public static string Pack(Message _message)
{
using (MemoryStream input = new MemoryStream())
{
BinaryFormatter bformatter = new BinaryFormatter();
bformatter.Serialize(input, _message);
input.Seek(0, SeekOrigin.Begin);
using (MemoryStream output = new MemoryStream())
using (DeflateStream deflateStream = new DeflateStream(output, CompressionMode.Compress))
{
input.CopyTo(deflateStream);
deflateStream.Close();
return Convert.ToBase64String(output.ToArray());
}
}
}
public static Message Unpack(string _packed)
{
using (MemoryStream input = new MemoryStream(Convert.FromBase64String(_packed)))
using (DeflateStream deflateStream = new DeflateStream(input, CompressionMode.Decompress))
using (MemoryStream output = new MemoryStream())
{
deflateStream.CopyTo(output);
deflateStream.Close();
output.Seek(0, SeekOrigin.Begin);
BinaryFormatter bformatter = new BinaryFormatter();
Message message = (Message)bformatter.Deserialize(output);
return message;
}
}
Now everything happens just right, as the screenshot proves below. This was the expected output from the first place. The Server and Client executables communicate with each other and the message travels... and it gets serialized and unserialized properly.
In addition to the existing observations about Encoding vs base-64, note you haven't closed the deflate stream. This is important because compression-streams buffer: if you don't close, it may not write the end. For a short stream, that may mean it writes nothing at all.
using(DeflateStream deflateStream = new DeflateStream(
outputStream, CompressionMode.Compress))
{
original.CopyTo(deflateStream);
}
return Convert.ToBase64String(outputStream.GetBuffer(), 0,
(int)outputStream.Length);
Your problem is most probably in the UTF8 encoding. Your bytes are not really a character string and UTF-8 is a encoding with different byte lengths for characters.
This means the byte array may not correspond to a correctly encoded UTF-8 string (there may be some bytes missing at the end for instance.)
Try using UTF16 or ASCII which are constant length encodings (the resulting string will likely contain control characters so it won't be printable or transmitable through something like HTTP or email.)
But if you want to encode as a string it is customary to use UUEncoding to convert the byte array into a real printable string, then you can use any encoding you want.
When I run the following Main() code against your Pack() and Unpack():
static void Main(string[] args)
{
Message msg = new Message() { content = "The quick brown fox" };
string message1 = Pack(msg);
Console.WriteLine(message1);
Message mess2 = Unpack(message1); // Step into... here be exceptions
Console.Write(mess2.content);
}
I see that the bytearray
byte[] bytearray = outputStream.ToArray();
is empty.
I did modify your serialized class slightly since you did not post code for the included classes
public enum MessageType
{
DEFAULT = 0
}
[Serializable]
public class Message
{
public MessageType type;
public string from;
public string to;
public string content;
}
I suggest the following steps to resolve this:
Check the intermediate results along the way. Do you also see 0 bytes in the array? What is the string value returned by Pack()?
Dispose of your streams once you are done with them. The easiest way to do that is with the using keyword.
Edit
As Eli and Marc correctly pointed out, you cannot store arbitrary bytes in a UTF8 string. The mapping is not bijective (you can't go back and forth without loss/distortion of information). You will need a mapping that is bijective, such as the Convert.ToBase64String() approach Marc suggests.
I'm calling a library method that writes to a stream. But I want to write to a string. Is this possible? (I do not control the source code of the method I'm calling and so changing that is not an option.)
Experimenting, I tried something like this:
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
byte[] buff = new byte[stream.Length];
stream.Read(buff, 0, (int)stream.Length);
But I get an error on the last line that's something about not being able to access a closed stream. Apparently, the Serialize() method closes the stream when it's done.
Are there other options?
How about byte[] buff = stream.ToArray()?
ToArray is one of 2 correct way of getting the data out of memory stream (the other one is GetBuffer and Length). It looks like you just want byte array sized to data of the stream and ToArray does exactly that.
Note that it is by design safe to call these 3 methods on disposed stream, so you can safely wrap using(stream) around the code that write some data to the stream.
In you case stream look to be disposed by serialization code (.Serialize).
iCalendarSerializer serializer = new iCalendarSerializer();
MemoryStream stream = new MemoryStream();
using(stream)
{
serializer.Serialize(new iCalendar(), stream, System.Text.Encoding.UTF8);
}
byte[] buff = stream.ToArray();
In your example you need to change the position of the stream before read takes place:
stream.Position = 0;
stream.Read(buff, 0, (int)stream.Length);
In order to write stream to string you can use StreamReader.ReadToEnd() method:
var reader = new StreamReader(stream);
var text = reader.ReadToEnd();
I've got a method that streams files by returning a System.IO.FileStream pointing to a file opened for reading. What I want to do at this point is store the stream into a System.IO.MemoryStream as well.
The simplest option would be to first read the FileStream into the MemoryStream, and then return the MemoryStream to the caller. However this means that I'll have to buffer the entire file into memory before returning, thereby delaying the streaming response to the caller.
What I'd like to do (if possible) is read the FileStream into a MemoryStream and also stream it to the caller simultaneously. This way, the file doesn't have to be buffered before being returned, and still ends up getting stored into a MemoryStream.
You can create a custom implementation of the Stream class in which you write away the data red to a memory stream, or use my version of that which can be found at: http://upreader.codeplex.com/SourceControl/changeset/view/d287ca854370#src%2fUpreader.Usenet.Nntp%2fEncodings%2fRecordStream.cs
This can be used in the following way:
using (FileStream fileStream = File.Open("test.txt", FileMode.Open))
using (RecordStream recordStream = new RecordStream(fileStream))
{
// make sure we record incoming data
recordStream.Record();
// do something with the data
StreamReader reader = new StreamReader(recordStream);
string copy1 = reader.ReadToEnd();
// now reset
recordStream.Playback();
// do something with the data again
StreamReader reader = new StreamReader(recordStream);
string copy2 = reader.ReadToEnd();
Assert.AreEqual(cop1, copy2);
}
I Built this class particularly for Network stream but it works equally well with a fileStream and it only reads the file once without buffering it first before returning
A simple implementation would be
class RecordStream : Stream
{
public Stream BaseStream { get; }
public MemoryStream Record { get; }
....
public override int Read(byte[] buffer, int offset, int count)
{
int result = BaseStream.Read(buffer, offset, count);
// store it
Record.Write(buffer, offset, count);
return result;
}
}
I need to read the first line from a stream to determine file's encoding, and then recreate the stream with that Encoding
The following code does not work correctly:
var r = response.GetResponseStream();
var sr = new StreamReader(r);
string firstLine = sr.ReadLine();
string encoding = GetEncodingFromFirstLine(firstLine);
string text = new StreamReader(r, Encoding.GetEncoding(encoding)).ReadToEnd();
The text variable doesn't contain the whole text. For some reason the first line and several lines after it are skipped.
I tried everything: closing the StreamReader, resetting it, calling a separate GetResponseStream... but nothing worked.
I can't get the response stream again as I'm getting this file from the internet, and redownloading it again would be bad performance wise.
Update
Here's what GetEncodingFromFirstLine() looks like:
public static string GetEncodingFromFirstLine(string line)
{
int encodingIndex = line.IndexOf("encoding=");
if (encodingIndex == -1)
{
return "utf-8";
}
return line.Substring(encodingIndex + "encoding=".Length).Replace("\"", "").Replace("'", "").Replace("?", "").Replace(">", "");
}
...
// true
Assert.AreEqual("windows-1251", GetEncodingFromFirstLine(#"<?xml version=""1.0"" encoding=""windows-1251""?>"));
** Update 2 **
I'm working with XML files, and the text variable is parsed as XML:
var feedItems = XElement.Parse(text);
Well you're asking it to detect the encoding... and that requires it to read data. That's reading it from the underlying stream, and you're then creating another StreamReader around the same stream.
I suggest you:
Get the response stream
Retrieve all the data into a byte array (or MemoryStream)
Detect the encoding (which should be performed on bytes, not text - currently you're already assuming UTF-8 by creating a StreamReader)
Create a MemoryStream around the byte array, and a StreamReader around that
It's not clear what your GetEncodingFromFirstLine method does... or what this file really is. More information may make it easier to help you.
EDIT: If this is to load some XML, don't reinvent the wheel. Just give the stream to one of the existing XML-parsing classes, which will perform the appropriate detection for you.
You need to change the current position in the stream to the beginning.
r.Position = 0;
string text = new StreamReader(r, Encoding.GetEncoding(encoding)).ReadToEnd();
I found the answer to my question here:
How can I read an Http response stream twice in C#?
Stream responseStream = CopyAndClose(resp.GetResponseStream());
// Do something with the stream
responseStream.Position = 0;
// Do something with the stream again
private static Stream CopyAndClose(Stream inputStream)
{
const int readSize = 256;
byte[] buffer = new byte[readSize];
MemoryStream ms = new MemoryStream();
int count = inputStream.Read(buffer, 0, readSize);
while (count > 0)
{
ms.Write(buffer, 0, count);
count = inputStream.Read(buffer, 0, readSize);
}
ms.Position = 0;
inputStream.Close();
return ms;
}