none
Why the NetworkStream.Read hang(block) internet connection? RRS feed

  • Question

  • Hi All

    I have to major the HTTP traffic via proxy, When I try to upload any image or any files  it goes to hang or block. As my observation the StreamReader make it block and it is bind with NetworkStream.

       while (totalBytesRead < contentLen && (bytesRead = clientStreamReader.ReadBlock(postBuffer, 0, contentLen)) > 0)

                        {                      

                            totalBytesRead += bytesRead;

                            sw.Write(postBuffer, 0, bytesRead);                       

                            Console.Write(postBuffer, 0, bytesRead);

                        }   

    Can you help me why it block or Hang?

      public class HTTPServer
        {      
            private   int BUFFER_SIZE = 8192;
            private   char[] semiSplit = new char[] { ';' };
            private   char[] equalSplit = new char[] { '=' };
            private   String[] colonSpaceSplit = new string[] { ": " };
            private   char[] spaceSplit = new char[] { ' ' };
            private   char[] commaSplit = new char[] { ',' };
            private   Regex cookieSplitRegEx = new Regex(@",(?! )");       
            private  object _outputLockObj = new object();


            private TcpListener _listener;
            private Thread _listenerThread;
        
     

            public HTTPServer()
            {
                _listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 10123);           
            }

            public bool Start()
            {           
                _listener.Start();
                _listenerThread = new Thread(new ParameterizedThreadStart(ProxyListen));   
                _listenerThread.Start(_listener);
                return true;
            }

            public void Stop()
            {
                _listener.Stop();
                _listenerThread.Abort();           
                _listenerThread.Join();           
            }

            private  void ProxyListen(Object obj)
            {
                TcpListener listener = (TcpListener)obj;
                try
                {
                    while (true)
                    {
                        TcpClient client = listener.AcceptTcpClient();
                        while (!ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), client)) ;
                    }
                }
                catch { }            
            }


            private  void ProcessClient(Object obj)
            {
                TcpClient client = (TcpClient)obj;
                try
                {
                    ProxyProcessing(client);
                }
                catch (Exception ex){}
                finally
                {
                    client.Close();
                }
            }

            private  void ProxyProcessing(TcpClient client)
            {
             //   client.ReceiveTimeout = 150000;
              //  client.SendTimeout = 150000;
                Stream mainStream = client.GetStream();
                Stream outStream = mainStream;
                SslStream sslStream = null;
                StreamReader clientStreamReader = new StreamReader(mainStream);
               
                MemoryStream cacheStream = null;            
          
                try
                {
                    //read the first line HTTP command
                    String httpCmd = clientStreamReader.ReadLine();
                    if (String.IsNullOrEmpty(httpCmd))
                    {
                        clientStreamReader.Close();
                        mainStream.Close();
                        return;
                    }
                    //break up the line into three components
                    String[] splitBuffer = httpCmd.Split(spaceSplit, 3);

                    String method = splitBuffer[0];
                    String remoteUri = splitBuffer[1];
                    Version version = new Version(1, 1);

                    HttpWebRequest webReq;
                    HttpWebResponse response = null;
                    if (splitBuffer[0].ToUpper() == "CONNECT")
                    {
                        clientStreamReader.Close();
                        mainStream.Close();
                        return;
                    }

                    //construct the web request that we are going to issue on behalf of the client.
                    webReq = (HttpWebRequest)HttpWebRequest.Create(remoteUri);
                    webReq.Method = method;
                    webReq.ProtocolVersion = version;

                    //read the request headers from the client and copy them to our request
                    int contentLen = ReadRequestHeaders(clientStreamReader, webReq);
                    
                    webReq.Proxy = null;
                    webReq.KeepAlive = false;
                    webReq.AllowAutoRedirect = false;
                    webReq.AutomaticDecompression = DecompressionMethods.None;
            
                  
                    if (method.ToUpper() == "POST")
                    {
                        char[] postBuffer = new char[contentLen];
                        int bytesRead;
                        int totalBytesRead = 0;
                        StreamWriter sw = new StreamWriter(webReq.GetRequestStream());

                        while (totalBytesRead < contentLen && (bytesRead = clientStreamReader.ReadBlock(postBuffer, 0, contentLen)) > 0)
                        {                       
                            totalBytesRead += bytesRead;
                            sw.Write(postBuffer, 0, bytesRead);                        
                            Console.Write(postBuffer, 0, bytesRead);
                        }    
                        sw.Close();
                    }
                        
                       webReq.Timeout = 15000;

                        try
                        {
                            response = (HttpWebResponse)webReq.GetResponse();
                        }
                        catch (WebException webEx)
                        {
                            response = webEx.Response as HttpWebResponse;
                        }
                        if (response != null)
                        {
                            List<Tuple<String,String>> responseHeaders = ProcessResponse(response);
                            StreamWriter myResponseWriter = new StreamWriter(outStream);
                            Stream responseStream = response.GetResponseStream();
                            try
                            {
                                //send the response status and response headers
                                WriteResponseStatus(response.StatusCode,response.StatusDescription, myResponseWriter);
                                WriteResponseHeaders(myResponseWriter, responseHeaders);

                                Byte[] buffer;
                                if (response.ContentLength > 0)
                                    buffer = new Byte[response.ContentLength];
                                else
                                    buffer = new Byte[BUFFER_SIZE];

                                int bytesRead;

                                while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0)
                                {
                                    if (cacheStream != null)
                                        cacheStream.Write(buffer, 0, bytesRead);
                                    outStream.Write(buffer, 0, bytesRead);                             
                                }

                                responseStream.Close();                         

                                outStream.Flush();
                             
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine(ex.Message);
                            }
                            finally
                            {
                                responseStream.Close();
                                response.Close();
                                myResponseWriter.Close();
                            }
                        }                
                  
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                finally
                {
                    clientStreamReader.Close();
                    mainStream.Close();
                    if (sslStream != null)
                        sslStream.Close();
                    outStream.Close();              
                }

            }

            private  List<Tuple<String,String>> ProcessResponse(HttpWebResponse response)
            {
                String value=null;
                String header=null;
                List<Tuple<String, String>> returnHeaders = new List<Tuple<String, String>>();
                foreach (String s in response.Headers.Keys)
                {
                    if (s.ToLower() == "set-cookie")
                    {
                        header = s;
                        value = response.Headers[s];
                    }
                    else
                        returnHeaders.Add(new Tuple<String, String>(s, response.Headers[s]));
                }
                
                if (!String.IsNullOrWhiteSpace(value))
                {
                    response.Headers.Remove(header);
                    String[] cookies = cookieSplitRegEx.Split(value);
                    foreach (String cookie in cookies)
                        returnHeaders.Add(new Tuple<String, String>("Set-Cookie", cookie));

                }           
                return returnHeaders;
            }

            private  void WriteResponseStatus(HttpStatusCode code, String description, StreamWriter myResponseWriter)
            {
                String s = String.Format("HTTP/1.0 {0} {1}", (Int32)code, description);
                myResponseWriter.WriteLine(s);           
            }

            private  void WriteResponseHeaders(StreamWriter myResponseWriter, List<Tuple<String,String>> headers)
            {
                if (headers != null)
                {
                    foreach (Tuple<String,String> header in headers)
                        myResponseWriter.WriteLine(String.Format("{0}: {1}", header.Item1,header.Item2));
                }
                myResponseWriter.WriteLine();
                myResponseWriter.Flush();         
            }
            private  int ReadRequestHeaders(StreamReader sr, HttpWebRequest webReq)
            {
                String httpCmd;
                int contentLen = 0;
                do
                {
                    httpCmd = sr.ReadLine();
                    if (String.IsNullOrEmpty(httpCmd))
                        return contentLen;
                    String[] header = httpCmd.Split(colonSpaceSplit, 2, StringSplitOptions.None);
                    switch (header[0].ToLower())
                    {
                        case "host":
                            webReq.Host = header[1];
                            break;
                        case "user-agent":
                            webReq.UserAgent = header[1];
                            break;
                        case "accept":
                            webReq.Accept = header[1];
                            break;
                        case "referer":
                            webReq.Referer = header[1];
                            break;
                        case "cookie":
                            webReq.Headers["Cookie"] = header[1];
                            break;
                        case "proxy-connection":
                        case "connection":
                        case "keep-alive":                       
                            break;
                        case "content-length":
                            int.TryParse(header[1], out contentLen);
                            break;
                        case "content-type":
                            webReq.ContentType = header[1];
                            break;
                        case "if-modified-since":
                            String[] sb = header[1].Trim().Split(semiSplit);
                            DateTime d;
                            if (DateTime.TryParse(sb[0], out d))
                                webReq.IfModifiedSince = d;
                            break;
                        default:
                            try
                            {
                                webReq.Headers.Add(header[0], header[1]);
                            }
                            catch (Exception ex){                         
                            }
                            break;
                    }
                } while (!String.IsNullOrWhiteSpace(httpCmd));
                return contentLen;
            }
        }
    Thursday, May 23, 2013 2:29 PM

All replies

  • A network connection is like a file where a file has an EOF test.  You have two different problems. 

    1) A TCP connection (TCP is the transport layer for http) stays open forever until one end of the connection closes.  The sending end can't close the connection at the end of data because the connection could close before all the data is read.

    2) With binary data you can't send an END character because it could look like any random data.

    The solution is two add at the beginning of the data 8 additionals bytes that contains a count of the number of bytes that are being send.  The reciieving end then strips off the 8 bytes count and read the number of bytes of the message.  Another solution is to UUENCODE (or any encoding method) to pack the data with an alogorithm that has an unique EOF marker.


    jdweng

    Thursday, May 23, 2013 10:28 PM
  • one more things,When i read the request http header ,the "content length:24565",but when read StreamReader, ReadBlock API return 23562.Means the less then original one.

    Without any image or document,the ReadBlock API return the as it original "content length".

    Friday, May 24, 2013 5:46 AM
  • Content-length is in bytes, ReadBlock() returns number of char.

    Check to see if there's any multi-byte characters in the stream you're testing.

    Friday, May 24, 2013 6:14 AM
  • I have use Read() instead of the ReadBlock() API,now it update only text data not any images.
    • Edited by GMahen Monday, May 27, 2013 4:57 AM
    Monday, May 27, 2013 4:50 AM
  • Humm... Did you change it to use byte array instead of char array to hold the payload?

    .NET generally store Unicode characters in decomposition form C, which means if you try to store binary data with Encoding.GetString(), you can have some bytes tweaked.

    You shouldn't process anything beyond the header boundary in character arrays.

    Monday, May 27, 2013 5:33 AM
  • Please look at the below code ,i have used the char array.

     if (method.ToUpper() == "POST")
                    {
                        char[] postBuffer = new char[contentLen];
                        int bytesRead;
                        int totalBytesRead = 0;
                       StreamWriter sw = new StreamWriter(webReq.GetRequestStream());                  
                        while (totalBytesRead < contentLen && (bytesRead = clientStreamReader.Read(postBuffer, 0, contentLen)) > 0)
                        {  
                            sw.Write(postBuffer, 0, bytesRead);
                            totalBytesRead += contentLen;                     
                        }    
                        sw.Close();
                    }


    Monday, May 27, 2013 7:47 AM
  • Some guess: Maybe the date format returned by "if-modified-since" header cannot be properly consumed by DateTime.TryParse()? Try to return DateTime.Now instead when TryParse() returns false and see if it helps your issue.
    Tuesday, May 28, 2013 10:10 AM
  • I am using this with below way.case "if-modified-since":
                            String[] sb = header[1].Trim().Split(semiSplit);
                            DateTime d;
                            if (DateTime.TryParse(sb[0], out d))
                                webReq.IfModifiedSince = d;
                            break;
    Friday, May 31, 2013 7:12 AM
  • case "if-modified-since":
                            String[] sb = header[1].Trim().Split(semiSplit);
                            DateTime d;
                            if (DateTime.TryParse(sb[0], out d))
                                webReq.IfModifiedSince = d;
                            break;
    Friday, May 31, 2013 7:19 AM
  • not get succeed .by the way it doesn't go in this case

      case "if-modified-since":
                            String[] sb = header[1].Trim().Split(semiSplit);
                            DateTime d;
                            if (DateTime.TryParse(sb[0], out d))
                                webReq.IfModifiedSince = d;
                            else
                                webReq.IfModifiedSince = DateTime.Now;
                            break;

    Friday, May 31, 2013 8:23 AM
  • not get succeed .by the way it doesn't go in this case.

     
    Friday, May 31, 2013 8:25 AM
  • case "if-modified-since":
                            String[] sb = header[1].Trim().Split(semiSplit);
                            DateTime d;
                            if (DateTime.TryParse(sb[0], out d))
                                webReq.IfModifiedSince = d;
                            else
                                webReq.IfModifiedSince = DateTime.Now;
                            break;
    Friday, May 31, 2013 8:30 AM
  • not get succeeded. By the way on that time http header don't  have a "if-modified-since".
    • Edited by GMahen Friday, May 31, 2013 10:24 AM
    Friday, May 31, 2013 10:20 AM
  • Can youu check the HTTP headers to see type of data being transfers.  The data may be GZIP.  The contents length would be the upacked data size while the data transfer size would be less because of the GZIP packing.


    jdweng

    Friday, May 31, 2013 11:09 AM
  • Please check the header which i am getting from the browser proxy.It Seems to gzip packing .

    Accept: text/html, application/xhtml+xml, */*
    Referer: http://193.193.193.23:8086/forum.aspx?f=102&t=1&a=2
    Accept-Language: en-IN
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
    Content-Type: multipart/form-data; boundary=---------------------------7dd20322d0eb2
    Accept-Encoding: gzip, deflate
    Pragma: no-cache
    Cookie: CounterAccess=1; UserName=Aa; LastUserName=Aa; ASP.NET_SessionId=ffyv1mokiohuxvkj1yjoiqpf

    Saturday, June 1, 2013 6:23 AM
  • it is work with following way ,for the image i have to read from BaseStream?

    Is it right way to use? Do you know any reason for the same?

    int bytesRead;
                        int totalBytesRead = 0;
                        StreamWriter sw = new StreamWriter(webReq.GetRequestStream());

                        if (webReq.ContentType.Contains("multipart/form-data"))//for image
                        {
                            byte[] postBuffer = new byte[contentLen];                                             
                          while ((bytesRead = clientStreamReader.BaseStream.Read(postBuffer, 0, contentLen)) >0)
                            {
                                sw.BaseStream.Write(postBuffer, 0, bytesRead);
                                totalBytesRead += bytesRead;

                                if (totalBytesRead >= contentLen)
                                    break;
                            }
                        }
                        else
                        {
                            char[] postBuffer = new char[contentLen];                       
    while (totalBytesRead < contentLen && (bytesRead = clientStreamReader.Read(postBuffer, 0, contentLen)) > 0)
                            {
                                sw.Write(postBuffer, 0, bytesRead);
                                totalBytesRead += bytesRead;
                            }
                        }
                        sw.Close();

    Tuesday, June 4, 2013 2:21 PM