1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 namespace IceInternal
6 {
7     using System;
8     using System.Diagnostics;
9     using System.Net.Sockets;
10     using System.Security.Cryptography;
11     using System.Text;
12 
13     sealed class WSTransceiver : Transceiver
14     {
fd()15         public Socket fd()
16         {
17             return _delegate.fd();
18         }
19 
initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData)20         public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData)
21         {
22             //
23             // Delegate logs exceptions that occur during initialize(), so there's no need to trap them here.
24             //
25             if(_state == StateInitializeDelegate)
26             {
27                 int op = _delegate.initialize(readBuffer, writeBuffer, ref hasMoreData);
28                 if(op != 0)
29                 {
30                     return op;
31                 }
32                 _state = StateConnected;
33             }
34 
35             try
36             {
37                 if(_state == StateConnected)
38                 {
39                     //
40                     // We don't know how much we'll need to read.
41                     //
42                     _readBuffer.resize(1024, true);
43                     _readBuffer.b.position(0);
44                     _readBufferPos = 0;
45 
46                     //
47                     // The server waits for the client's upgrade request, the
48                     // client sends the upgrade request.
49                     //
50                     _state = StateUpgradeRequestPending;
51                     if(!_incoming)
52                     {
53                         //
54                         // Compose the upgrade request.
55                         //
56                         StringBuilder @out = new StringBuilder();
57                         @out.Append("GET " + _resource + " HTTP/1.1\r\n");
58                         @out.Append("Host: " + _host + "\r\n");
59                         @out.Append("Upgrade: websocket\r\n");
60                         @out.Append("Connection: Upgrade\r\n");
61                         @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n");
62                         @out.Append("Sec-WebSocket-Version: 13\r\n");
63                         @out.Append("Sec-WebSocket-Key: ");
64 
65                         //
66                         // The value for Sec-WebSocket-Key is a 16-byte random number,
67                         // encoded with Base64.
68                         //
69                         byte[] key = new byte[16];
70                         _rand.NextBytes(key);
71                         _key = System.Convert.ToBase64String(key);
72                         @out.Append(_key + "\r\n\r\n"); // EOM
73 
74                         byte[] bytes = _utf8.GetBytes(@out.ToString());
75                         _writeBuffer.resize(bytes.Length, false);
76                         _writeBuffer.b.position(0);
77                         _writeBuffer.b.put(bytes);
78                         _writeBuffer.b.flip();
79                     }
80                 }
81 
82                 //
83                 // Try to write the client's upgrade request.
84                 //
85                 if(_state == StateUpgradeRequestPending && !_incoming)
86                 {
87                     if(_writeBuffer.b.hasRemaining())
88                     {
89                         int s = _delegate.write(_writeBuffer);
90                         if(s != 0)
91                         {
92                             return s;
93                         }
94                     }
95                     Debug.Assert(!_writeBuffer.b.hasRemaining());
96                     _state = StateUpgradeResponsePending;
97                 }
98 
99                 while(true)
100                 {
101                     if(_readBuffer.b.hasRemaining())
102                     {
103                         int s = _delegate.read(_readBuffer, ref hasMoreData);
104                         if(s == SocketOperation.Write || _readBuffer.b.position() == 0)
105                         {
106                             return s;
107                         }
108                     }
109 
110                     //
111                     // Try to read the client's upgrade request or the server's response.
112                     //
113                     if((_state == StateUpgradeRequestPending && _incoming) ||
114                        (_state == StateUpgradeResponsePending && !_incoming))
115                     {
116                         //
117                         // Check if we have enough data for a complete message.
118                         //
119                         int p = _parser.isCompleteMessage(_readBuffer.b, 0, _readBuffer.b.position());
120                         if(p == -1)
121                         {
122                             if(_readBuffer.b.hasRemaining())
123                             {
124                                 return SocketOperation.Read;
125                             }
126 
127                             //
128                             // Enlarge the buffer and try to read more.
129                             //
130                             int oldSize = _readBuffer.b.position();
131                             if(oldSize + 1024 > _instance.messageSizeMax())
132                             {
133                                 throw new Ice.MemoryLimitException();
134                             }
135                             _readBuffer.resize(oldSize + 1024, true);
136                             _readBuffer.b.position(oldSize);
137                             continue; // Try again to read the response/request
138                         }
139 
140                         //
141                         // Set _readBufferPos at the end of the response/request message.
142                         //
143                         _readBufferPos = p;
144                     }
145 
146                     //
147                     // We're done, the client's upgrade request or server's response is read.
148                     //
149                     break;
150                 }
151 
152                 try
153                 {
154                     //
155                     // Parse the client's upgrade request.
156                     //
157                     if(_state == StateUpgradeRequestPending && _incoming)
158                     {
159                         if(_parser.parse(_readBuffer.b, 0, _readBufferPos))
160                         {
161                             handleRequest(_writeBuffer);
162                             _state = StateUpgradeResponsePending;
163                         }
164                         else
165                         {
166                             throw new Ice.ProtocolException("incomplete request message");
167                         }
168                     }
169 
170                     if(_state == StateUpgradeResponsePending)
171                     {
172                         if(_incoming)
173                         {
174                             if(_writeBuffer.b.hasRemaining())
175                             {
176                                 int s = _delegate.write(_writeBuffer);
177                                 if(s != 0)
178                                 {
179                                     return s;
180                                 }
181                             }
182                         }
183                         else
184                         {
185                             //
186                             // Parse the server's response
187                             //
188                             if(_parser.parse(_readBuffer.b, 0, _readBufferPos))
189                             {
190                                 handleResponse();
191                             }
192                             else
193                             {
194                                 throw new Ice.ProtocolException("incomplete response message");
195                             }
196                         }
197                     }
198                 }
199                 catch(WebSocketException ex)
200                 {
201                     throw new Ice.ProtocolException(ex.Message);
202                 }
203 
204                 _state = StateOpened;
205                 _nextState = StateOpened;
206 
207                 hasMoreData = _readBufferPos < _readBuffer.b.position();
208             }
209             catch(Ice.LocalException ex)
210             {
211                 if(_instance.traceLevel() >= 2)
212                 {
213                     _instance.logger().trace(_instance.traceCategory(),
214                         protocol() + " connection HTTP upgrade request failed\n" + ToString() + "\n" + ex);
215                 }
216                 throw;
217             }
218 
219             if(_instance.traceLevel() >= 1)
220             {
221                 if(_incoming)
222                 {
223                     _instance.logger().trace(_instance.traceCategory(),
224                         "accepted " + protocol() + " connection HTTP upgrade request\n" + ToString());
225                 }
226                 else
227                 {
228                     _instance.logger().trace(_instance.traceCategory(),
229                         protocol() + " connection HTTP upgrade request accepted\n" + ToString());
230                 }
231             }
232 
233             return SocketOperation.None;
234         }
235 
closing(bool initiator, Ice.LocalException reason)236         public int closing(bool initiator, Ice.LocalException reason)
237         {
238             if(_instance.traceLevel() >= 1)
239             {
240                 _instance.logger().trace(_instance.traceCategory(),
241                     "gracefully closing " + protocol() + " connection\n" + ToString());
242             }
243 
244             int s = _nextState == StateOpened ? _state : _nextState;
245 
246             if(s == StateClosingRequestPending && _closingInitiator)
247             {
248                 //
249                 // If we initiated a close connection but also received a
250                 // close connection, we assume we didn't initiated the
251                 // connection and we send the close frame now. This is to
252                 // ensure that if both peers close the connection at the same
253                 // time we don't hang having both peer waiting for the close
254                 // frame of the other.
255                 //
256                 Debug.Assert(!initiator);
257                 _closingInitiator = false;
258                 return SocketOperation.Write;
259             }
260             else if(s >= StateClosingRequestPending)
261             {
262                 return SocketOperation.None;
263             }
264 
265             _closingInitiator = initiator;
266             if(reason is Ice.CloseConnectionException)
267             {
268                 _closingReason = CLOSURE_NORMAL;
269             }
270             else if(reason is Ice.ObjectAdapterDeactivatedException ||
271                     reason is Ice.CommunicatorDestroyedException)
272             {
273                 _closingReason = CLOSURE_SHUTDOWN;
274             }
275             else if(reason is Ice.ProtocolException)
276             {
277                 _closingReason  = CLOSURE_PROTOCOL_ERROR;
278             }
279             else if(reason is Ice.MemoryLimitException)
280             {
281                 _closingReason = CLOSURE_TOO_BIG;
282             }
283 
284             if(_state == StateOpened)
285             {
286                 _state = StateClosingRequestPending;
287                 return initiator ? SocketOperation.Read : SocketOperation.Write;
288             }
289             else
290             {
291                 _nextState = StateClosingRequestPending;
292                 return SocketOperation.None;
293             }
294         }
295 
close()296         public void close()
297         {
298             _delegate.close();
299             _state = StateClosed;
300 
301             //
302             // Clear the buffers now instead of waiting for destruction.
303             //
304             if(!_readPending)
305             {
306                 _readBuffer.clear();
307             }
308             if(!_writePending)
309             {
310                 _writeBuffer.clear();
311             }
312         }
313 
bind()314         public EndpointI bind()
315         {
316             Debug.Assert(false);
317             return null;
318         }
319 
destroy()320         public void destroy()
321         {
322             _delegate.destroy();
323         }
324 
write(Buffer buf)325         public int write(Buffer buf)
326         {
327             if(_writePending)
328             {
329                 return SocketOperation.Write;
330             }
331 
332             if(_state < StateOpened)
333             {
334                 if(_state < StateConnected)
335                 {
336                     return _delegate.write(buf);
337                 }
338                 else
339                 {
340                     return _delegate.write(_writeBuffer);
341                 }
342             }
343 
344             int s = SocketOperation.None;
345             do
346             {
347                 if(preWrite(buf))
348                 {
349                     if(_writeState == WriteStateFlush)
350                     {
351                         //
352                         // Invoke write() even though there's nothing to write.
353                         //
354                         Debug.Assert(!buf.b.hasRemaining());
355                         s = _delegate.write(buf);
356                     }
357 
358                     if(s == SocketOperation.None && _writeBuffer.b.hasRemaining())
359                     {
360                         s = _delegate.write(_writeBuffer);
361                     }
362                     else if(s == SocketOperation.None && _incoming && !buf.empty() && _writeState == WriteStatePayload)
363                     {
364                         s = _delegate.write(buf);
365                     }
366                 }
367             }
368             while(postWrite(buf, s));
369 
370             if(s != SocketOperation.None)
371             {
372                 return s;
373             }
374             if(_state == StateClosingResponsePending && !_closingInitiator)
375             {
376                 return SocketOperation.Read;
377             }
378             return SocketOperation.None;
379         }
380 
read(Buffer buf, ref bool hasMoreData)381         public int read(Buffer buf, ref bool hasMoreData)
382         {
383             if(_readPending)
384             {
385                 return SocketOperation.Read;
386             }
387 
388             if(_state < StateOpened)
389             {
390                 if(_state < StateConnected)
391                 {
392                     return _delegate.read(buf, ref hasMoreData);
393                 }
394                 else
395                 {
396                     if(_delegate.read(_readBuffer, ref hasMoreData) == SocketOperation.Write)
397                     {
398                         return SocketOperation.Write;
399                     }
400                     else
401                     {
402                         return SocketOperation.None;
403                     }
404                 }
405             }
406 
407             if(!buf.b.hasRemaining())
408             {
409                 hasMoreData |= _readBufferPos < _readBuffer.b.position();
410                 return SocketOperation.None;
411             }
412 
413             int s = SocketOperation.None;
414             do
415             {
416                 if(preRead(buf))
417                 {
418                     if(_readState == ReadStatePayload)
419                     {
420                         //
421                         // If the payload length is smaller than what remains to be read, we read
422                         // no more than the payload length. The remaining of the buffer will be
423                         // sent over in another frame.
424                         //
425                         int readSz = _readPayloadLength - (buf.b.position() - _readStart);
426                         if(buf.b.remaining() > readSz)
427                         {
428                             int size = buf.size();
429                             buf.resize(buf.b.position() + readSz, true);
430                             s = _delegate.read(buf, ref hasMoreData);
431                             buf.resize(size, true);
432                         }
433                         else
434                         {
435                             s = _delegate.read(buf, ref hasMoreData);
436                         }
437                     }
438                     else
439                     {
440                         s = _delegate.read(_readBuffer, ref hasMoreData);
441                     }
442 
443                     if(s == SocketOperation.Write)
444                     {
445                         postRead(buf);
446                         return s;
447                     }
448                 }
449             }
450             while(postRead(buf));
451 
452             if(!buf.b.hasRemaining())
453             {
454                 hasMoreData |= _readBufferPos < _readBuffer.b.position();
455                 s = SocketOperation.None;
456             }
457             else
458             {
459                 hasMoreData = false;
460                 s = SocketOperation.Read;
461             }
462 
463             if(((_state == StateClosingRequestPending && !_closingInitiator) ||
464                 (_state == StateClosingResponsePending && _closingInitiator) ||
465                 _state == StatePingPending ||
466                 _state == StatePongPending) &&
467                _writeState == WriteStateHeader)
468             {
469                 // We have things to write, ask to be notified when writes are ready.
470                 s |= SocketOperation.Write;
471             }
472 
473             return s;
474         }
475 
startRead(Buffer buf, AsyncCallback callback, object state)476         public bool startRead(Buffer buf, AsyncCallback callback, object state)
477         {
478             _readPending = true;
479             if(_state < StateOpened)
480             {
481                 _finishRead = true;
482                 if(_state < StateConnected)
483                 {
484                     return _delegate.startRead(buf, callback, state);
485                 }
486                 else
487                 {
488                     return _delegate.startRead(_readBuffer, callback, state);
489                 }
490             }
491 
492             if(preRead(buf))
493             {
494                 _finishRead = true;
495                 if(_readState == ReadStatePayload)
496                 {
497                     //
498                     // If the payload length is smaller than what remains to be read, we read
499                     // no more than the payload length. The remaining of the buffer will be
500                     // sent over in another frame.
501                     //
502                     int readSz = _readPayloadLength  - (buf.b.position() - _readStart);
503                     if(buf.b.remaining() > readSz)
504                     {
505                         int size = buf.size();
506                         buf.resize(buf.b.position() + readSz, true);
507                         bool completedSynchronously = _delegate.startRead(buf, callback, state);
508                         buf.resize(size, true);
509                         return completedSynchronously;
510                     }
511                     else
512                     {
513                         return _delegate.startRead(buf, callback, state);
514                     }
515                 }
516                 else
517                 {
518                     return _delegate.startRead(_readBuffer, callback, state);
519                 }
520             }
521             else
522             {
523                 return true;
524             }
525         }
526 
finishRead(Buffer buf)527         public void finishRead(Buffer buf)
528         {
529             Debug.Assert(_readPending);
530             _readPending = false;
531 
532             if(_state < StateOpened)
533             {
534                 Debug.Assert(_finishRead);
535                 _finishRead = false;
536                 if(_state < StateConnected)
537                 {
538                     _delegate.finishRead(buf);
539                 }
540                 else
541                 {
542                     _delegate.finishRead(_readBuffer);
543                 }
544                 return;
545             }
546 
547             if(!_finishRead)
548             {
549                 // Nothing to do.
550             }
551             else if(_readState == ReadStatePayload)
552             {
553                 Debug.Assert(_finishRead);
554                 _finishRead = false;
555                 _delegate.finishRead(buf);
556             }
557             else
558             {
559                 Debug.Assert(_finishRead);
560                 _finishRead = false;
561                 _delegate.finishRead(_readBuffer);
562             }
563 
564             if(_state == StateClosed)
565             {
566                 _readBuffer.clear();
567                 return;
568             }
569 
570             postRead(buf);
571         }
572 
startWrite(Buffer buf, AsyncCallback callback, object state, out bool completed)573         public bool startWrite(Buffer buf, AsyncCallback callback, object state,
574                                out bool completed)
575         {
576             _writePending = true;
577             if(_state < StateOpened)
578             {
579                 if(_state < StateConnected)
580                 {
581                     return _delegate.startWrite(buf, callback, state, out completed);
582                 }
583                 else
584                 {
585                     return _delegate.startWrite(_writeBuffer, callback, state, out completed);
586                 }
587             }
588 
589             if(preWrite(buf))
590             {
591                 if(_writeBuffer.b.hasRemaining())
592                 {
593                     return _delegate.startWrite(_writeBuffer, callback, state, out completed);
594                 }
595                 else
596                 {
597                     Debug.Assert(_incoming);
598                     return _delegate.startWrite(buf, callback, state, out completed);
599                 }
600             }
601             else
602             {
603                 completed = true;
604                 return false;
605             }
606         }
607 
finishWrite(Buffer buf)608         public void finishWrite(Buffer buf)
609         {
610             _writePending = false;
611 
612             if(_state < StateOpened)
613             {
614                 if(_state < StateConnected)
615                 {
616                     _delegate.finishWrite(buf);
617                 }
618                 else
619                 {
620                     _delegate.finishWrite(_writeBuffer);
621                 }
622                 return;
623             }
624 
625             if(_writeBuffer.b.hasRemaining())
626             {
627                 _delegate.finishWrite(_writeBuffer);
628             }
629             else if(!buf.empty() && buf.b.hasRemaining())
630             {
631                 Debug.Assert(_incoming);
632                 _delegate.finishWrite(buf);
633             }
634 
635             if(_state == StateClosed)
636             {
637                 _writeBuffer.clear();
638                 return;
639             }
640 
641             postWrite(buf, SocketOperation.None);
642         }
643 
protocol()644         public string protocol()
645         {
646             return _instance.protocol();
647         }
648 
getInfo()649         public Ice.ConnectionInfo getInfo()
650         {
651             Ice.WSConnectionInfo info = new Ice.WSConnectionInfo();
652             info.headers = _parser.getHeaders();
653             info.underlying = _delegate.getInfo();
654             return info;
655         }
656 
checkSendSize(Buffer buf)657         public void checkSendSize(Buffer buf)
658         {
659             _delegate.checkSendSize(buf);
660         }
661 
setBufferSize(int rcvSize, int sndSize)662         public void setBufferSize(int rcvSize, int sndSize)
663         {
664             _delegate.setBufferSize(rcvSize, sndSize);
665         }
666 
ToString()667         public override string ToString()
668         {
669             return _delegate.ToString();
670         }
671 
toDetailedString()672         public string toDetailedString()
673         {
674             return _delegate.toDetailedString();
675         }
676 
677         internal
WSTransceiver(ProtocolInstance instance, Transceiver del, string host, string resource)678         WSTransceiver(ProtocolInstance instance, Transceiver del, string host, string resource)
679         {
680             init(instance, del);
681             _host = host;
682             _resource = resource;
683             _incoming = false;
684 
685             //
686             // Use a 16KB write buffer size. We use 16KB for the write
687             // buffer size because all the data needs to be copied to the
688             // write buffer for the purpose of masking. A 16KB buffer
689             // appears to be a good compromise to reduce the number of
690             // socket write calls and not consume too much memory.
691             //
692             _writeBufferSize = 16 * 1024;
693 
694             //
695             // Write and read buffer size must be large enough to hold the frame header!
696             //
697             Debug.Assert(_writeBufferSize > 256);
698             Debug.Assert(_readBufferSize > 256);
699         }
700 
WSTransceiver(ProtocolInstance instance, Transceiver del)701         internal WSTransceiver(ProtocolInstance instance, Transceiver del)
702         {
703             init(instance, del);
704             _host = "";
705             _resource = "";
706             _incoming = true;
707 
708             //
709             // Write and read buffer size must be large enough to hold the frame header!
710             //
711             Debug.Assert(_writeBufferSize > 256);
712             Debug.Assert(_readBufferSize > 256);
713         }
714 
init(ProtocolInstance instance, Transceiver del)715         private void init(ProtocolInstance instance, Transceiver del)
716         {
717             _instance = instance;
718             _delegate = del;
719             _state = StateInitializeDelegate;
720             _parser = new HttpParser();
721             _readState = ReadStateOpcode;
722             _readBuffer = new Buffer(ByteBuffer.ByteOrder.BIG_ENDIAN); // Network byte order
723             _readBufferSize = 1024;
724             _readLastFrame = true;
725             _readOpCode = 0;
726             _readHeaderLength = 0;
727             _readPayloadLength = 0;
728             _writeState = WriteStateHeader;
729             _writeBuffer = new Buffer(ByteBuffer.ByteOrder.BIG_ENDIAN); // Network byte order
730             _writeBufferSize = 1024;
731             _readPending = false;
732             _finishRead = false;
733             _writePending = false;
734             _readMask = new byte[4];
735             _writeMask = new byte[4];
736             _key = "";
737             _pingPayload = new byte[0];
738             _rand = new Random();
739         }
740 
handleRequest(Buffer responseBuffer)741         private void handleRequest(Buffer responseBuffer)
742         {
743             //
744             // HTTP/1.1
745             //
746             if(_parser.versionMajor() != 1 || _parser.versionMinor() != 1)
747             {
748                 throw new WebSocketException("unsupported HTTP version");
749             }
750 
751             //
752             // "An |Upgrade| header field containing the value 'websocket',
753             //  treated as an ASCII case-insensitive value."
754             //
755             string val = _parser.getHeader("Upgrade", true);
756             if(val == null)
757             {
758                 throw new WebSocketException("missing value for Upgrade field");
759             }
760             else if(!val.Equals("websocket"))
761             {
762                 throw new WebSocketException("invalid value `" + val + "' for Upgrade field");
763             }
764 
765             //
766             // "A |Connection| header field that includes the token 'Upgrade',
767             //  treated as an ASCII case-insensitive value.
768             //
769             val = _parser.getHeader("Connection", true);
770             if(val == null)
771             {
772                 throw new WebSocketException("missing value for Connection field");
773             }
774             else if(val.IndexOf("upgrade") == -1)
775             {
776                 throw new WebSocketException("invalid value `" + val + "' for Connection field");
777             }
778 
779             //
780             // "A |Sec-WebSocket-Version| header field, with a value of 13."
781             //
782             val = _parser.getHeader("Sec-WebSocket-Version", false);
783             if(val == null)
784             {
785                 throw new WebSocketException("missing value for WebSocket version");
786             }
787             else if(!val.Equals("13"))
788             {
789                 throw new WebSocketException("unsupported WebSocket version `" + val + "'");
790             }
791 
792             //
793             // "Optionally, a |Sec-WebSocket-Protocol| header field, with a list
794             //  of values indicating which protocols the client would like to
795             //  speak, ordered by preference."
796             //
797             bool addProtocol = false;
798             val = _parser.getHeader("Sec-WebSocket-Protocol", true);
799             if(val != null)
800             {
801                 string[] protocols = IceUtilInternal.StringUtil.splitString(val, ",");
802                 if(protocols == null)
803                 {
804                     throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol");
805                 }
806                 foreach(string p in protocols)
807                 {
808                     if(!p.Trim().Equals(_iceProtocol))
809                     {
810                         throw new WebSocketException("unknown value `" + p + "' for WebSocket protocol");
811                     }
812                     addProtocol = true;
813                 }
814             }
815 
816             //
817             // "A |Sec-WebSocket-Key| header field with a base64-encoded
818             //  value that, when decoded, is 16 bytes in length."
819             //
820             string key = _parser.getHeader("Sec-WebSocket-Key", false);
821             if(key == null)
822             {
823                 throw new WebSocketException("missing value for WebSocket key");
824             }
825 
826             byte[] decodedKey = Convert.FromBase64String(key);
827             if(decodedKey.Length != 16)
828             {
829                 throw new WebSocketException("invalid value `" + key + "' for WebSocket key");
830             }
831 
832             //
833             // Retain the target resource.
834             //
835             _resource = _parser.uri();
836 
837             //
838             // Compose the response.
839             //
840             StringBuilder @out = new StringBuilder();
841             @out.Append("HTTP/1.1 101 Switching Protocols\r\n");
842             @out.Append("Upgrade: websocket\r\n");
843             @out.Append("Connection: Upgrade\r\n");
844             if(addProtocol)
845             {
846                 @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n");
847             }
848 
849             //
850             // The response includes:
851             //
852             // "A |Sec-WebSocket-Accept| header field.  The value of this
853             //  header field is constructed by concatenating /key/, defined
854             //  above in step 4 in Section 4.2.2, with the string "258EAFA5-
855             //  E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
856             //  concatenated value to obtain a 20-byte value and base64-
857             //  encoding (see Section 4 of [RFC4648]) this 20-byte hash.
858             //
859             @out.Append("Sec-WebSocket-Accept: ");
860             string input = key + _wsUUID;
861             byte[] hash = SHA1.Create().ComputeHash(_utf8.GetBytes(input));
862             @out.Append(Convert.ToBase64String(hash) + "\r\n" + "\r\n"); // EOM
863 
864             byte[] bytes = _utf8.GetBytes(@out.ToString());
865             Debug.Assert(bytes.Length == @out.Length);
866             responseBuffer.resize(bytes.Length, false);
867             responseBuffer.b.position(0);
868             responseBuffer.b.put(bytes);
869             responseBuffer.b.flip();
870         }
871 
handleResponse()872         private void handleResponse()
873         {
874             string val;
875 
876             //
877             // HTTP/1.1
878             //
879             if(_parser.versionMajor() != 1 || _parser.versionMinor() != 1)
880             {
881                 throw new WebSocketException("unsupported HTTP version");
882             }
883 
884             //
885             // "If the status code received from the server is not 101, the
886             //  client handles the response per HTTP [RFC2616] procedures.  In
887             //  particular, the client might perform authentication if it
888             //  receives a 401 status code; the server might redirect the client
889             //  using a 3xx status code (but clients are not required to follow
890             //  them), etc."
891             //
892             if(_parser.status() != 101)
893             {
894                 StringBuilder @out = new StringBuilder("unexpected status value " + _parser.status());
895                 if(_parser.reason().Length > 0)
896                 {
897                     @out.Append(":\n" + _parser.reason());
898                 }
899                 throw new WebSocketException(@out.ToString());
900             }
901 
902             //
903             // "If the response lacks an |Upgrade| header field or the |Upgrade|
904             //  header field contains a value that is not an ASCII case-
905             //  insensitive match for the value "websocket", the client MUST
906             //  _Fail the WebSocket Connection_."
907             //
908             val = _parser.getHeader("Upgrade", true);
909             if(val == null)
910             {
911                 throw new WebSocketException("missing value for Upgrade field");
912             }
913             else if(!val.Equals("websocket"))
914             {
915                 throw new WebSocketException("invalid value `" + val + "' for Upgrade field");
916             }
917 
918             //
919             // "If the response lacks a |Connection| header field or the
920             //  |Connection| header field doesn't contain a token that is an
921             //  ASCII case-insensitive match for the value "Upgrade", the client
922             //  MUST _Fail the WebSocket Connection_."
923             //
924             val = _parser.getHeader("Connection", true);
925             if(val == null)
926             {
927                 throw new WebSocketException("missing value for Connection field");
928             }
929             else if(val.IndexOf("upgrade") == -1)
930             {
931                 throw new WebSocketException("invalid value `" + val + "' for Connection field");
932             }
933 
934             //
935             // "If the response includes a |Sec-WebSocket-Protocol| header field
936             //  and this header field indicates the use of a subprotocol that was
937             //  not present in the client's handshake (the server has indicated a
938             //  subprotocol not requested by the client), the client MUST _Fail
939             //  the WebSocket Connection_."
940             //
941             val = _parser.getHeader("Sec-WebSocket-Protocol", true);
942             if(val != null && !val.Equals(_iceProtocol))
943             {
944                 throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol");
945             }
946 
947             //
948             // "If the response lacks a |Sec-WebSocket-Accept| header field or
949             //  the |Sec-WebSocket-Accept| contains a value other than the
950             //  base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket-
951             //  Key| (as a string, not base64-decoded) with the string "258EAFA5-
952             //  E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and
953             //  trailing whitespace, the client MUST _Fail the WebSocket
954             //  Connection_."
955             //
956             val = _parser.getHeader("Sec-WebSocket-Accept", false);
957             if(val == null)
958             {
959                 throw new WebSocketException("missing value for Sec-WebSocket-Accept");
960             }
961 
962             string input = _key + _wsUUID;
963             byte[] hash = SHA1.Create().ComputeHash(_utf8.GetBytes(input));
964             if(!val.Equals(Convert.ToBase64String(hash)))
965             {
966                 throw new WebSocketException("invalid value `" + val + "' for Sec-WebSocket-Accept");
967             }
968         }
969 
preRead(Buffer buf)970         private bool preRead(Buffer buf)
971         {
972             while(true)
973             {
974                 if(_readState == ReadStateOpcode)
975                 {
976                     //
977                     // Is there enough data available to read the opcode?
978                     //
979                     if(!readBuffered(2))
980                     {
981                         return true;
982                     }
983 
984                     //
985                     // Most-significant bit indicates whether this is the
986                     // last frame. Least-significant four bits hold the
987                     // opcode.
988                     //
989                     int ch = _readBuffer.b.get(_readBufferPos++);
990                     _readOpCode = ch & 0xf;
991 
992                     //
993                     // Remember if last frame if we're going to read a data or
994                     // continuation frame, this is only for protocol
995                     // correctness checking purpose.
996                     //
997                     if(_readOpCode == OP_DATA)
998                     {
999                         if(!_readLastFrame)
1000                         {
1001                             throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame");
1002                         }
1003                         _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
1004                     }
1005                     else if(_readOpCode == OP_CONT)
1006                     {
1007                         if(_readLastFrame)
1008                         {
1009                             throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set");
1010                         }
1011                         _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
1012                     }
1013 
1014                     ch = _readBuffer.b.get(_readBufferPos++);
1015 
1016                     //
1017                     // Check the MASK bit. Messages sent by a client must be masked;
1018                     // messages sent by a server must not be masked.
1019                     //
1020                     bool masked = (ch & FLAG_MASKED) == FLAG_MASKED;
1021                     if(masked != _incoming)
1022                     {
1023                         throw new Ice.ProtocolException("invalid masking");
1024                     }
1025 
1026                     //
1027                     // Extract the payload length, which can have the following values:
1028                     //
1029                     // 0-125: The payload length
1030                     // 126:   The subsequent two bytes contain the payload length
1031                     // 127:   The subsequent eight bytes contain the payload length
1032                     //
1033                     _readPayloadLength = (ch & 0x7f);
1034                     if(_readPayloadLength < 126)
1035                     {
1036                         _readHeaderLength = 0;
1037                     }
1038                     else if(_readPayloadLength == 126)
1039                     {
1040                         _readHeaderLength = 2; // Need to read a 16-bit payload length.
1041                     }
1042                     else
1043                     {
1044                         _readHeaderLength = 8; // Need to read a 64-bit payload length.
1045                     }
1046                     if(masked)
1047                     {
1048                         _readHeaderLength += 4; // Need to read a 32-bit mask.
1049                     }
1050 
1051                     _readState = ReadStateHeader;
1052                 }
1053 
1054                 if(_readState == ReadStateHeader)
1055                 {
1056                     //
1057                     // Is there enough data available to read the header?
1058                     //
1059                     if(_readHeaderLength > 0 && !readBuffered(_readHeaderLength))
1060                     {
1061                         return true;
1062                     }
1063 
1064                     if(_readPayloadLength == 126)
1065                     {
1066                         _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order.
1067                         if(_readPayloadLength < 0)
1068                         {
1069                             _readPayloadLength += 65536;
1070                         }
1071                         _readBufferPos += 2;
1072                     }
1073                     else if(_readPayloadLength == 127)
1074                     {
1075                         long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order.
1076                         _readBufferPos += 8;
1077                         if(l < 0 || l > Int32.MaxValue)
1078                         {
1079                             throw new Ice.ProtocolException("invalid WebSocket payload length: " + l);
1080                         }
1081                         _readPayloadLength = (int)l;
1082                     }
1083 
1084                     //
1085                     // Read the mask if this is an incoming connection.
1086                     //
1087                     if(_incoming)
1088                     {
1089                         //
1090                         // We must have needed to read the mask.
1091                         //
1092                         Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4);
1093                         for(int i = 0; i < 4; ++i)
1094                         {
1095                             _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask.
1096                         }
1097                     }
1098 
1099                     switch(_readOpCode)
1100                     {
1101                     case OP_TEXT: // Text frame
1102                     {
1103                         throw new Ice.ProtocolException("text frames not supported");
1104                     }
1105                     case OP_DATA: // Data frame
1106                     case OP_CONT: // Continuation frame
1107                     {
1108                         if(_instance.traceLevel() >= 2)
1109                         {
1110                             _instance.logger().trace(_instance.traceCategory(), "received " + protocol() +
1111                                                      (_readOpCode == OP_DATA ? " data" : " continuation") +
1112                                                      " frame with payload length of " + _readPayloadLength +
1113                                                      " bytes\n" + ToString());
1114                         }
1115 
1116                         if(_readPayloadLength <= 0)
1117                         {
1118                             throw new Ice.ProtocolException("payload length is 0");
1119                         }
1120                         _readState = ReadStatePayload;
1121                         Debug.Assert(buf.b.hasRemaining());
1122                         _readFrameStart = buf.b.position();
1123                         break;
1124                     }
1125                     case OP_CLOSE: // Connection close
1126                     {
1127                         if(_instance.traceLevel() >= 2)
1128                         {
1129                             _instance.logger().trace(_instance.traceCategory(),
1130                                 "received " + protocol() + " connection close frame\n" + ToString());
1131                         }
1132 
1133                         _readState = ReadStateControlFrame;
1134                         int s = _nextState == StateOpened ? _state : _nextState;
1135                         if(s == StateClosingRequestPending)
1136                         {
1137                             //
1138                             // If we receive a close frame while we were actually
1139                             // waiting to send one, change the role and send a
1140                             // close frame response.
1141                             //
1142                             if(!_closingInitiator)
1143                             {
1144                                 _closingInitiator = true;
1145                             }
1146                             if(_state == StateClosingRequestPending)
1147                             {
1148                                 _state = StateClosingResponsePending;
1149                             }
1150                             else
1151                             {
1152                                 _nextState = StateClosingResponsePending;
1153                             }
1154                             return false; // No longer interested in reading
1155                         }
1156                         else
1157                         {
1158                             throw new Ice.ConnectionLostException();
1159                         }
1160                     }
1161                     case OP_PING:
1162                     {
1163                         if(_instance.traceLevel() >= 2)
1164                         {
1165                             _instance.logger().trace(_instance.traceCategory(),
1166                                 "received " + protocol() + " connection ping frame\n" + ToString());
1167                         }
1168                         _readState = ReadStateControlFrame;
1169                         break;
1170                     }
1171                     case OP_PONG: // Pong
1172                     {
1173                         if(_instance.traceLevel() >= 2)
1174                         {
1175                             _instance.logger().trace(_instance.traceCategory(),
1176                                 "received " + protocol() + " connection pong frame\n" + ToString());
1177                         }
1178                         _readState = ReadStateControlFrame;
1179                         break;
1180                     }
1181                     default:
1182                     {
1183                         throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode);
1184                     }
1185                     }
1186                 }
1187 
1188                 if(_readState == ReadStateControlFrame)
1189                 {
1190                     if(_readPayloadLength > 0 && !readBuffered(_readPayloadLength))
1191                     {
1192                         return true;
1193                     }
1194 
1195                     if(_readPayloadLength > 0 && _readOpCode == OP_PING)
1196                     {
1197                         _pingPayload = new byte[_readPayloadLength];
1198                         System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, _pingPayload, 0,
1199                                                 _readPayloadLength);
1200                     }
1201 
1202                     _readBufferPos += _readPayloadLength;
1203                     _readPayloadLength = 0;
1204 
1205                     if(_readOpCode == OP_PING)
1206                     {
1207                         if(_state == StateOpened)
1208                         {
1209                             _state = StatePongPending; // Send pong frame now
1210                         }
1211                         else if(_nextState < StatePongPending)
1212                         {
1213                             _nextState = StatePongPending; // Send pong frame next
1214                         }
1215                     }
1216 
1217                     //
1218                     // We've read the payload of the PING/PONG frame, we're ready
1219                     // to read a new frame.
1220                     //
1221                     _readState = ReadStateOpcode;
1222                 }
1223 
1224                 if(_readState == ReadStatePayload)
1225                 {
1226                     //
1227                     // This must be assigned before the check for the buffer. If the buffer is empty
1228                     // or already read, postRead will return false.
1229                     //
1230                     _readStart = buf.b.position();
1231 
1232                     if(buf.empty() || !buf.b.hasRemaining())
1233                     {
1234                         return false;
1235                     }
1236 
1237                     int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining());
1238                     if(n > _readPayloadLength)
1239                     {
1240                         n = _readPayloadLength;
1241                     }
1242                     if(n > 0)
1243                     {
1244                         System.Buffer.BlockCopy(_readBuffer.b.rawBytes(), _readBufferPos, buf.b.rawBytes(),
1245                                                 buf.b.position(), n);
1246                         buf.b.position(buf.b.position() + n);
1247                         _readBufferPos += n;
1248                     }
1249 
1250                     //
1251                     // Continue reading if we didn't read the full message, otherwise give back
1252                     // the control to the connection
1253                     //
1254                     return buf.b.hasRemaining() && n < _readPayloadLength;
1255                 }
1256             }
1257         }
1258 
postRead(Buffer buf)1259         private bool postRead(Buffer buf)
1260         {
1261             if(_readState != ReadStatePayload)
1262             {
1263                 return _readStart < _readBuffer.b.position(); // Returns true if data was read.
1264             }
1265 
1266             if(_readStart == buf.b.position())
1267             {
1268                 return false; // Nothing was read or nothing to read.
1269             }
1270             Debug.Assert(_readStart < buf.b.position());
1271 
1272             if(_incoming)
1273             {
1274                 //
1275                 // Unmask the data we just read.
1276                 //
1277                 int pos = buf.b.position();
1278                 byte[] arr = buf.b.rawBytes();
1279                 for(int n = _readStart; n < pos; ++n)
1280                 {
1281                     arr[n] = (byte)(arr[n] ^ _readMask[(n - _readFrameStart) % 4]);
1282                 }
1283             }
1284 
1285             _readPayloadLength -= buf.b.position() - _readStart;
1286             _readStart = buf.b.position();
1287             if(_readPayloadLength == 0)
1288             {
1289                 //
1290                 // We've read the complete payload, we're ready to read a new frame.
1291                 //
1292                 _readState = ReadStateOpcode;
1293             }
1294             return buf.b.hasRemaining();
1295         }
1296 
preWrite(Buffer buf)1297         private bool preWrite(Buffer buf)
1298         {
1299             if(_writeState == WriteStateHeader)
1300             {
1301                 if(_state == StateOpened)
1302                 {
1303                     if(buf.empty() || !buf.b.hasRemaining())
1304                     {
1305                         return false;
1306                     }
1307 
1308                     Debug.Assert(buf.b.position() == 0);
1309                     prepareWriteHeader((byte)OP_DATA, buf.size());
1310 
1311                     _writeState = WriteStatePayload;
1312                 }
1313                 else if(_state == StatePingPending)
1314                 {
1315                     prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload
1316 
1317                     _writeState = WriteStateControlFrame;
1318                     _writeBuffer.b.flip();
1319                 }
1320                 else if(_state == StatePongPending)
1321                 {
1322                     prepareWriteHeader((byte)OP_PONG, _pingPayload.Length);
1323                     if(_pingPayload.Length > _writeBuffer.b.remaining())
1324                     {
1325                         int pos = _writeBuffer.b.position();
1326                         _writeBuffer.resize(pos + _pingPayload.Length, false);
1327                         _writeBuffer.b.position(pos);
1328                     }
1329                     _writeBuffer.b.put(_pingPayload);
1330                     _pingPayload = new byte[0];
1331 
1332                     _writeState = WriteStateControlFrame;
1333                     _writeBuffer.b.flip();
1334                 }
1335                 else if((_state == StateClosingRequestPending && !_closingInitiator) ||
1336                         (_state == StateClosingResponsePending && _closingInitiator))
1337                 {
1338                     prepareWriteHeader((byte)OP_CLOSE, 2);
1339 
1340                     // Write closing reason
1341                     _writeBuffer.b.putShort((short)_closingReason);
1342 
1343                     if(!_incoming)
1344                     {
1345                         byte b;
1346                         int pos = _writeBuffer.b.position() - 2;
1347                         b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]);
1348                         _writeBuffer.b.put(pos, b);
1349                         pos++;
1350                         b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]);
1351                         _writeBuffer.b.put(pos, b);
1352                     }
1353 
1354                     _writeState = WriteStateControlFrame;
1355                     _writeBuffer.b.flip();
1356                 }
1357                 else
1358                 {
1359                     Debug.Assert(_state != StateClosed);
1360                     return false; // Nothing to write in this state
1361                 }
1362 
1363                 _writePayloadLength = 0;
1364             }
1365 
1366             if(_writeState == WriteStatePayload)
1367             {
1368                 //
1369                 // For an outgoing connection, each message must be masked with a random
1370                 // 32-bit value, so we copy the entire message into the internal buffer
1371                 // for writing. For incoming connections, we just copy the start of the
1372                 // message in the internal buffer after the hedaer. If the message is
1373                 // larger, the reminder is sent directly from the message buffer to avoid
1374                 // copying.
1375                 //
1376                 if(!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining()))
1377                 {
1378                     if(!_writeBuffer.b.hasRemaining())
1379                     {
1380                         _writeBuffer.b.position(0);
1381                     }
1382 
1383                     int n = buf.b.position();
1384                     int sz = buf.size();
1385                     int pos = _writeBuffer.b.position();
1386                     int count = Math.Min(sz - n, _writeBuffer.b.remaining());
1387                     byte[] src = buf.b.rawBytes();
1388                     byte[] dest = _writeBuffer.b.rawBytes();
1389                     for(int i = 0; i < count; ++i, ++n, ++pos)
1390                     {
1391                         dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]);
1392                     }
1393                     _writeBuffer.b.position(pos);
1394                     _writePayloadLength = n;
1395 
1396                     _writeBuffer.b.flip();
1397                 }
1398                 else if(_writePayloadLength == 0)
1399                 {
1400                     Debug.Assert(_incoming);
1401                     if(_writeBuffer.b.hasRemaining())
1402                     {
1403                         Debug.Assert(buf.b.position() == 0);
1404                         int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining());
1405                         int pos = _writeBuffer.b.position();
1406                         System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n);
1407                         _writeBuffer.b.position(pos + n);
1408                         _writePayloadLength = n;
1409                    }
1410                     _writeBuffer.b.flip();
1411                 }
1412                 return true;
1413             }
1414             else if(_writeState == WriteStateControlFrame)
1415             {
1416                 return _writeBuffer.b.hasRemaining();
1417             }
1418             else
1419             {
1420                 Debug.Assert(_writeState == WriteStateFlush);
1421                 return true;
1422             }
1423         }
1424 
postWrite(Buffer buf, int status)1425         private bool postWrite(Buffer buf, int status)
1426         {
1427             if(_state > StateOpened && _writeState == WriteStateControlFrame)
1428             {
1429                 if(!_writeBuffer.b.hasRemaining())
1430                 {
1431                     if(_state == StatePingPending)
1432                     {
1433                         if(_instance.traceLevel() >= 2)
1434                         {
1435                             _instance.logger().trace(_instance.traceCategory(),
1436                                 "sent " + protocol() + " connection ping frame\n" + ToString());
1437                         }
1438                     }
1439                     else if(_state == StatePongPending)
1440                     {
1441                         if(_instance.traceLevel() >= 2)
1442                         {
1443                             _instance.logger().trace(_instance.traceCategory(),
1444                                 "sent " + protocol() + " connection pong frame\n" + ToString());
1445                         }
1446                     }
1447                     else if((_state == StateClosingRequestPending && !_closingInitiator) ||
1448                             (_state == StateClosingResponsePending && _closingInitiator))
1449                     {
1450                         if(_instance.traceLevel() >= 2)
1451                         {
1452                             _instance.logger().trace(_instance.traceCategory(),
1453                                 "sent " + protocol() + " connection close frame\n" + ToString());
1454                         }
1455 
1456                         if(_state == StateClosingRequestPending && !_closingInitiator)
1457                         {
1458                             _writeState = WriteStateHeader;
1459                             _state = StateClosingResponsePending;
1460                             return false;
1461                         }
1462                         else
1463                         {
1464                             throw new Ice.ConnectionLostException();
1465                         }
1466                     }
1467                     else if(_state == StateClosed)
1468                     {
1469                         return false;
1470                     }
1471 
1472                     _state = _nextState;
1473                     _nextState = StateOpened;
1474                     _writeState = WriteStateHeader;
1475                 }
1476                 else
1477                 {
1478                     return status == SocketOperation.None;
1479                 }
1480             }
1481 
1482             if((!_incoming || buf.b.position() == 0) && _writePayloadLength > 0)
1483             {
1484                 if(!_writeBuffer.b.hasRemaining())
1485                 {
1486                     buf.b.position(_writePayloadLength);
1487                 }
1488             }
1489 
1490             if(status == SocketOperation.Write && !buf.b.hasRemaining() && !_writeBuffer.b.hasRemaining())
1491             {
1492                 //
1493                 // Our buffers are empty but the delegate needs another call to write().
1494                 //
1495                 _writeState = WriteStateFlush;
1496                 return false;
1497             }
1498             else if(!buf.b.hasRemaining())
1499             {
1500                 _writeState = WriteStateHeader;
1501                 if(_state == StatePingPending ||
1502                    _state == StatePongPending ||
1503                    (_state == StateClosingRequestPending && !_closingInitiator) ||
1504                    (_state == StateClosingResponsePending && _closingInitiator))
1505                 {
1506                     return true;
1507                 }
1508             }
1509             else if(_state == StateOpened)
1510             {
1511                 return status == SocketOperation.None;
1512             }
1513 
1514             return false;
1515         }
1516 
readBuffered(int sz)1517         private bool readBuffered(int sz)
1518         {
1519             if(_readBufferPos == _readBuffer.b.position())
1520             {
1521                 _readBuffer.resize(_readBufferSize, true);
1522                 _readBufferPos = 0;
1523                 _readBuffer.b.position(0);
1524             }
1525             else
1526             {
1527                 int available = _readBuffer.b.position() - _readBufferPos;
1528                 if(available < sz)
1529                 {
1530                     if(_readBufferPos > 0)
1531                     {
1532                         _readBuffer.b.limit(_readBuffer.b.position());
1533                         _readBuffer.b.position(_readBufferPos);
1534                         _readBuffer.b.compact();
1535                         Debug.Assert(_readBuffer.b.position() == available);
1536                     }
1537                     _readBuffer.resize(Math.Max(_readBufferSize, sz), true);
1538                     _readBufferPos = 0;
1539                     _readBuffer.b.position(available);
1540                 }
1541             }
1542 
1543             _readStart = _readBuffer.b.position();
1544             if(_readBufferPos + sz > _readBuffer.b.position())
1545             {
1546                 return false; // Not enough read.
1547             }
1548             Debug.Assert(_readBuffer.b.position() > _readBufferPos);
1549             return true;
1550         }
1551 
prepareWriteHeader(byte opCode, int payloadLength)1552         private void prepareWriteHeader(byte opCode, int payloadLength)
1553         {
1554             //
1555             // We need to prepare the frame header.
1556             //
1557             _writeBuffer.resize(_writeBufferSize, false);
1558             _writeBuffer.b.limit(_writeBufferSize);
1559             _writeBuffer.b.position(0);
1560 
1561             //
1562             // Set the opcode - this is the one and only data frame.
1563             //
1564             _writeBuffer.b.put((byte)(opCode | FLAG_FINAL));
1565 
1566             //
1567             // Set the payload length.
1568             //
1569             if(payloadLength <= 125)
1570             {
1571                 _writeBuffer.b.put((byte)payloadLength);
1572             }
1573             else if(payloadLength > 125 && payloadLength <= 65535)
1574             {
1575                 //
1576                 // Use an extra 16 bits to encode the payload length.
1577                 //
1578                 _writeBuffer.b.put(126);
1579                 _writeBuffer.b.putShort((short)payloadLength);
1580             }
1581             else if(payloadLength > 65535)
1582             {
1583                 //
1584                 // Use an extra 64 bits to encode the payload length.
1585                 //
1586                 _writeBuffer.b.put(127);
1587                 _writeBuffer.b.putLong(payloadLength);
1588             }
1589 
1590             if(!_incoming)
1591             {
1592                 //
1593                 // Add a random 32-bit mask to every outgoing frame, copy the payload data,
1594                 // and apply the mask.
1595                 //
1596                 _writeBuffer.b.put(1, (byte)(_writeBuffer.b.get(1) | FLAG_MASKED));
1597                 _rand.NextBytes(_writeMask);
1598                 _writeBuffer.b.put(_writeMask);
1599             }
1600         }
1601 
1602         private ProtocolInstance _instance;
1603         private Transceiver _delegate;
1604         private string _host;
1605         private string _resource;
1606         private bool _incoming;
1607 
1608         private const int StateInitializeDelegate = 0;
1609         private const int StateConnected = 1;
1610         private const int StateUpgradeRequestPending = 2;
1611         private const int StateUpgradeResponsePending = 3;
1612         private const int StateOpened = 4;
1613         private const int StatePingPending = 5;
1614         private const int StatePongPending = 6;
1615         private const int StateClosingRequestPending = 7;
1616         private const int StateClosingResponsePending = 8;
1617         private const int StateClosed = 9;
1618 
1619         private int _state;
1620         private int _nextState;
1621 
1622         private HttpParser _parser;
1623         private string _key;
1624 
1625         private const int ReadStateOpcode = 0;
1626         private const int ReadStateHeader = 1;
1627         private const int ReadStateControlFrame = 2;
1628         private const int ReadStatePayload = 3;
1629 
1630         private int _readState;
1631         private Buffer _readBuffer;
1632         private int _readBufferPos;
1633         private int _readBufferSize;
1634 
1635         private bool _readLastFrame;
1636         private int _readOpCode;
1637         private int _readHeaderLength;
1638         private int _readPayloadLength;
1639         private int _readStart;
1640         private int _readFrameStart;
1641         private byte[] _readMask;
1642 
1643         private const int WriteStateHeader = 0;
1644         private const int WriteStatePayload = 1;
1645         private const int WriteStateControlFrame = 2;
1646         private const int WriteStateFlush = 3;
1647 
1648         private int _writeState;
1649         private Buffer _writeBuffer;
1650         private int _writeBufferSize;
1651         private byte[] _writeMask;
1652         private int _writePayloadLength;
1653 
1654         private bool _closingInitiator;
1655         private int _closingReason;
1656 
1657         private bool _readPending;
1658         private bool _finishRead;
1659         private bool _writePending;
1660 
1661         private byte[] _pingPayload;
1662 
1663         private Random _rand;
1664 
1665         //
1666         // WebSocket opcodes
1667         //
1668         private const int OP_CONT     = 0x0;    // Continuation frame
1669         private const int OP_TEXT     = 0x1;    // Text frame
1670         private const int OP_DATA     = 0x2;    // Data frame
1671         private const int OP_RES_0x3  = 0x3;    // Reserved
1672         private const int OP_RES_0x4  = 0x4;    // Reserved
1673         private const int OP_RES_0x5  = 0x5;    // Reserved
1674         private const int OP_RES_0x6  = 0x6;    // Reserved
1675         private const int OP_RES_0x7  = 0x7;    // Reserved
1676         private const int OP_CLOSE    = 0x8;    // Connection close
1677         private const int OP_PING     = 0x9;    // Ping
1678         private const int OP_PONG     = 0xA;    // Pong
1679         private const int OP_RES_0xB  = 0xB;    // Reserved
1680         private const int OP_RES_0xC  = 0xC;    // Reserved
1681         private const int OP_RES_0xD  = 0xD;    // Reserved
1682         private const int OP_RES_0xE  = 0xE;    // Reserved
1683         private const int OP_RES_0xF  = 0xF;    // Reserved
1684         private const int FLAG_FINAL  = 0x80;   // Last frame
1685         private const int FLAG_MASKED = 0x80;   // Payload is masked
1686 
1687         private const int CLOSURE_NORMAL         = 1000;
1688         private const int CLOSURE_SHUTDOWN       = 1001;
1689         private const int CLOSURE_PROTOCOL_ERROR = 1002;
1690         private const int CLOSURE_TOO_BIG        = 1009;
1691 
1692         private const string _iceProtocol = "ice.zeroc.com";
1693         private const string _wsUUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
1694 
1695         private static UTF8Encoding _utf8 = new UTF8Encoding(false, true);
1696     }
1697 }
1698