1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2  * Copyright (c) 2003-2012 by AG-Software 											 *
3  * All Rights Reserved.																 *
4  * Contact information for AG-Software is available at http://www.ag-software.de	 *
5  *																					 *
6  * Licence:																			 *
7  * The agsXMPP SDK is released under a dual licence									 *
8  * agsXMPP can be used under either of two licences									 *
9  * 																					 *
10  * A commercial licence which is probably the most appropriate for commercial 		 *
11  * corporate use and closed source projects. 										 *
12  *																					 *
13  * The GNU Public License (GPL) is probably most appropriate for inclusion in		 *
14  * other open source projects.														 *
15  *																					 *
16  * See README.html for details.														 *
17  *																					 *
18  * For general enquiries visit our website at:										 *
19  * http://www.ag-software.de														 *
20  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
21 
22 using System;
23 using System.IO;
24 using System.Net;
25 using System.Text;
26 using System.Threading;
27 using System.Collections;
28 using System.Security.Cryptography;
29 
30 using agsXMPP.Xml;
31 using agsXMPP.Xml.Dom;
32 using agsXMPP.protocol.extensions.bosh;
33 
34 namespace agsXMPP.Net
35 {
36 
37     public class WebRequestState
38     {
WebRequestState(WebRequest request)39         public WebRequestState(WebRequest request)
40         {
41             m_WebRequest = request;
42         }
43 
44         DateTime m_Started;
45 
46 
47         WebRequest m_WebRequest = null;
48         Stream m_RequestStream = null;
49         string m_Output = null;
50         bool m_IsSessionRequest = false;
51         Timer m_TimeOutTimer = null;
52         private bool m_Aborted = false;
53 
54         public int WebRequestId;
55 
56         /// <summary>
57         /// when was this request started (timestamp)?
58         /// </summary>
59         public DateTime Started
60         {
61             get { return m_Started; }
62             set { m_Started = value; }
63         }
64 
65         public bool IsSessionRequest
66         {
67             get { return m_IsSessionRequest; }
68             set { m_IsSessionRequest = value; }
69         }
70 
71         public string Output
72         {
73             get { return m_Output; }
74             set { m_Output = value; }
75         }
76 
77         public WebRequest WebRequest
78         {
79             get { return m_WebRequest; }
80             set { m_WebRequest = value; }
81         }
82 
83         public Stream RequestStream
84         {
85             get { return m_RequestStream; }
86             set { m_RequestStream = value; }
87         }
88 
89         public Timer TimeOutTimer
90         {
91             get { return m_TimeOutTimer; }
92             set { m_TimeOutTimer = value; }
93         }
94 
95         public bool Aborted
96         {
97             get { return m_Aborted; }
98             set { m_Aborted = value; }
99         }
100     }
101 
102     public class BoshClientSocket : BaseSocket
103     {
104         private const string    CONTENT_TYPE        = "text/xml; charset=utf-8";
105         private const string    METHOD              = "POST";
106         private const string    BOSH_VERSION        = "1.6";
107         private const int       WEBREQUEST_TIMEOUT  = 5000;
108         private const int       OFFSET_WAIT         = 5000;
109 
110         private string[]        Keys;                                   // Array of keys
111         private int             activeRequests = 0;                    // currently active (waiting) WebRequests
112         private int             CurrentKeyIdx;                          // index of the currect key
113         private Queue           m_SendQueue     = new Queue();          // Queue for stanzas to send
114         private bool            streamStarted   = false;                // is the stream started? received stream header?
115         private int             polling         = 0;
116         private bool            terminate       = false;
117         private bool            terminated      = false;
118         private DateTime        lastSend        = DateTime.MinValue;    // DateTime of the last activity/response
119 
120         private bool            m_KeepAlive     = true;
121 
122         private long            rid;
123         private bool            restart         = false;                // stream state, are we currently restarting the stream?
124         private string          sid;
125         private bool            requestIsTerminating = false;
126 
127         private int webRequestId = 1;
128 
BoshClientSocket(XmppConnection con)129         public BoshClientSocket(XmppConnection con)
130         {
131             base.m_XmppCon = con;
132         }
133 
Init()134         private void Init()
135         {
136             Keys          = null;
137             streamStarted   = false;
138             terminate       = false;
139             terminated      = false;
140         }
141 
142         #region << Properties >>
143         private Jid             m_To;
144         private int             m_Wait          = 300;  // 5 minutes by default, if you think this is to long change it over the public property
145         private int             m_Requests      = 2;
146 
147 #if !CF && !CF_2
148         private int             m_MinCountKeys  = 1000;
149         private int             m_MaxCountKeys  = 9999;
150 #else
151 		// set this lower on embedded devices because the key generation is slow there
152         private int             m_MinCountKeys  = 10;
153         private int             m_MaxCountKeys  = 99;
154 #endif
155         private int             m_Hold          = 1;    // should be 1
156         private int             m_MaxPause      = 0;
157         private WebProxy        m_Proxy         = null;
158 
159         public Jid To
160         {
161             get { return m_To; }
162             set { m_To = value; }
163         }
164 
165         /// <summary>
166         /// The longest time (in seconds) that the connection manager is allowed to wait before responding to any request during the session.
167         /// This enables the client to prevent its TCP connection from expiring due to inactivity, as well as to limit the delay before
168         /// it discovers any network failure.
169         /// </summary>
170         public int Wait
171         {
172             get { return m_Wait; }
173             set { m_Wait = value; }
174         }
175 
176         public int Requests
177         {
178             get { return m_Requests; }
179             set { m_Requests = value; }
180         }
181 
182         public int MaxCountKeys
183         {
184             get { return m_MaxCountKeys; }
185             set { m_MaxCountKeys = value; }
186         }
187 
188         public int MinCountKeys
189         {
190             get { return m_MinCountKeys; }
191             set { m_MinCountKeys = value; }
192         }
193 
194         /// <summary>
195         /// This attribute specifies the maximum number of requests the connection manager is allowed to keep waiting
196         /// at any one time during the session. If the client is not able to use HTTP Pipelining then this SHOULD be set to "1".
197         /// </summary>
198         public int Hold
199         {
200             get { return m_Hold; }
201             set { m_Hold = value; }
202         }
203 
204         /// <summary>
205         /// Keep Alive for HTTP Webrequests, its disables by default because not many BOSH implementations support Keep Alives
206         /// </summary>
207         public bool KeepAlive
208         {
209             get { return m_KeepAlive; }
210             set { m_KeepAlive = value; }
211         }
212 
213         /// <summary>
214         /// If the connection manager supports session pausing (see Inactivity) then it SHOULD advertise that to the client
215         /// by including a 'maxpause' attribute in the session creation response element.
216         /// The value of the attribute indicates the maximum length of a temporary session pause (in seconds) that a client MAY request.
217         /// 0 is the default value and indicated that the connection manager supports no session pausing.
218         /// </summary>
219         public int MaxPause
220         {
221             get { return m_MaxPause; }
222             set { m_MaxPause = value; }
223         }
224 
225         public bool SupportsSessionPausing
226         {
227             get { return !(m_MaxPause == 0); }
228         }
229 
230         public WebProxy Proxy
231         {
232             get { return m_Proxy; }
233             set { m_Proxy = value; }
234         }
235         #endregion
236 
237         private string DummyStreamHeader
238         {
239             get
240             {
241                 // <stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' id='1075705237'>
242                 // create dummy stream header
243                 StringBuilder sb = new StringBuilder();
244 
245                 sb.Append("<stream:stream");
246 
247                 sb.Append(" xmlns='");
248                 sb.Append(Uri.CLIENT);
249 
250                 sb.Append("' xmlns:stream='");
251                 sb.Append(Uri.STREAM);
252 
253                 sb.Append("' id='");
254                 sb.Append(sid);
255 
256                 sb.Append("' version='");
257                 sb.Append("1.0");
258 
259                 sb.Append("'>");
260 
261                 return sb.ToString();
262             }
263         }
264 
265         /// <summary>
266         /// Generates a bunch of keys
267         /// </summary>
GenerateKeys()268         private void GenerateKeys()
269         {
270             /*
271             13.3 Generating the Key Sequence
272 
273             Prior to requesting a new session, the client MUST select an unpredictable counter ("n") and an unpredictable value ("seed").
274             The client then processes the "seed" through a cryptographic hash and converts the resulting 160 bits to a hexadecimal string K(1).
275             It does this "n" times to arrive at the initial key K(n). The hashing algorithm MUST be SHA-1 as defined in RFC 3174.
276 
277             Example 25. Creating the key sequence
278 
279                     K(1) = hex(SHA-1(seed))
280                     K(2) = hex(SHA-1(K(1)))
281                     ...
282                     K(n) = hex(SHA-1(K(n-1)))
283 
284             */
285             int countKeys = GetRandomNumber(m_MinCountKeys, m_MaxCountKeys);
286 
287             Keys = new string[countKeys];
288             string prev = GenerateSeed();
289 
290             for (int i = 0; i < countKeys; i++)
291             {
292                 Keys[i] = Util.Hash.Sha1Hash(prev);
293                 prev = Keys[i];
294             }
295             CurrentKeyIdx = countKeys - 1;
296         }
297 
GenerateSeed()298         private string GenerateSeed()
299         {
300             int m_lenght = 10;
301 
302 #if CF
303             util.RandomNumberGenerator rng = util.RandomNumberGenerator.Create();
304 #else
305             RandomNumberGenerator rng = RandomNumberGenerator.Create();
306 #endif
307             byte[] buf = new byte[m_lenght];
308             rng.GetBytes(buf);
309 
310             return Util.Hash.HexToString(buf);
311         }
312 
GenerateRid()313         private int GenerateRid()
314         {
315             int min = 1;
316             int max = int.MaxValue;
317 
318             Random rnd = new Random();
319 
320             return rnd.Next(min, max);
321         }
322 
GetRandomNumber(int min, int max)323         private int GetRandomNumber(int min, int max)
324         {
325             Random rnd = new Random();
326             return rnd.Next(min, max);
327         }
328 
Reset()329         public override void Reset()
330         {
331             base.Reset();
332 
333             streamStarted   = false;
334             restart         = true;
335         }
336 
RequestBoshSession()337         public void RequestBoshSession()
338         {
339             /*
340             Example 1. Requesting a BOSH session
341 
342             POST /webclient HTTP/1.1
343             Host: httpcm.jabber.org
344             Accept-Encoding: gzip, deflate
345             Content-Type: text/xml; charset=utf-8
346             Content-Length: 104
347 
348             <body content='text/xml; charset=utf-8'
349                   hold='1'
350                   rid='1573741820'
351                   to='jabber.org'
352                   route='xmpp:jabber.org:9999'
353                   secure='true'
354                   ver='1.6'
355                   wait='60'
356                   ack='1'
357                   xml:lang='en'
358                   xmlns='http://jabber.org/protocol/httpbind'/>
359              */
360 
361             lastSend = DateTime.Now;
362 
363             // Generate the keys
364             GenerateKeys();
365             rid = GenerateRid();
366             Body body = new Body();
367             /*
368              * <body hold='1' xmlns='http://jabber.org/protocol/httpbind'
369              *  to='vm-2k'
370              *  wait='300'
371              *  rid='782052'
372              *  newkey='8e7d6cec12004e2bfcf7fc000310fda87bc8337c'
373              *  ver='1.6'
374              *  xmpp:xmlns='urn:xmpp:xbosh'
375              *  xmpp:version='1.0'/>
376              */
377             //window='5' content='text/xml; charset=utf-8'
378             //body.SetAttribute("window", "5");
379             //body.SetAttribute("content", "text/xml; charset=utf-8");
380 
381             body.Version        = BOSH_VERSION;
382             body.XmppVersion    = "1.0";
383             body.Hold           = m_Hold;
384             body.Wait           = m_Wait;
385             body.Rid            = rid;
386             body.Polling        = 0;
387             body.Requests       = m_Requests;
388             body.To             = new Jid(m_XmppCon.Server);
389 
390             body.NewKey         = Keys[CurrentKeyIdx];
391 
392             body.SetAttribute("xmpp:xmlns", "urn:xmpp:xbosh");
393 
394             activeRequests++;
395 
396             HttpWebRequest req = (HttpWebRequest) CreateWebrequest(Address);
397 
398             WebRequestState state = new WebRequestState(req);
399             state.Started           = DateTime.Now;
400             state.Output            = body.ToString();
401             state.IsSessionRequest  = true;
402 
403             req.Method          = METHOD;
404             req.ContentType     = CONTENT_TYPE;
405             req.Timeout         = m_Wait * 1000;
406             req.KeepAlive       = m_KeepAlive;
407             req.ContentLength   = Encoding.UTF8.GetBytes(state.Output).Length; // state.Output.Length;
408 
409             try
410             {
411                 IAsyncResult result = req.BeginGetRequestStream(new AsyncCallback(this.OnGetSessionRequestStream), state);
412             }
413             catch (Exception)
414             {
415             }
416         }
417 
OnGetSessionRequestStream(IAsyncResult ar)418         private void OnGetSessionRequestStream(IAsyncResult ar)
419         {
420             try
421             {
422                 WebRequestState state = ar.AsyncState as WebRequestState;
423                 HttpWebRequest req = state.WebRequest as HttpWebRequest;
424 
425                 Stream outputStream = req.EndGetRequestStream(ar);
426 
427                 byte[] bytes = Encoding.UTF8.GetBytes(state.Output);
428 
429                 state.RequestStream = outputStream;
430                 IAsyncResult result = outputStream.BeginWrite(bytes, 0, bytes.Length, OnEndWrite, state);
431             }
432             catch (WebException ex)
433             {
434                 FireOnError(ex);
435                 Disconnect();
436             }
437         }
438 
OnGetSessionRequestResponse(IAsyncResult result)439         private void OnGetSessionRequestResponse(IAsyncResult result)
440         {
441             // grab the custom state object
442             WebRequestState state = (WebRequestState)result.AsyncState;
443             HttpWebRequest request = (HttpWebRequest)state.WebRequest;
444 
445             //state.TimeOutTimer.Dispose();
446 
447             // get the Response
448             HttpWebResponse resp = (HttpWebResponse)request.EndGetResponse(result);
449 
450             // The server must always return a 200 response code,
451             // sending any session errors as specially-formatted identifiers.
452             if (resp.StatusCode != HttpStatusCode.OK)
453             {
454                 return;
455             }
456 
457             Stream rs = resp.GetResponseStream();
458 
459             int readlen;
460             byte[] readbuf = new byte[1024];
461             MemoryStream ms = new MemoryStream();
462             while ((readlen = rs.Read(readbuf, 0, readbuf.Length)) > 0)
463             {
464                 ms.Write(readbuf, 0, readlen);
465             }
466 
467             byte[] recv = ms.ToArray();
468 
469             if (recv.Length > 0)
470             {
471                 string body = null;
472                 string stanzas = null;
473 
474                 string res = Encoding.UTF8.GetString(recv, 0, recv.Length);
475 
476                 ParseResponse(res, ref body, ref stanzas);
477 
478                 Document doc = new Document();
479                 doc.LoadXml(body);
480                 Body boshBody = doc.RootElement as Body;
481 
482                 sid         = boshBody.Sid;
483                 polling     = boshBody.Polling;
484                 m_MaxPause  = boshBody.MaxPause;
485 
486                 byte[] bin = Encoding.UTF8.GetBytes(DummyStreamHeader + stanzas);
487 
488                 base.FireOnReceive(bin, bin.Length);
489 
490                 // cleanup webrequest resources
491                 ms.Close();
492                 rs.Close();
493                 resp.Close();
494 
495                 activeRequests--;
496 
497                 if (activeRequests == 0)
498                     StartWebRequest();
499             }
500         }
501 
502         /// <summary>
503         /// This is ugly code, but currently all BOSH server implementaions are not namespace correct,
504         /// which means we can't use the XML parser here and have to spit it with string functions.
505         /// </summary>
506         /// <param name="res"></param>
507         /// <param name="body"></param>
508         /// <param name="stanzas"></param>
ParseResponse(string res, ref string body, ref string stanzas)509         private void ParseResponse(string res, ref string body, ref string stanzas)
510         {
511             res = res.Trim();
512             if (res.EndsWith("/>"))
513             {
514                 // <body ..../>
515                 // empty response
516                 body = res;
517                 stanzas = null;
518             }
519             else
520             {
521                 /*
522                  * <body .....>
523                  *  <message/>
524                  *  <presence/>
525                  * </body>
526                  */
527 
528                 // find position of the first closing angle bracket
529                 int startPos = res.IndexOf(">");
530                 // find position of the last opening angle bracket
531                 int endPos = res.LastIndexOf("<");
532 
533                 body = res.Substring(0, startPos) + "/>";
534                 stanzas = res.Substring(startPos + 1, endPos - startPos - 1);
535             }
536         }
537 
538         #region << Public Methods and Functions >>
Connect()539         public override void Connect()
540         {
541             base.Connect();
542 
543             Init();
544             FireOnConnect();
545 
546             RequestBoshSession();
547         }
548 
Disconnect()549         public override void Disconnect()
550         {
551             base.Disconnect();
552 
553             FireOnDisconnect();
554             //m_Connected = false;
555         }
556 
Send(byte[] bData)557         public override void Send(byte[] bData)
558         {
559             base.Send(bData);
560 
561             Send(Encoding.UTF8.GetString(bData, 0, bData.Length));
562         }
563 
564 
Send(string data)565         public override void Send(string data)
566         {
567             base.Send(data);
568 
569             // This are hacks because we send no stream headers and footer in Bosh
570             if (data.StartsWith("<stream:stream"))
571             {
572                 if (!streamStarted && !restart)
573                     streamStarted = true;
574                 else
575                 {
576                     byte[] bin = Encoding.UTF8.GetBytes(DummyStreamHeader);
577                     base.FireOnReceive(bin, bin.Length);
578                 }
579                 return;
580             }
581 
582             if (data.EndsWith("</stream:stream>"))
583             {
584                 protocol.client.Presence pres = new protocol.client.Presence();
585                 pres.Type = agsXMPP.protocol.client.PresenceType.unavailable;
586                 data = pres.ToString(); //= "<presence type='unavailable' xmlns='jabber:client'/>";
587                 terminate = true;
588             }
589             //    return;
590 
591             lock (m_SendQueue)
592             {
593                 m_SendQueue.Enqueue(data);
594             }
595 
596             CheckDoRequest();
597             return;
598         }
599         #endregion
600 
CheckDoRequest()601         private void CheckDoRequest()
602         {
603             /*
604              * requestIsTerminating is true when a webrequest is ending
605              * when the requests ends a new request gets started immedialtely,
606              * so we don't have to create another request in the case
607              */
608             if (!requestIsTerminating)
609                 Request();
610         }
611 
Request()612         private void Request()
613         {
614             if (activeRequests < 2)
615                 StartWebRequest();
616         }
617 
BuildPostData()618         private string BuildPostData()
619         {
620             CurrentKeyIdx--;
621             rid++;
622 
623             StringBuilder sb = new StringBuilder();
624 
625             Body body = new Body();
626 
627             body.Rid        = rid;
628             body.Key        = Keys[CurrentKeyIdx];
629 
630             if (CurrentKeyIdx == 0)
631             {
632                 // this is our last key
633                 // Generate a new key sequence
634                 GenerateKeys();
635                 body.NewKey = Keys[CurrentKeyIdx];
636             }
637 
638             body.Sid        = sid;
639             //body.Polling    = 0;
640             body.To = new Jid(m_XmppCon.Server);
641 
642             if (restart)
643             {
644                 body.XmppRestart = true;
645                 restart = false;
646             }
647 
648             lock (m_SendQueue)
649             {
650                 if (terminate && m_SendQueue.Count == 1)
651                     body.Type = BoshType.terminate;
652 
653                 if (m_SendQueue.Count > 0)
654                 {
655                     sb.Append(body.StartTag());
656 
657                     while (m_SendQueue.Count > 0)
658                     {
659                         string data = m_SendQueue.Dequeue() as string;
660                         sb.Append(data);
661                     }
662 
663                     sb.Append(body.EndTag());
664 
665                     return sb.ToString();
666                 }
667                 else
668                     return body.ToString();
669             }
670         }
671 
StartWebRequest()672         private void StartWebRequest()
673         {
674             StartWebRequest(false, null);
675         }
676 
StartWebRequest(bool retry, string content)677         private void StartWebRequest(bool retry, string content)
678         {
679             lock (this)
680             {
681                 webRequestId++;
682             }
683 
684             activeRequests++;
685 
686             lastSend = DateTime.Now;
687 
688             HttpWebRequest req = (HttpWebRequest) CreateWebrequest(Address);;
689 
690             WebRequestState state = new WebRequestState(req);
691             state.Started = DateTime.Now;
692             state.WebRequestId = webRequestId;
693 
694             if (!retry)
695                 state.Output = BuildPostData();
696             else
697                 state.Output = content;
698 
699             req.Method          = METHOD;
700             req.ContentType     = CONTENT_TYPE;
701             req.Timeout         = m_Wait * 1000;
702             req.KeepAlive       = m_KeepAlive;
703             req.ContentLength   = Encoding.UTF8.GetBytes(state.Output).Length;
704 
705             // Create the delegate that invokes methods for the timer.
706             TimerCallback timerDelegate = TimeOutGetRequestStream;
707             Timer timeoutTimer = new Timer(timerDelegate, state, WEBREQUEST_TIMEOUT, WEBREQUEST_TIMEOUT);
708             state.TimeOutTimer = timeoutTimer;
709 
710             try
711             {
712                 req.BeginGetRequestStream(OnGetRequestStream, state);
713             }
714             catch(Exception ex)
715             {
716                 //Console.WriteLine(ex.Message);
717             }
718         }
719 
TimeOutGetRequestStream(Object stateObj)720         public void TimeOutGetRequestStream(Object stateObj)
721         {
722 
723             //Console.WriteLine("Web Request timed out");
724 
725             WebRequestState state = stateObj as WebRequestState;
726             state.TimeOutTimer.Dispose();
727             state.Aborted = true;
728             state.WebRequest.Abort();
729         }
730 
OnGetRequestStream(IAsyncResult ar)731         private void OnGetRequestStream(IAsyncResult ar)
732         {
733             try
734             {
735                 WebRequestState state = ar.AsyncState as WebRequestState;
736 
737                 if (state.Aborted)
738                 {
739                     activeRequests--;
740                     StartWebRequest(true, state.Output);
741                 }
742                 else
743                 {
744                     state.TimeOutTimer.Dispose();
745                     HttpWebRequest req = state.WebRequest as HttpWebRequest;
746 
747                     Stream requestStream = req.EndGetRequestStream(ar);
748                     state.RequestStream = requestStream;
749                     byte[] bytes = Encoding.UTF8.GetBytes(state.Output);
750                     requestStream.BeginWrite(bytes, 0, bytes.Length, OnEndWrite, state);
751                 }
752             }
753             catch (Exception ex)
754             {
755                 activeRequests--;
756 
757                 WebRequestState state = ar.AsyncState as WebRequestState;
758                 StartWebRequest(true, state.Output);
759             }
760         }
761 
OnEndWrite(IAsyncResult ar)762         private void OnEndWrite(IAsyncResult ar)
763         {
764             WebRequestState state = ar.AsyncState as WebRequestState;
765 
766             HttpWebRequest req      = state.WebRequest as HttpWebRequest;
767             Stream requestStream    = state.RequestStream;
768 
769             requestStream.EndWrite(ar);
770             requestStream.Close();
771 
772             IAsyncResult result;
773 
774             try
775             {
776                 if (state.IsSessionRequest)
777                     req.BeginGetResponse(OnGetSessionRequestResponse, state);
778                 else
779                     req.BeginGetResponse(OnGetResponse, state);
780 
781             }
782             catch (Exception ex)
783             {
784                 //Console.WriteLine(ex.Message);
785             }
786         }
787 
OnGetResponse(IAsyncResult ar)788         private void OnGetResponse(IAsyncResult ar)
789         {
790             try
791             {
792                 requestIsTerminating = true;
793                 // grab the custom state object
794                 WebRequestState state = (WebRequestState)ar.AsyncState;
795                 HttpWebRequest request = (HttpWebRequest)state.WebRequest;
796                 HttpWebResponse resp = null;
797 
798                 if (request.HaveResponse)
799                 {
800                     // TODO, its crashing mostly here
801                     // get the Response
802                     try
803                     {
804                         resp = (HttpWebResponse) request.EndGetResponse(ar);
805                     }
806                     catch (WebException ex)
807                     {
808                         activeRequests--;
809                         requestIsTerminating = false;
810                         if (ex.Response == null)
811                         {
812                             StartWebRequest();
813                         }
814                         else
815                         {
816                             HttpWebResponse res = ex.Response as HttpWebResponse;
817                             if (res.StatusCode == HttpStatusCode.NotFound)
818                             {
819                                 TerminateBoshSession();
820                             }
821                         }
822                         return;
823                     }
824 
825                     // The server must always return a 200 response code,
826                     // sending any session errors as specially-formatted identifiers.
827                     if (resp.StatusCode != HttpStatusCode.OK)
828                     {
829                         activeRequests--;
830                         requestIsTerminating = false;
831                         if (resp.StatusCode == HttpStatusCode.NotFound)
832                         {
833                             //Console.WriteLine("Not Found");
834                             TerminateBoshSession();
835                         }
836                         return;
837                     }
838                 }
839                 else
840                 {
841                     //Console.WriteLine("No response");
842                 }
843 
844                 Stream rs = resp.GetResponseStream();
845 
846                 int readlen;
847                 byte[] readbuf = new byte[1024];
848                 MemoryStream ms = new MemoryStream();
849                 while ((readlen = rs.Read(readbuf, 0, readbuf.Length)) > 0)
850                 {
851                     ms.Write(readbuf, 0, readlen);
852                 }
853 
854                 byte[] recv = ms.ToArray();
855 
856                 if (recv.Length > 0)
857                 {
858                     string sbody = null;
859                     string stanzas = null;
860 
861                     ParseResponse(Encoding.UTF8.GetString(recv, 0, recv.Length), ref sbody, ref stanzas);
862 
863                     if (stanzas != null)
864                     {
865                         byte[] bStanzas = Encoding.UTF8.GetBytes(stanzas);
866                         base.FireOnReceive(bStanzas, bStanzas.Length);
867                     }
868                     else
869                     {
870                         if (sbody != null)
871                         {
872                             var doc = new Document();
873                             doc.LoadXml(sbody);
874                             if (doc.RootElement != null)
875                             {
876                                 var body = doc.RootElement as Body;
877                                 if (body.Type == BoshType.terminate)
878                                     TerminateBoshSession();
879                             }
880                         }
881 
882                         if (terminate && !terminated)
883                         {
884                             // empty teminate response
885                             TerminateBoshSession();
886                         }
887                     }
888                 }
889 
890                 // cleanup webrequest resources
891                 ms.Close();
892                 rs.Close();
893                 resp.Close();
894 
895                 activeRequests--;
896                 requestIsTerminating = false;
897 
898                 //if (activeRequests == 0 && !terminated)
899                 if ( (activeRequests == 0 && !terminated)
900                     || (activeRequests == 1 && m_SendQueue.Count > 0) )
901                 {
902                     StartWebRequest();
903                 }
904             }
905             catch (Exception ex)
906             {
907 
908             }
909         }
910 
CreateWebrequest(string address)911         private WebRequest CreateWebrequest(string address)
912         {
913             WebRequest webReq = WebRequest.Create(address);
914 #if !CF_2
915             if (m_Proxy != null)
916                 webReq.Proxy = m_Proxy;
917             else
918             {
919                 if (webReq.Proxy != null)
920                     webReq.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
921             }
922 
923 #endif
924             return webReq;
925         }
926 
TerminateBoshSession()927         private void TerminateBoshSession()
928         {
929             // empty teminate response
930             byte[] bStanzas = Encoding.UTF8.GetBytes("</stream:stream>");
931             base.FireOnReceive(bStanzas, bStanzas.Length);
932             terminated = true;
933         }
934     }
935 }