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 }