I am using StreamReader over NetworkStream and I want just read one or more lines and another data is byte array (like file data) and I dont want to read that file data in StreamReader, for example I need to read text header line by line and when I see one empty line, header reads must end and next I must read bytes of file,
When I test this with StreamReader I get problems because StreamReader read bytes before I calling ReadLine (after first ReadLine) and after dispose StreamReader and using NetworkStream to read bytes I get block of bytes that is not start of file byte array after header, because StreamReader readed block of bytes in ReadLine and not called ReadLine.
What is wrong in StreamReader or my code settings?
using (var reader = new StreamReader(tcpClient.GetStream()))
{
while (true)
{
var line = reader.ReadLine();
headerResponse += line + "\r\n";
if (line == "")
break;
}
using (var fileStreamReader = tcpClient.GetStream())
{
byte[] bytes = new byte[1024];
var readCount = fileStreamReader.Read(bytes, 0, bytes.Length);
}
}
I created one CustomStreamReader and I read bytes one by one in ReadLine Method and fixed my problem:
public class CustomStreamReader : Stream
{
NetworkStream CurrentStream { get; set; }
public CustomStreamReader(NetworkStream currentStream)
{
CurrentStream = currentStream;
}
public override bool CanRead => CurrentStream.CanRead;
public override bool CanSeek => CurrentStream.CanSeek;
public override bool CanWrite => CurrentStream.CanWrite;
public override long Length => CurrentStream.Length;
public override long Position { get => CurrentStream.Position; set => CurrentStream.Position = value; }
public override void Flush()
{
CurrentStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return CurrentStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return CurrentStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
CurrentStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
CurrentStream.Write(buffer, offset, count);
}
public string ReadLine()
{
List<byte> result = new List<byte>();
do
{
int data = CurrentStream.ReadByte();
if (data == -1)
break;
result.Add((byte)data);
if (data == 13)
{
data = CurrentStream.ReadByte();
if (data == -1)
break;
result.Add((byte)data);
if (data == 10)
break;
}
}
while (true);
return Encoding.UTF8.GetString(result.ToArray());
}
}
Related
Using: C#, MVC 5, IIS 8
I am trying to implement an ActionFilter that will minify html. The basic approach here is to substitute the response's Stream with a custom Stream that writes input into a MemoryStream and then on the Close method minifies the content stored in the MemoryStream and writes out the (minified) content.
The problem I am having is that although the response's type is 'text/html', the content passed to the custom Stream does not look like text or html, it looks like binary. I should add that my site's pages are rendering just fine, so whatever that content is, it's not complete garbage. I added some logging statements to debug, and this is what they look like:
Minification Error | Chunks: 1 | Url: /Login/iFrameLogin.aspx |
Encoding: System.Text.UTF8Encoding | MediaType: text/html | Content:
�
I have also tried turning off dynamic compression on my site, and that made no change either. Does anyone have any ideas why my 'text/html' looks like binary?
FilterAttribute
public class MinifyHtmlFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
HttpContextBase context = filterContext.HttpContext;
HttpRequestBase request = context.Request;
HttpResponseBase response = context.Response;
Encoding encoding = response.ContentEncoding;
string mediaType = response.ContentType;
string currentUrl = request.RawUrl;
var minificationManager = HtmlMinificationManager.Current;
if (response.Filter != null
&& response.StatusCode == 200
&& minificationManager.IsSupportedMediaType(mediaType) //text/html
&& minificationManager.IsProcessablePage(currentUrl))
{
response.Filter = new HtmlMinificationFilterStream(response, minificationManager, currentUrl, encoding, mediaType);
}
}
}
Custom Stream
public class HtmlMinificationFilterStream : Stream
{
private readonly HttpResponseBase _response;
private readonly Stream _stream;
private readonly MemoryStream _cacheStream = new MemoryStream();
private readonly IMarkupMinificationManager _minificationManager;
private readonly string _currentUrl;
private readonly Encoding _encoding;
private readonly string _mediaType;
private int _chunkCount = 0;
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get { return 0; }
}
public override long Position
{
get;
set;
}
public HtmlMinificationFilterStream(HttpResponseBase response,
IMarkupMinificationManager minificationManager,
string currentUrl,
Encoding encoding,
string mediaType)
{
_response = response;
_stream = response.Filter;
_minificationManager = minificationManager;
_currentUrl = currentUrl;
_encoding = encoding;
_mediaType = mediaType;
}
public override int Read(byte[] buffer, int offset, int count)
{
return _stream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_stream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_cacheStream.Write(buffer, 0, count);
_chunkCount++;
}
public override void Flush()
{
_stream.Flush();
}
public override void Close()
{
byte[] cacheBytes = _cacheStream.ToArray();
int cacheSize = cacheBytes.Length;
string content = _encoding.GetString(cacheBytes);
var log = $" | Chunks: {_chunkCount} | Url: {_currentUrl} | Encoding: {_encoding} | MediaType: {_mediaType} | Content: {content}";
IMarkupMinifier minifier = _minificationManager.CreateMinifier();
MarkupMinificationResult minificationResult = minifier.Minify(content, _currentUrl, _encoding, false);
bool isMinified = false;
if (minificationResult.Errors.Count == 0)
{
ExceptionHandler.LogException("MINIFICATION SUCCESS" + log, System.Diagnostics.EventLogEntryType.Warning);
using (var writer = new StreamWriter(_stream, _encoding))
{
writer.Write(minificationResult.MinifiedContent);
}
isMinified = true;
}
else
{
foreach (var error in minificationResult.Errors)
{
ExceptionHandler.LogException("Minification Error" + log + " | " + error.SourceFragment, System.Diagnostics.EventLogEntryType.Warning);
}
}
if (!isMinified)
{
_cacheStream.Seek(0, SeekOrigin.Begin);
_cacheStream.CopyTo(_stream);
}
_cacheStream.SetLength(0);
_stream.Close();
}
}
Turning off compression on IIS (8) fixes the issue.
My goal is to read from one stream, transform that stream, and use it as an input to a library that accepts a Stream to read.
I am using two different libraries. One takes an output Stream and transforms it. Let's call it TransformingOutputStream. Its intended use is:
var outputStream = new TransformingOutputStream(finalDestinationOutputStream);
inputStream.CopyTo(outputStream);
I'm using another library that accepts an input Stream. It does whatever it needs and then reads from that stream. Its intended use is:
MagicStreamReadingLibrary.ProcessStream(someInputStream);
I can't pass TransformingOutputStream to it because its intended use is to be written-to, not read-from. I do not have control over either library.
How do I hook-up the TransformingOutputStream to the library function that requires reading from an input Stream?
So far this is the best working example I have, using Anonymous Pipes:
using( var pipeServer = new AnonymousPipeServerStream( PipeDirection.Out ) ) {
var pipeServerTask = Task.Run(
async () => {
using( var stream = getInputStream() ) {
await stream.CopyToAsync( new TransformingOutputStream( pipeServer ) );
}
pipeServer.WaitForPipeDrain();
pipeServer.Dispose();
} );
using( var client = new AnonymousPipeClientStream( PipeDirection.In, pipeServer.ClientSafePipeHandle ) ) {
MagicStreamReadingLibrary.ProcessStream( client );
}
pipeServerTask.Wait();
}
Write it to a flat file then read it back out.
Here is something I just threw together, it should in theory work (untested, I just know it compiles correctly).
public class BufferingStream
{
private readonly Stream _readingStream;
private readonly Stream _writingStream;
private BlockingCollection<byte[]> _buffer;
public BufferingStream()
{
_buffer = new BlockingCollection<byte[]>(new ConcurrentQueue<byte[]>());
_readingStream = new InternalReadingStream(_buffer);
_writingStream = new InternalWritingStream(_buffer);
}
public BufferingStream(int maxQueueLength)
{
_buffer = new BlockingCollection<byte[]>(new ConcurrentQueue<byte[]>(), maxQueueLength);
_readingStream = new InternalReadingStream(_buffer);
_writingStream = new InternalWritingStream(_buffer);
}
public Stream GetReadingStream()
{
return _readingStream;
}
public Stream GetWritingStream()
{
return _writingStream;
}
public int QueueLength
{
get { return _buffer.Count; }
}
public class InternalWritingStream : Stream
{
private readonly BlockingCollection<byte[]> _queuedBytes;
public InternalWritingStream(BlockingCollection<byte[]> queuedBytes)
{
_queuedBytes = queuedBytes;
}
public override void Write(byte[] buffer, int offset, int count)
{
byte[] internalBuffer = new byte[count];
Array.Copy(buffer, offset, internalBuffer, 0, count);
_queuedBytes.Add(internalBuffer);
}
public override void Close()
{
_queuedBytes.CompleteAdding();
base.Close();
}
public override void Flush()
{
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
}
private sealed class InternalReadingStream : Stream
{
private readonly BlockingCollection<byte[]> _queuedBytes;
private byte[] _currentItem;
private int _currentItemOffset;
public InternalReadingStream(BlockingCollection<byte[]> queuedBytes)
{
_queuedBytes = queuedBytes;
_currentItem = new byte[0];
_currentItemOffset = 0;
}
public override int Read(byte[] buffer, int offset, int count)
{
if (_currentItemOffset == _currentItem.Length)
{
//Try to take the next buffer, if we can't take a item it means we where done adding from the source.
var taken = _queuedBytes.TryTake(out _currentItem, Timeout.Infinite);
if (!taken)
return 0;
_currentItemOffset = 0;
}
var bytesToRead = Math.Min(count, _currentItem.Length - _currentItemOffset);
Array.Copy(_currentItem, _currentItemOffset, buffer, offset, bytesToRead);
_currentItemOffset += bytesToRead;
return bytesToRead;
}
public override void Flush()
{
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get { throw new NotSupportedException(); }
set { throw new NotSupportedException(); }
}
}
}
The way it would work in your use case would be
var bufferingStream = new BufferingStream();
Task.Run(() =>
{
using(var inputStream = GetTheStreamFromSomewhere();
using(var finalDestinationOutputStream = bufferingStream.GetWritingStream())
using(var outputStream = new TransformingOutputStream(finalDestinationOutputStream))
{
inputStream.CopyTo(outputStream);
}
}
using(var someInputStream = bufferingStream.GetReadingStream()) //Technically a using is not necessary on the reading stream but it is good to keep good habits.
{
MagicStreamReadingLibrary.ProcessStream(someInputStream);
}
Initially calls to .Read( calls ProcessStream makes will block until data becomes available. As bytes become available .Read( unblocks and passes along the data. Once finalDestinationOutputStream is disposed it will mark the queue as completed adding and once outputStream finishes its last read it will just return 0 for any subsequent calls.
If you find that your writer is much faster than your reader you may want to pass in a max queue length so writes will block till the reader has a chance to read.
i wonder if anyone encountered this:
i have a WCF service that returns a Stream (custom i've created).
during the download, it may be that an error occurs, and i want the client to know it.
the problem is, that when error occurs during download, the client stream receives "0 bytes" from the Stream.Read, and finishes (successfully) instead of exception / any other error.
is there any solution for this? it seems that WCF ignores any read errors and just closes the stream afterward.
code:
server:
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "GET",
BodyStyle = WebMessageBodyStyle.Bare,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "Data")]
Stream GetFileStream();
}
public class Service1 : IService1
{
public System.IO.Stream GetFileStream()
{
return new MyCustomStream();
}
}
implementation:
public class MyCustomStream : Stream
{
FileStream _Stream;
private bool wasRead = false;
public MyCustomStream()
{
_Stream = new FileStream(#"someFileOnDisk", FileMode.Open,
FileAccess.Read, FileShare.ReadWrite);
}
~MyCustomStream()
{
if (_Stream != null)
{
_Stream.Dispose();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (!wasRead)
{
wasRead = true;
return _Stream.Read(buffer, offset, count);
}
else
{
throw new Exception("ERROR!!!!");
}
}
public override void Close()
{
_Stream.Close();
}
client:
internal static HttpWebRequest InitializeRequest(Uri url, string i_RelativeURL, int i_Timeout, string i_MethodType, string i_ContentType)
{
System.Net.HttpWebRequest newRequest = (HttpWebRequest)WebRequest.Create(CombineURI(url.AbsoluteUri, i_RelativeURL));
newRequest.Proxy = null; // We are not using proxy
newRequest.Timeout = i_Timeout;
newRequest.Method = i_MethodType;
newRequest.ContentType = i_ContentType;
return newRequest;
}
static void Main(string[] args)
{
HttpWebRequest request = InitializeRequest(new Uri(#"http://localhost/WCFTest/Service1.svc"), "data", 999999, "GET", "application/json");
using (WebResponse response = request.GetResponse())
{
using (Stream curStream = response.GetResponseStream())
{
byte[] buffer = new byte[2000000]; // ~2MB
int read = curStream.Read(buffer, 0, buffer.Length);
int blockToWrite = read;
while (read > 0)
{
// If we reach to ~1MB of data - write it to the disk
if (blockToWrite >= 1000000)
{
blockToWrite = 0;
}
read = curStream.Read(buffer, blockToWrite, buffer.Length - blockToWrite); // Returns 0 if exception occurs on server side.
blockToWrite += read;
}
}
}
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I have an input stream and a stream reader component. This works fine but now I want to log all the traffic (save a copy in a file).
So I need to spy on a stream. A solution I am thinking of is a stream pipe (pipeline) or a stream wrapper that takes a stream as input and then gives me a first look at the traffic. Something like this:
void Init(System.Net.Sockets.NetworkStream stream)
{
System.IO.Stream wrappedStream = new MyWrapper(stream);
wrappedStream.ReadSpy = MyMethod;
XmlReader reader = XmlReader.Create(wrappedStream);
}
// This will get called after some bytes have been read from a stream,
// but before they get passed to the XmlReader
byte[] MyMethod(byte[] buffer)
{
m_Writer.Write(buffer); // write to a file
return buffer; // Give to XmlReader
}
What you want is called the Decorator Pattern. It's a technique for dynamically adding/modifying behavior:
http://sourcemaking.com/design_patterns/decorator
http://www.oodesign.com/decorator-pattern.html
To do this, you want to implement the abstract class Stream, with a constructor of factory that accepts another Stream instance. You provide an implementation for every method/overload of the abstract class that invokes the same method/overload on the decorated Stream, plus doing whatever additional work your needs require.
Once you've done that and decorated a Stream with your new decorator, It can be used interchangeably by anything else that accepts a Stream, including other similar decorators: decorators can even be nested, like layers of an onion to compose the behaviors you need.
Something like this:
class StreamInterceptor : Stream
{
public Stream DecoratedInstance { get; set; }
public event Action<byte[]> BytesRead;
public event Action<byte[]> BytesWritten;
public StreamInterceptor( Stream instance )
{
if ( instance == null ) throw new ArgumentNullException("instance");
this.DecoratedInstance = instance ;
return ;
}
public override bool CanRead
{
get { return DecoratedInstance.CanRead; }
}
public override bool CanSeek
{
get { return DecoratedInstance.CanSeek; }
}
public override bool CanWrite
{
get { return DecoratedInstance.CanWrite; }
}
public override void Flush()
{
DecoratedInstance.Flush();
return;
}
public override long Length
{
get { return DecoratedInstance.Length; }
}
public override long Position
{
get { return DecoratedInstance.Position; }
set { DecoratedInstance.Position = value; }
}
public override int Read( byte[] buffer , int offset , int count )
{
int bytesRead = DecoratedInstance.Read(buffer, offset, count);
// raise the bytes read event
byte[] temp = new byte[bytesRead];
Array.Copy(buffer,offset,temp,0,bytesRead);
BytesRead(temp);
return bytesRead;
}
public override long Seek( long offset , SeekOrigin origin )
{
return DecoratedInstance.Seek(offset, origin);
}
public override void SetLength( long value )
{
DecoratedInstance.SetLength(value);
return;
}
public override void Write( byte[] buffer , int offset , int count )
{
// raise the bytes written event
byte[] temp = new byte[count];
Array.Copy(buffer,offset,temp,0,count);
BytesWritten(temp);
DecoratedInstance.Write(buffer, offset, count);
return;
}
}
Once you have that, you can say something like this:
static void Main()
{
StreamInterceptor si = new StreamInterceptor(File.Open("foo.bar.txt",FileMode.Open,FileAccess.ReadWrite,FileShare.Read));
si.BytesRead += (bytes) => { Console.WriteLine("{0} bytes read", bytes.Length); } ;
si.BytesWritten += (bytes) => { Console.WriteLine("{0} bytes written", bytes.Length); } ;
Stream s = (Stream) si ;
DoSomethingUseful(s);
}
And your event handler will be invoked whenever somebody reads or writes from the stream.
I have a few different methods which connect to a remote host, send a message, get a reply, and use the information. this has worked fine until i use two methods in the same instance of my connection class. in the example where i'm getting the error, i run the following method;
public string sendRequestAccountID(string siteID)
{
//build message
String response = String.Empty;
TcpClient client = new TcpClient();
client.Connect(detailsHere);
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = false;
writer.WriteLine(sb.ToString());
writer.Flush();
StreamReader reader = new StreamReader(stream);
List<XmlNode> nodeList = new List<XmlNode>();
byte[] responseBytes = new byte[4096];
int bytesRead = 0;
while (true)
{
bytesRead = stream.Read(responseBytes, 0, responseBytes.Length);
if (bytesRead > 0)
{
//handle message
}
if (bytesRead == 0)
{
stream.Flush();
stream.Close();
client.Close();
string finalResponse = stuffHereToSend;
return finalResponse;
}
}
}
This sends fine, and returns a message as expected. However, if i then use the same instance of my connection class and use the following method;
public bool sendNewDevice(IDeviceInterface device)
{
NetworkStream stream;
sb = new StringBuilder();
//build message
String response = String.Empty;
TcpClient client = new TcpClient();
client.Connect(detailsHere);
stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = false;
writer.WriteLine(sb.ToString());
writer.Flush();
StreamReader reader = new StreamReader(stream);
List<XmlNode> nodeList = new List<XmlNode>();
byte[] responseBytes = new byte[4096];
int bytesRead = 0;
while (true)
{
bytesRead = stream.Read(responseBytes, 0, responseBytes.Length);
if (bytesRead > 0)
{
//handle message
}
}
}
I'm getting an error which reads "cannot access a disposed object" on;
bytesRead = stream.Read(responseBytes, 0, responseBytes.Length);
Although i thought that i'd newly just assigned the stream in the latest method. Is it trying to use the previously closed one? is there a way around this or something silly that i'm missing?
edit: is it something to with the clients not disposing correctly? the two methods are ran within a second of each other, maybe the second is trying to open before the first has closed?
When a StreamWriter (and reader) is closed or its Dispose method is called, it disposes the underlying stream. Prior to .net 4.5 there really wasn't anything you could do about it, other than use something other than StreamWriter or write a class to wrap the Stream given to StreamWriter and ignore the call to Dispose. in .NET 4.5, there is an overload you can use to tell the StreamWriter not to dispose the stream you give it. eg: new StreamWriter(stream, StreamWriter.UTF8NoBOM, 1024, false)
You could try using a wrapped stream to ignore the close (calling new StreamWriter(new NonDisposableStreamWrapper(stream))):
public class NonDisposableStreamWrapper : Stream
{
private Stream wrappedStream;
public NonDisposableStreamWrapper(Stream wrappedStream)
{
this.wrappedStream = wrappedStream;
}
public override void Flush()
{
wrappedStream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
return wrappedStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
wrappedStream.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count)
{
return wrappedStream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
wrappedStream.Write(buffer, offset, count);
}
public override bool CanRead
{
get { return wrappedStream.CanRead; }
}
public override bool CanSeek
{
get { return wrappedStream.CanSeek; }
}
public override bool CanWrite
{
get { return wrappedStream.CanWrite; }
}
public override long Length
{
get { return wrappedStream.Length; }
}
public override long Position
{
get { return wrappedStream.Position; }
set { wrappedStream.Position = value; }
}
}