< Summary

Information
Class: Ice.Internal.WSTransceiver
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/WSTransceiver.cs
Tag: 71_18251537082
Line coverage
79%
Covered lines: 515
Uncovered lines: 129
Coverable lines: 644
Total lines: 1688
Line coverage: 79.9%
Branch coverage
79%
Covered branches: 380
Total branches: 480
Branch coverage: 79.1%
Method coverage
90%
Covered methods: 27
Total methods: 30
Method coverage: 90%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
fd()100%210%
initialize(...)87.1%74.696285.11%
closing(...)83.33%26.672483.33%
close()100%44100%
bind()100%210%
destroy()100%11100%
write(...)93.33%30.93090%
read(...)88.89%49.13678.38%
startRead(...)90%11.821073.68%
finishRead(...)90%10.141088.89%
startWrite(...)87.5%8.38881.82%
finishWrite(...)91.67%12.341286.67%
protocol()100%11100%
getInfo(...)100%11100%
checkSendSize(...)100%11100%
setBufferSize(...)100%11100%
ToString()100%11100%
toDetailedString()100%210%
.ctor(...)100%11100%
.ctor(...)100%11100%
init(...)100%11100%
handleRequest(...)60%35.583081.63%
handleResponse()50%64.812458.62%
preRead(...)64%425.5310068.07%
postRead(...)100%1010100%
preWrite(...)88.1%58.274279.03%
postWrite(...)78.33%193.296066.67%
readBuffered(...)100%88100%
prepareWriteHeader(...)100%1010100%
.cctor()100%11100%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/Internal/WSTransceiver.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4using System.Net.Sockets;
 5using System.Security.Cryptography;
 6using System.Text;
 7
 8namespace Ice.Internal;
 9
 10internal sealed class WSTransceiver : Transceiver
 11{
 012    public Socket fd() => _delegate.fd();
 13
 14    public int initialize(Buffer readBuffer, Buffer writeBuffer, ref bool hasMoreData)
 15    {
 16        //
 17        // Delegate logs exceptions that occur during initialize(), so there's no need to trap them here.
 18        //
 119        if (_state == StateInitializeDelegate)
 20        {
 121            int op = _delegate.initialize(readBuffer, writeBuffer, ref hasMoreData);
 122            if (op != 0)
 23            {
 124                return op;
 25            }
 126            _state = StateConnected;
 27        }
 28
 29        try
 30        {
 131            if (_state == StateConnected)
 32            {
 33                //
 34                // We don't know how much we'll need to read.
 35                //
 136                _readBuffer.resize(1024, true);
 137                _readBuffer.b.position(0);
 138                _readBufferPos = 0;
 39
 40                //
 41                // The server waits for the client's upgrade request, the
 42                // client sends the upgrade request.
 43                //
 144                _state = StateUpgradeRequestPending;
 145                if (!_incoming)
 46                {
 47                    //
 48                    // Compose the upgrade request.
 49                    //
 150                    var @out = new StringBuilder();
 151                    @out.Append("GET " + _resource + " HTTP/1.1\r\n");
 152                    @out.Append("Host: " + _host + "\r\n");
 153                    @out.Append("Upgrade: websocket\r\n");
 154                    @out.Append("Connection: Upgrade\r\n");
 155                    @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n");
 156                    @out.Append("Sec-WebSocket-Version: 13\r\n");
 157                    @out.Append("Sec-WebSocket-Key: ");
 58
 59                    //
 60                    // The value for Sec-WebSocket-Key is a 16-byte random number,
 61                    // encoded with Base64.
 62                    //
 163                    byte[] key = new byte[16];
 164                    _rand.NextBytes(key);
 165                    _key = System.Convert.ToBase64String(key);
 166                    @out.Append(_key + "\r\n\r\n"); // EOM
 67
 168                    byte[] bytes = _utf8.GetBytes(@out.ToString());
 169                    _writeBuffer.resize(bytes.Length, false);
 170                    _writeBuffer.b.position(0);
 171                    _writeBuffer.b.put(bytes);
 172                    _writeBuffer.b.flip();
 73                }
 74            }
 75
 76            //
 77            // Try to write the client's upgrade request.
 78            //
 179            if (_state == StateUpgradeRequestPending && !_incoming)
 80            {
 181                if (_writeBuffer.b.hasRemaining())
 82                {
 183                    int s = _delegate.write(_writeBuffer);
 184                    if (s != 0)
 85                    {
 186                        return s;
 87                    }
 88                }
 89                Debug.Assert(!_writeBuffer.b.hasRemaining());
 190                _state = StateUpgradeResponsePending;
 91
 192                if (_instance.traceLevel() >= 1)
 93                {
 194                    _instance.logger().trace(
 195                        _instance.traceCategory(),
 196                        "sent " + protocol() + " connection HTTP upgrade request\n" + ToString());
 97                }
 98            }
 99
 100            while (true)
 101            {
 1102                if (_readBuffer.b.hasRemaining())
 103                {
 1104                    int s = _delegate.read(_readBuffer, ref hasMoreData);
 1105                    if (s == SocketOperation.Write || _readBuffer.b.position() == 0)
 106                    {
 1107                        return s;
 108                    }
 109                }
 110
 111                //
 112                // Try to read the client's upgrade request or the server's response.
 113                //
 1114                if ((_state == StateUpgradeRequestPending && _incoming) ||
 1115                   (_state == StateUpgradeResponsePending && !_incoming))
 116                {
 117                    //
 118                    // Check if we have enough data for a complete message.
 119                    //
 1120                    int p = _parser.isCompleteMessage(_readBuffer.b, 0, _readBuffer.b.position());
 1121                    if (p == -1)
 122                    {
 0123                        if (_readBuffer.b.hasRemaining())
 124                        {
 0125                            return SocketOperation.Read;
 126                        }
 127
 128                        //
 129                        // Enlarge the buffer and try to read more.
 130                        //
 0131                        int oldSize = _readBuffer.b.position();
 0132                        if (oldSize + 1024 > _instance.messageSizeMax())
 133                        {
 0134                            Ex.throwMemoryLimitException(
 0135                                requested: oldSize + 1024,
 0136                                maximum: _instance.messageSizeMax());
 137                        }
 0138                        _readBuffer.resize(oldSize + 1024, true);
 0139                        _readBuffer.b.position(oldSize);
 0140                        continue; // Try again to read the response/request
 141                    }
 142
 143                    //
 144                    // Set _readBufferPos at the end of the response/request message.
 145                    //
 1146                    _readBufferPos = p;
 147                }
 148
 149                //
 150                // We're done, the client's upgrade request or server's response is read.
 151                //
 152                break;
 153            }
 154
 155            try
 156            {
 157                //
 158                // Parse the client's upgrade request.
 159                //
 1160                if (_state == StateUpgradeRequestPending && _incoming)
 161                {
 1162                    if (_parser.parse(_readBuffer.b, 0, _readBufferPos))
 163                    {
 1164                        handleRequest(_writeBuffer);
 1165                        _state = StateUpgradeResponsePending;
 166                    }
 167                    else
 168                    {
 0169                        throw new Ice.ProtocolException("incomplete request message");
 170                    }
 171                }
 172
 1173                if (_state == StateUpgradeResponsePending)
 174                {
 1175                    if (_incoming)
 176                    {
 1177                        if (_writeBuffer.b.hasRemaining())
 178                        {
 1179                            int s = _delegate.write(_writeBuffer);
 1180                            if (s != 0)
 181                            {
 1182                                return s;
 183                            }
 184                        }
 185                    }
 186                    else
 187                    {
 188                        //
 189                        // Parse the server's response
 190                        //
 1191                        if (_parser.parse(_readBuffer.b, 0, _readBufferPos))
 192                        {
 1193                            handleResponse();
 194                        }
 195                        else
 196                        {
 0197                            throw new Ice.ProtocolException("incomplete response message");
 198                        }
 199                    }
 200                }
 1201            }
 0202            catch (WebSocketException ex)
 203            {
 0204                throw new Ice.ProtocolException(ex.Message);
 205            }
 206
 1207            _state = StateOpened;
 1208            _nextState = StateOpened;
 209
 1210            hasMoreData = _readBufferPos < _readBuffer.b.position();
 1211        }
 1212        catch (Ice.LocalException ex)
 213        {
 1214            if (_instance.traceLevel() >= 2)
 215            {
 1216                _instance.logger().trace(
 1217                    _instance.traceCategory(),
 1218                    protocol() + " connection HTTP upgrade request failed\n" + ToString() + "\n" + ex);
 219            }
 1220            throw;
 221        }
 222
 1223        if (_instance.traceLevel() >= 1)
 224        {
 1225            if (_incoming)
 226            {
 1227                _instance.logger().trace(
 1228                    _instance.traceCategory(),
 1229                    "accepted " + protocol() + " connection HTTP upgrade request\n" + ToString());
 230            }
 231            else
 232            {
 1233                _instance.logger().trace(
 1234                    _instance.traceCategory(),
 1235                    protocol() + " connection HTTP upgrade request accepted\n" + ToString());
 236            }
 237        }
 238
 1239        return SocketOperation.None;
 1240    }
 241
 242    public int closing(bool initiator, Ice.LocalException reason)
 243    {
 1244        if (_instance.traceLevel() >= 1)
 245        {
 1246            _instance.logger().trace(
 1247                _instance.traceCategory(),
 1248                "gracefully closing " + protocol() + " connection\n" + ToString());
 249        }
 250
 1251        int s = _nextState == StateOpened ? _state : _nextState;
 252
 1253        if (s == StateClosingRequestPending && _closingInitiator)
 254        {
 255            //
 256            // If we initiated a close connection but also received a
 257            // close connection, we assume we didn't initiated the
 258            // connection and we send the close frame now. This is to
 259            // ensure that if both peers close the connection at the same
 260            // time we don't hang having both peer waiting for the close
 261            // frame of the other.
 262            //
 263            Debug.Assert(!initiator);
 1264            _closingInitiator = false;
 1265            return SocketOperation.Write;
 266        }
 1267        else if (s >= StateClosingRequestPending)
 268        {
 0269            return SocketOperation.None;
 270        }
 271
 1272        _closingInitiator = initiator;
 1273        if (reason is Ice.CloseConnectionException)
 274        {
 1275            _closingReason = CLOSURE_NORMAL;
 276        }
 1277        else if (reason is Ice.ObjectAdapterDeactivatedException ||
 1278                reason is Ice.ObjectAdapterDestroyedException ||
 1279                reason is Ice.CommunicatorDestroyedException)
 280        {
 1281            _closingReason = CLOSURE_SHUTDOWN;
 282        }
 1283        else if (reason is Ice.ProtocolException)
 284        {
 0285            _closingReason = CLOSURE_PROTOCOL_ERROR;
 286        }
 1287        if (_state == StateOpened)
 288        {
 1289            _state = StateClosingRequestPending;
 1290            return initiator ? SocketOperation.Read : SocketOperation.Write;
 291        }
 292        else
 293        {
 0294            _nextState = StateClosingRequestPending;
 0295            return SocketOperation.None;
 296        }
 297    }
 298
 299    public void close()
 300    {
 1301        _delegate.close();
 1302        _state = StateClosed;
 303
 304        //
 305        // Clear the buffers now instead of waiting for destruction.
 306        //
 1307        if (!_readPending)
 308        {
 1309            _readBuffer.clear();
 310        }
 1311        if (!_writePending)
 312        {
 1313            _writeBuffer.clear();
 314        }
 1315    }
 316
 317    public EndpointI bind()
 318    {
 319        Debug.Assert(false);
 0320        return null;
 321    }
 322
 1323    public void destroy() => _delegate.destroy();
 324
 325    public int write(Buffer buf)
 326    {
 1327        if (_writePending)
 328        {
 0329            return SocketOperation.Write;
 330        }
 331
 1332        if (_state < StateOpened)
 333        {
 1334            if (_state < StateConnected)
 335            {
 1336                return _delegate.write(buf);
 337            }
 338            else
 339            {
 1340                return _delegate.write(_writeBuffer);
 341            }
 342        }
 343
 1344        int s = SocketOperation.None;
 345        do
 346        {
 1347            if (preWrite(buf))
 348            {
 1349                if (_writeState == WriteStateFlush)
 350                {
 351                    //
 352                    // Invoke write() even though there's nothing to write.
 353                    //
 354                    Debug.Assert(!buf.b.hasRemaining());
 0355                    s = _delegate.write(buf);
 356                }
 357
 1358                if (s == SocketOperation.None && _writeBuffer.b.hasRemaining())
 359                {
 1360                    s = _delegate.write(_writeBuffer);
 361                }
 1362                else if (s == SocketOperation.None && _incoming && !buf.empty() && _writeState == WriteStatePayload)
 363                {
 1364                    s = _delegate.write(buf);
 365                }
 366            }
 367        }
 1368        while (postWrite(buf, s));
 369
 1370        if (s != SocketOperation.None)
 371        {
 1372            return s;
 373        }
 1374        if (_state == StateClosingResponsePending && !_closingInitiator)
 375        {
 1376            return SocketOperation.Read;
 377        }
 1378        return SocketOperation.None;
 379    }
 380
 381    public int read(Buffer buf, ref bool hasMoreData)
 382    {
 1383        if (_readPending)
 384        {
 0385            return SocketOperation.Read;
 386        }
 387
 1388        if (_state < StateOpened)
 389        {
 1390            if (_state < StateConnected)
 391            {
 1392                return _delegate.read(buf, ref hasMoreData);
 393            }
 394            else
 395            {
 1396                if (_delegate.read(_readBuffer, ref hasMoreData) == SocketOperation.Write)
 397                {
 0398                    return SocketOperation.Write;
 399                }
 400                else
 401                {
 1402                    return SocketOperation.None;
 403                }
 404            }
 405        }
 406
 1407        if (!buf.b.hasRemaining())
 408        {
 1409            hasMoreData |= _readBufferPos < _readBuffer.b.position();
 1410            return SocketOperation.None;
 411        }
 412
 413        int s;
 414        do
 415        {
 1416            if (preRead(buf))
 417            {
 1418                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                    //
 1425                    int readSz = _readPayloadLength - (buf.b.position() - _readStart);
 1426                    if (buf.b.remaining() > readSz)
 427                    {
 0428                        int size = buf.size();
 0429                        buf.resize(buf.b.position() + readSz, true);
 0430                        s = _delegate.read(buf, ref hasMoreData);
 0431                        buf.resize(size, true);
 432                    }
 433                    else
 434                    {
 1435                        s = _delegate.read(buf, ref hasMoreData);
 436                    }
 437                }
 438                else
 439                {
 1440                    s = _delegate.read(_readBuffer, ref hasMoreData);
 441                }
 442
 1443                if (s == SocketOperation.Write)
 444                {
 0445                    postRead(buf);
 0446                    return s;
 447                }
 448            }
 449        }
 1450        while (postRead(buf));
 451
 1452        if (!buf.b.hasRemaining())
 453        {
 1454            hasMoreData |= _readBufferPos < _readBuffer.b.position();
 1455            s = SocketOperation.None;
 456        }
 457        else
 458        {
 1459            hasMoreData = false;
 1460            s = SocketOperation.Read;
 461        }
 462
 1463        if (((_state == StateClosingRequestPending && !_closingInitiator) ||
 1464            (_state == StateClosingResponsePending && _closingInitiator) ||
 1465            _state == StatePingPending ||
 1466            _state == StatePongPending) &&
 1467           _writeState == WriteStateHeader)
 468        {
 469            // We have things to write, ask to be notified when writes are ready.
 1470            s |= SocketOperation.Write;
 471        }
 472
 1473        return s;
 474    }
 475
 476    public bool startRead(Buffer buf, AsyncCallback callback, object state)
 477    {
 1478        _readPending = true;
 1479        if (_state < StateOpened)
 480        {
 1481            _finishRead = true;
 1482            if (_state < StateConnected)
 483            {
 1484                return _delegate.startRead(buf, callback, state);
 485            }
 486            else
 487            {
 1488                return _delegate.startRead(_readBuffer, callback, state);
 489            }
 490        }
 491
 1492        if (preRead(buf))
 493        {
 1494            _finishRead = true;
 1495            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                //
 1502                int readSz = _readPayloadLength - (buf.b.position() - _readStart);
 1503                if (buf.b.remaining() > readSz)
 504                {
 0505                    int size = buf.size();
 0506                    buf.resize(buf.b.position() + readSz, true);
 0507                    bool completedSynchronously = _delegate.startRead(buf, callback, state);
 0508                    buf.resize(size, true);
 0509                    return completedSynchronously;
 510                }
 511                else
 512                {
 1513                    return _delegate.startRead(buf, callback, state);
 514                }
 515            }
 516            else
 517            {
 1518                return _delegate.startRead(_readBuffer, callback, state);
 519            }
 520        }
 521        else
 522        {
 1523            return true;
 524        }
 525    }
 526
 527    public void finishRead(Buffer buf)
 528    {
 529        Debug.Assert(_readPending);
 1530        _readPending = false;
 531
 1532        if (_state < StateOpened)
 533        {
 534            Debug.Assert(_finishRead);
 1535            _finishRead = false;
 1536            if (_state < StateConnected)
 537            {
 1538                _delegate.finishRead(buf);
 539            }
 540            else
 541            {
 1542                _delegate.finishRead(_readBuffer);
 543            }
 1544            return;
 545        }
 546
 1547        if (!_finishRead)
 548        {
 549            // Nothing to do.
 550        }
 1551        else if (_readState == ReadStatePayload)
 552        {
 553            Debug.Assert(_finishRead);
 1554            _finishRead = false;
 1555            _delegate.finishRead(buf);
 556        }
 557        else
 558        {
 559            Debug.Assert(_finishRead);
 1560            _finishRead = false;
 1561            _delegate.finishRead(_readBuffer);
 562        }
 563
 1564        if (_state == StateClosed)
 565        {
 0566            _readBuffer.clear();
 0567            return;
 568        }
 569
 1570        postRead(buf);
 1571    }
 572
 573    public bool startWrite(Buffer buf, AsyncCallback callback, object state, out bool messageWritten)
 574    {
 1575        _writePending = true;
 1576        if (_state < StateOpened)
 577        {
 1578            if (_state < StateConnected)
 579            {
 1580                return _delegate.startWrite(buf, callback, state, out messageWritten);
 581            }
 582            else
 583            {
 1584                return _delegate.startWrite(_writeBuffer, callback, state, out messageWritten);
 585            }
 586        }
 587
 1588        if (preWrite(buf))
 589        {
 1590            if (_writeBuffer.b.hasRemaining())
 591            {
 1592                return _delegate.startWrite(_writeBuffer, callback, state, out messageWritten);
 593            }
 594            else
 595            {
 596                Debug.Assert(_incoming);
 1597                return _delegate.startWrite(buf, callback, state, out messageWritten);
 598            }
 599        }
 600        else
 601        {
 0602            messageWritten = true;
 0603            return false;
 604        }
 605    }
 606
 607    public void finishWrite(Buffer buf)
 608    {
 1609        _writePending = false;
 610
 1611        if (_state < StateOpened)
 612        {
 1613            if (_state < StateConnected)
 614            {
 1615                _delegate.finishWrite(buf);
 616            }
 617            else
 618            {
 1619                _delegate.finishWrite(_writeBuffer);
 620            }
 1621            return;
 622        }
 623
 1624        if (_writeBuffer.b.hasRemaining())
 625        {
 1626            _delegate.finishWrite(_writeBuffer);
 627        }
 1628        else if (!buf.empty() && buf.b.hasRemaining())
 629        {
 630            Debug.Assert(_incoming);
 1631            _delegate.finishWrite(buf);
 632        }
 633
 1634        if (_state == StateClosed)
 635        {
 0636            _writeBuffer.clear();
 0637            return;
 638        }
 639
 1640        postWrite(buf, SocketOperation.None);
 1641    }
 642
 1643    public string protocol() => _instance.protocol();
 644
 645    public ConnectionInfo getInfo(bool incoming, string adapterName, string connectionId) =>
 1646        new WSConnectionInfo(_delegate.getInfo(incoming, adapterName, connectionId), _parser.getHeaders());
 647
 1648    public void checkSendSize(Buffer buf) => _delegate.checkSendSize(buf);
 649
 1650    public void setBufferSize(int rcvSize, int sndSize) => _delegate.setBufferSize(rcvSize, sndSize);
 651
 1652    public override string ToString() => _delegate.ToString();
 653
 0654    public string toDetailedString() => _delegate.toDetailedString();
 655
 1656    internal
 1657    WSTransceiver(ProtocolInstance instance, Transceiver del, string host, string resource)
 658    {
 1659        init(instance, del);
 1660        _host = host;
 1661        _resource = resource;
 1662        _incoming = false;
 663
 664        //
 665        // Use a 16KB write buffer size. We use 16KB for the write
 666        // buffer size because all the data needs to be copied to the
 667        // write buffer for the purpose of masking. A 16KB buffer
 668        // appears to be a good compromise to reduce the number of
 669        // socket write calls and not consume too much memory.
 670        //
 1671        _writeBufferSize = 16 * 1024;
 672
 673        //
 674        // Write and read buffer size must be large enough to hold the frame header!
 675        //
 676        Debug.Assert(_writeBufferSize > 256);
 677        Debug.Assert(_readBufferSize > 256);
 1678    }
 679
 1680    internal WSTransceiver(ProtocolInstance instance, Transceiver del)
 681    {
 1682        init(instance, del);
 1683        _host = "";
 1684        _resource = "";
 1685        _incoming = true;
 686
 687        //
 688        // Write and read buffer size must be large enough to hold the frame header!
 689        //
 690        Debug.Assert(_writeBufferSize > 256);
 691        Debug.Assert(_readBufferSize > 256);
 1692    }
 693
 694    private void init(ProtocolInstance instance, Transceiver del)
 695    {
 1696        _instance = instance;
 1697        _delegate = del;
 1698        _state = StateInitializeDelegate;
 1699        _parser = new HttpParser();
 1700        _readState = ReadStateOpcode;
 1701        _readBuffer = new Buffer(ByteBuffer.ByteOrder.BigEndian); // Network byte order
 1702        _readBufferSize = 1024;
 1703        _readLastFrame = true;
 1704        _readOpCode = 0;
 1705        _readHeaderLength = 0;
 1706        _readPayloadLength = 0;
 1707        _writeState = WriteStateHeader;
 1708        _writeBuffer = new Buffer(ByteBuffer.ByteOrder.BigEndian); // Network byte order
 1709        _writeBufferSize = 1024;
 1710        _readPending = false;
 1711        _finishRead = false;
 1712        _writePending = false;
 1713        _readMask = new byte[4];
 1714        _writeMask = new byte[4];
 1715        _key = "";
 1716        _pingPayload = [];
 1717        _rand = new Random();
 1718    }
 719
 720    private void handleRequest(Buffer responseBuffer)
 721    {
 722        //
 723        // HTTP/1.1
 724        //
 1725        if (_parser.versionMajor() != 1 || _parser.versionMinor() != 1)
 726        {
 0727            throw new WebSocketException("unsupported HTTP version");
 728        }
 729
 730        //
 731        // "An |Upgrade| header field containing the value 'websocket',
 732        //  treated as an ASCII case-insensitive value."
 733        //
 1734        string val = _parser.getHeader("Upgrade", true);
 1735        if (val == null)
 736        {
 0737            throw new WebSocketException("missing value for Upgrade field");
 738        }
 1739        else if (val != "websocket")
 740        {
 0741            throw new WebSocketException("invalid value `" + val + "' for Upgrade field");
 742        }
 743
 744        //
 745        // "A |Connection| header field that includes the token 'Upgrade',
 746        //  treated as an ASCII case-insensitive value.
 747        //
 1748        val = _parser.getHeader("Connection", true);
 1749        if (val == null)
 750        {
 0751            throw new WebSocketException("missing value for Connection field");
 752        }
 1753        else if (!val.Contains("upgrade", StringComparison.Ordinal))
 754        {
 0755            throw new WebSocketException("invalid value `" + val + "' for Connection field");
 756        }
 757
 758        //
 759        // "A |Sec-WebSocket-Version| header field, with a value of 13."
 760        //
 1761        val = _parser.getHeader("Sec-WebSocket-Version", false);
 1762        if (val == null)
 763        {
 0764            throw new WebSocketException("missing value for WebSocket version");
 765        }
 1766        else if (val != "13")
 767        {
 0768            throw new WebSocketException("unsupported WebSocket version `" + val + "'");
 769        }
 770
 771        //
 772        // "Optionally, a |Sec-WebSocket-Protocol| header field, with a list
 773        //  of values indicating which protocols the client would like to
 774        //  speak, ordered by preference."
 775        //
 1776        bool addProtocol = false;
 1777        val = _parser.getHeader("Sec-WebSocket-Protocol", true);
 1778        if (val != null)
 779        {
 1780            string[] protocols = Ice.UtilInternal.StringUtil.splitString(val, ",") ??
 1781                throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol");
 1782            foreach (string p in protocols)
 783            {
 1784                if (!p.Trim().Equals(_iceProtocol, StringComparison.Ordinal))
 785                {
 0786                    throw new WebSocketException("unknown value `" + p + "' for WebSocket protocol");
 787                }
 1788                addProtocol = true;
 789            }
 790        }
 791
 792        //
 793        // "A |Sec-WebSocket-Key| header field with a base64-encoded
 794        //  value that, when decoded, is 16 bytes in length."
 795        //
 1796        string key = _parser.getHeader("Sec-WebSocket-Key", false) ??
 1797            throw new WebSocketException("missing value for WebSocket key");
 1798        byte[] decodedKey = Convert.FromBase64String(key);
 1799        if (decodedKey.Length != 16)
 800        {
 0801            throw new WebSocketException("invalid value `" + key + "' for WebSocket key");
 802        }
 803
 804        //
 805        // Retain the target resource.
 806        //
 1807        _resource = _parser.uri();
 808
 809        //
 810        // Compose the response.
 811        //
 1812        var @out = new StringBuilder();
 1813        @out.Append("HTTP/1.1 101 Switching Protocols\r\n");
 1814        @out.Append("Upgrade: websocket\r\n");
 1815        @out.Append("Connection: Upgrade\r\n");
 1816        if (addProtocol)
 817        {
 1818            @out.Append("Sec-WebSocket-Protocol: " + _iceProtocol + "\r\n");
 819        }
 820
 821        //
 822        // The response includes:
 823        //
 824        // "A |Sec-WebSocket-Accept| header field.  The value of this
 825        //  header field is constructed by concatenating /key/, defined
 826        //  above in step 4 in Section 4.2.2, with the string "258EAFA5-
 827        //  E914-47DA-95CA-C5AB0DC85B11", taking the SHA-1 hash of this
 828        //  concatenated value to obtain a 20-byte value and base64-
 829        //  encoding (see Section 4 of [RFC4648]) this 20-byte hash.
 830        //
 1831        @out.Append("Sec-WebSocket-Accept: ");
 1832        string input = key + _wsUUID;
 833#pragma warning disable CA5350 // SHA1 is used for compatibility with the WebSocket protocol
 1834        using var sha1 = SHA1.Create();
 1835        byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input));
 836#pragma warning restore CA5350
 1837        @out.Append(Convert.ToBase64String(hash) + "\r\n" + "\r\n"); // EOM
 838
 1839        byte[] bytes = _utf8.GetBytes(@out.ToString());
 840        Debug.Assert(bytes.Length == @out.Length);
 1841        responseBuffer.resize(bytes.Length, false);
 1842        responseBuffer.b.position(0);
 1843        responseBuffer.b.put(bytes);
 1844        responseBuffer.b.flip();
 1845    }
 846
 847    private void handleResponse()
 848    {
 849        string val;
 850
 851        //
 852        // HTTP/1.1
 853        //
 1854        if (_parser.versionMajor() != 1 || _parser.versionMinor() != 1)
 855        {
 0856            throw new WebSocketException("unsupported HTTP version");
 857        }
 858
 859        //
 860        // "If the status code received from the server is not 101, the
 861        //  client handles the response per HTTP [RFC2616] procedures.  In
 862        //  particular, the client might perform authentication if it
 863        //  receives a 401 status code; the server might redirect the client
 864        //  using a 3xx status code (but clients are not required to follow
 865        //  them), etc."
 866        //
 1867        if (_parser.status() != 101)
 868        {
 0869            var @out = new StringBuilder("unexpected status value " + _parser.status());
 0870            if (_parser.reason().Length > 0)
 871            {
 0872                @out.Append(":\n" + _parser.reason());
 873            }
 0874            throw new WebSocketException(@out.ToString());
 875        }
 876
 877        //
 878        // "If the response lacks an |Upgrade| header field or the |Upgrade|
 879        //  header field contains a value that is not an ASCII case-
 880        //  insensitive match for the value "websocket", the client MUST
 881        //  _Fail the WebSocket Connection_."
 882        //
 1883        val = _parser.getHeader("Upgrade", true);
 1884        if (val == null)
 885        {
 0886            throw new WebSocketException("missing value for Upgrade field");
 887        }
 1888        else if (val != "websocket")
 889        {
 0890            throw new WebSocketException("invalid value `" + val + "' for Upgrade field");
 891        }
 892
 893        //
 894        // "If the response lacks a |Connection| header field or the
 895        //  |Connection| header field doesn't contain a token that is an
 896        //  ASCII case-insensitive match for the value "Upgrade", the client
 897        //  MUST _Fail the WebSocket Connection_."
 898        //
 1899        val = _parser.getHeader("Connection", true);
 1900        if (val == null)
 901        {
 0902            throw new WebSocketException("missing value for Connection field");
 903        }
 1904        else if (!val.Contains("upgrade", StringComparison.Ordinal))
 905        {
 0906            throw new WebSocketException("invalid value `" + val + "' for Connection field");
 907        }
 908
 909        //
 910        // "If the response includes a |Sec-WebSocket-Protocol| header field
 911        //  and this header field indicates the use of a subprotocol that was
 912        //  not present in the client's handshake (the server has indicated a
 913        //  subprotocol not requested by the client), the client MUST _Fail
 914        //  the WebSocket Connection_."
 915        //
 1916        val = _parser.getHeader("Sec-WebSocket-Protocol", true);
 1917        if (val != null && !val.Equals(_iceProtocol, StringComparison.Ordinal))
 918        {
 0919            throw new WebSocketException("invalid value `" + val + "' for WebSocket protocol");
 920        }
 921
 922        //
 923        // "If the response lacks a |Sec-WebSocket-Accept| header field or
 924        //  the |Sec-WebSocket-Accept| contains a value other than the
 925        //  base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket-
 926        //  Key| (as a string, not base64-decoded) with the string "258EAFA5-
 927        //  E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and
 928        //  trailing whitespace, the client MUST _Fail the WebSocket
 929        //  Connection_."
 930        //
 1931        val = _parser.getHeader("Sec-WebSocket-Accept", false);
 1932        if (val == null)
 933        {
 0934            throw new WebSocketException("missing value for Sec-WebSocket-Accept");
 935        }
 936
 1937        string input = _key + _wsUUID;
 938#pragma warning disable CA5350 // SHA1 is used for compatibility with the WebSocket protocol
 1939        using var sha1 = SHA1.Create();
 1940        byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input));
 941#pragma warning restore CA5350
 1942        if (!val.Equals(Convert.ToBase64String(hash), StringComparison.Ordinal))
 943        {
 0944            throw new WebSocketException("invalid value `" + val + "' for Sec-WebSocket-Accept");
 945        }
 1946    }
 947
 948    private bool preRead(Buffer buf)
 949    {
 950        while (true)
 951        {
 1952            if (_readState == ReadStateOpcode)
 953            {
 954                //
 955                // Is there enough data available to read the opcode?
 956                //
 1957                if (!readBuffered(2))
 958                {
 1959                    return true;
 960                }
 961
 962                //
 963                // Most-significant bit indicates whether this is the
 964                // last frame. Least-significant four bits hold the
 965                // opcode.
 966                //
 1967                int ch = _readBuffer.b.get(_readBufferPos++);
 1968                _readOpCode = ch & 0xf;
 969
 970                //
 971                // Remember if last frame if we're going to read a data or
 972                // continuation frame, this is only for protocol
 973                // correctness checking purpose.
 974                //
 1975                if (_readOpCode == OP_DATA)
 976                {
 1977                    if (!_readLastFrame)
 978                    {
 0979                        throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame");
 980                    }
 1981                    _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
 982                }
 1983                else if (_readOpCode == OP_CONT)
 984                {
 0985                    if (_readLastFrame)
 986                    {
 0987                        throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set");
 988                    }
 0989                    _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
 990                }
 991
 1992                ch = _readBuffer.b.get(_readBufferPos++);
 993
 994                //
 995                // Check the MASK bit. Messages sent by a client must be masked;
 996                // messages sent by a server must not be masked.
 997                //
 1998                bool masked = (ch & FLAG_MASKED) == FLAG_MASKED;
 1999                if (masked != _incoming)
 1000                {
 01001                    throw new Ice.ProtocolException("invalid masking");
 1002                }
 1003
 1004                //
 1005                // Extract the payload length, which can have the following values:
 1006                //
 1007                // 0-125: The payload length
 1008                // 126:   The subsequent two bytes contain the payload length
 1009                // 127:   The subsequent eight bytes contain the payload length
 1010                //
 11011                _readPayloadLength = ch & 0x7f;
 11012                if (_readPayloadLength < 126)
 1013                {
 11014                    _readHeaderLength = 0;
 1015                }
 11016                else if (_readPayloadLength == 126)
 1017                {
 11018                    _readHeaderLength = 2; // Need to read a 16-bit payload length.
 1019                }
 1020                else
 1021                {
 11022                    _readHeaderLength = 8; // Need to read a 64-bit payload length.
 1023                }
 11024                if (masked)
 1025                {
 11026                    _readHeaderLength += 4; // Need to read a 32-bit mask.
 1027                }
 1028
 11029                _readState = ReadStateHeader;
 1030            }
 1031
 11032            if (_readState == ReadStateHeader)
 1033            {
 1034                //
 1035                // Is there enough data available to read the header?
 1036                //
 11037                if (_readHeaderLength > 0 && !readBuffered(_readHeaderLength))
 1038                {
 11039                    return true;
 1040                }
 1041
 11042                if (_readPayloadLength == 126)
 1043                {
 11044                    _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order.
 11045                    if (_readPayloadLength < 0)
 1046                    {
 01047                        _readPayloadLength += 65536;
 1048                    }
 11049                    _readBufferPos += 2;
 1050                }
 11051                else if (_readPayloadLength == 127)
 1052                {
 11053                    long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order.
 11054                    _readBufferPos += 8;
 11055                    if (l < 0 || l > int.MaxValue)
 1056                    {
 01057                        throw new Ice.ProtocolException("invalid WebSocket payload length: " + l);
 1058                    }
 11059                    _readPayloadLength = (int)l;
 1060                }
 1061
 1062                //
 1063                // Read the mask if this is an incoming connection.
 1064                //
 11065                if (_incoming)
 1066                {
 1067                    //
 1068                    // We must have needed to read the mask.
 1069                    //
 1070                    Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4);
 11071                    for (int i = 0; i < 4; ++i)
 1072                    {
 11073                        _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask.
 1074                    }
 1075                }
 1076
 11077                switch (_readOpCode)
 1078                {
 1079                    case OP_TEXT: // Text frame
 1080                    {
 01081                        throw new Ice.ProtocolException("text frames not supported");
 1082                    }
 1083                    case OP_DATA: // Data frame
 1084                    case OP_CONT: // Continuation frame
 1085                    {
 11086                        if (_instance.traceLevel() >= 2)
 1087                        {
 11088                            _instance.logger().trace(
 11089                                _instance.traceCategory(), "received " + protocol() +
 11090                                (_readOpCode == OP_DATA ? " data" : " continuation") +
 11091                                " frame with payload length of " + _readPayloadLength +
 11092                                " bytes\n" + ToString());
 1093                        }
 1094
 11095                        if (_readPayloadLength <= 0)
 1096                        {
 01097                            throw new Ice.ProtocolException("payload length is 0");
 1098                        }
 11099                        _readState = ReadStatePayload;
 1100                        Debug.Assert(buf.b.hasRemaining());
 11101                        _readFrameStart = buf.b.position();
 11102                        break;
 1103                    }
 1104                    case OP_CLOSE: // Connection close
 1105                    {
 11106                        if (_instance.traceLevel() >= 2)
 1107                        {
 11108                            _instance.logger().trace(
 11109                                _instance.traceCategory(),
 11110                                "received " + protocol() + " connection close frame\n" + ToString());
 1111                        }
 1112
 11113                        _readState = ReadStateControlFrame;
 11114                        int s = _nextState == StateOpened ? _state : _nextState;
 11115                        if (s == StateClosingRequestPending)
 1116                        {
 1117                            //
 1118                            // If we receive a close frame while we were actually
 1119                            // waiting to send one, change the role and send a
 1120                            // close frame response.
 1121                            //
 11122                            if (!_closingInitiator)
 1123                            {
 01124                                _closingInitiator = true;
 1125                            }
 11126                            if (_state == StateClosingRequestPending)
 1127                            {
 11128                                _state = StateClosingResponsePending;
 1129                            }
 1130                            else
 1131                            {
 01132                                _nextState = StateClosingResponsePending;
 1133                            }
 11134                            return false; // No longer interested in reading
 1135                        }
 1136                        else
 1137                        {
 11138                            throw new Ice.ConnectionLostException();
 1139                        }
 1140                    }
 1141                    case OP_PING:
 1142                    {
 01143                        if (_instance.traceLevel() >= 2)
 1144                        {
 01145                            _instance.logger().trace(
 01146                                _instance.traceCategory(),
 01147                                "received " + protocol() + " connection ping frame\n" + ToString());
 1148                        }
 01149                        _readState = ReadStateControlFrame;
 01150                        break;
 1151                    }
 1152                    case OP_PONG: // Pong
 1153                    {
 01154                        if (_instance.traceLevel() >= 2)
 1155                        {
 01156                            _instance.logger().trace(
 01157                                _instance.traceCategory(),
 01158                                "received " + protocol() + " connection pong frame\n" + ToString());
 1159                        }
 01160                        _readState = ReadStateControlFrame;
 01161                        break;
 1162                    }
 1163                    default:
 1164                    {
 01165                        throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode);
 1166                    }
 1167                }
 1168            }
 1169
 11170            if (_readState == ReadStateControlFrame)
 1171            {
 11172                if (_readPayloadLength > 0 && !readBuffered(_readPayloadLength))
 1173                {
 01174                    return true;
 1175                }
 1176
 11177                if (_readPayloadLength > 0 && _readOpCode == OP_PING)
 1178                {
 01179                    _pingPayload = new byte[_readPayloadLength];
 01180                    System.Buffer.BlockCopy(
 01181                        _readBuffer.b.rawBytes(),
 01182                        _readBufferPos,
 01183                        _pingPayload,
 01184                        0,
 01185                        _readPayloadLength);
 1186                }
 1187
 11188                _readBufferPos += _readPayloadLength;
 11189                _readPayloadLength = 0;
 1190
 11191                if (_readOpCode == OP_PING)
 1192                {
 01193                    if (_state == StateOpened)
 1194                    {
 01195                        _state = StatePongPending; // Send pong frame now
 1196                    }
 01197                    else if (_nextState < StatePongPending)
 1198                    {
 01199                        _nextState = StatePongPending; // Send pong frame next
 1200                    }
 1201                }
 1202
 1203                //
 1204                // We've read the payload of the PING/PONG frame, we're ready
 1205                // to read a new frame.
 1206                //
 11207                _readState = ReadStateOpcode;
 1208            }
 1209
 11210            if (_readState == ReadStatePayload)
 1211            {
 1212                //
 1213                // This must be assigned before the check for the buffer. If the buffer is empty
 1214                // or already read, postRead will return false.
 1215                //
 11216                _readStart = buf.b.position();
 1217
 11218                if (buf.empty() || !buf.b.hasRemaining())
 1219                {
 01220                    return false;
 1221                }
 1222
 11223                int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining());
 11224                if (n > _readPayloadLength)
 1225                {
 01226                    n = _readPayloadLength;
 1227                }
 11228                if (n > 0)
 1229                {
 11230                    System.Buffer.BlockCopy(
 11231                        _readBuffer.b.rawBytes(),
 11232                        _readBufferPos,
 11233                        buf.b.rawBytes(),
 11234                        buf.b.position(),
 11235                        n);
 11236                    buf.b.position(buf.b.position() + n);
 11237                    _readBufferPos += n;
 1238                }
 1239
 1240                //
 1241                // Continue reading if we didn't read the full message, otherwise give back
 1242                // the control to the connection
 1243                //
 11244                return buf.b.hasRemaining() && n < _readPayloadLength;
 1245            }
 1246        }
 1247    }
 1248
 1249    private bool postRead(Buffer buf)
 1250    {
 11251        if (_readState != ReadStatePayload)
 1252        {
 11253            return _readStart < _readBuffer.b.position(); // Returns true if data was read.
 1254        }
 1255
 11256        if (_readStart == buf.b.position())
 1257        {
 11258            return false; // Nothing was read or nothing to read.
 1259        }
 1260        Debug.Assert(_readStart < buf.b.position());
 1261
 11262        if (_incoming)
 1263        {
 1264            //
 1265            // Unmask the data we just read.
 1266            //
 11267            int pos = buf.b.position();
 11268            byte[] arr = buf.b.rawBytes();
 11269            for (int n = _readStart; n < pos; ++n)
 1270            {
 11271                arr[n] = (byte)(arr[n] ^ _readMask[(n - _readFrameStart) % 4]);
 1272            }
 1273        }
 1274
 11275        _readPayloadLength -= buf.b.position() - _readStart;
 11276        _readStart = buf.b.position();
 11277        if (_readPayloadLength == 0)
 1278        {
 1279            //
 1280            // We've read the complete payload, we're ready to read a new frame.
 1281            //
 11282            _readState = ReadStateOpcode;
 1283        }
 11284        return buf.b.hasRemaining();
 1285    }
 1286
 1287    private bool preWrite(Buffer buf)
 1288    {
 11289        if (_writeState == WriteStateHeader)
 1290        {
 11291            if (_state == StateOpened)
 1292            {
 11293                if (buf.empty() || !buf.b.hasRemaining())
 1294                {
 11295                    return false;
 1296                }
 1297
 1298                Debug.Assert(buf.b.position() == 0);
 11299                prepareWriteHeader((byte)OP_DATA, buf.size());
 1300
 11301                _writeState = WriteStatePayload;
 1302            }
 11303            else if (_state == StatePingPending)
 1304            {
 01305                prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload
 1306
 01307                _writeState = WriteStateControlFrame;
 01308                _writeBuffer.b.flip();
 1309            }
 11310            else if (_state == StatePongPending)
 1311            {
 01312                prepareWriteHeader((byte)OP_PONG, _pingPayload.Length);
 01313                if (_pingPayload.Length > _writeBuffer.b.remaining())
 1314                {
 01315                    int pos = _writeBuffer.b.position();
 01316                    _writeBuffer.resize(pos + _pingPayload.Length, false);
 01317                    _writeBuffer.b.position(pos);
 1318                }
 01319                _writeBuffer.b.put(_pingPayload);
 01320                _pingPayload = [];
 1321
 01322                _writeState = WriteStateControlFrame;
 01323                _writeBuffer.b.flip();
 1324            }
 11325            else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11326                    (_state == StateClosingResponsePending && _closingInitiator))
 1327            {
 11328                prepareWriteHeader((byte)OP_CLOSE, 2);
 1329
 1330                // Write closing reason
 11331                _writeBuffer.b.putShort((short)_closingReason);
 1332
 11333                if (!_incoming)
 1334                {
 1335                    byte b;
 11336                    int pos = _writeBuffer.b.position() - 2;
 11337                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]);
 11338                    _writeBuffer.b.put(pos, b);
 11339                    pos++;
 11340                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]);
 11341                    _writeBuffer.b.put(pos, b);
 1342                }
 1343
 11344                _writeState = WriteStateControlFrame;
 11345                _writeBuffer.b.flip();
 1346            }
 1347            else
 1348            {
 1349                Debug.Assert(_state != StateClosed);
 11350                return false; // Nothing to write in this state
 1351            }
 1352
 11353            _writePayloadLength = 0;
 1354        }
 1355
 11356        if (_writeState == WriteStatePayload)
 1357        {
 1358            //
 1359            // For an outgoing connection, each message must be masked with a random
 1360            // 32-bit value, so we copy the entire message into the internal buffer
 1361            // for writing. For incoming connections, we just copy the start of the
 1362            // message in the internal buffer after the hedaer. If the message is
 1363            // larger, the reminder is sent directly from the message buffer to avoid
 1364            // copying.
 1365            //
 11366            if (!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining()))
 1367            {
 11368                if (!_writeBuffer.b.hasRemaining())
 1369                {
 11370                    _writeBuffer.b.position(0);
 1371                }
 1372
 11373                int n = buf.b.position();
 11374                int sz = buf.size();
 11375                int pos = _writeBuffer.b.position();
 11376                int count = Math.Min(sz - n, _writeBuffer.b.remaining());
 11377                byte[] src = buf.b.rawBytes();
 11378                byte[] dest = _writeBuffer.b.rawBytes();
 11379                for (int i = 0; i < count; ++i, ++n, ++pos)
 1380                {
 11381                    dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]);
 1382                }
 11383                _writeBuffer.b.position(pos);
 11384                _writePayloadLength = n;
 1385
 11386                _writeBuffer.b.flip();
 1387            }
 11388            else if (_writePayloadLength == 0)
 1389            {
 1390                Debug.Assert(_incoming);
 11391                if (_writeBuffer.b.hasRemaining())
 1392                {
 1393                    Debug.Assert(buf.b.position() == 0);
 11394                    int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining());
 11395                    int pos = _writeBuffer.b.position();
 11396                    System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n);
 11397                    _writeBuffer.b.position(pos + n);
 11398                    _writePayloadLength = n;
 1399                }
 11400                _writeBuffer.b.flip();
 1401            }
 11402            return true;
 1403        }
 11404        else if (_writeState == WriteStateControlFrame)
 1405        {
 11406            return _writeBuffer.b.hasRemaining();
 1407        }
 1408        else
 1409        {
 1410            Debug.Assert(_writeState == WriteStateFlush);
 01411            return true;
 1412        }
 1413    }
 1414
 1415    private bool postWrite(Buffer buf, int status)
 1416    {
 11417        if (_state > StateOpened && _writeState == WriteStateControlFrame)
 1418        {
 11419            if (!_writeBuffer.b.hasRemaining())
 1420            {
 11421                if (_state == StatePingPending)
 1422                {
 01423                    if (_instance.traceLevel() >= 2)
 1424                    {
 01425                        _instance.logger().trace(
 01426                            _instance.traceCategory(),
 01427                            "sent " + protocol() + " connection ping frame\n" + ToString());
 1428                    }
 1429                }
 11430                else if (_state == StatePongPending)
 1431                {
 01432                    if (_instance.traceLevel() >= 2)
 1433                    {
 01434                        _instance.logger().trace(
 01435                            _instance.traceCategory(),
 01436                            "sent " + protocol() + " connection pong frame\n" + ToString());
 1437                    }
 1438                }
 11439                else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11440                        (_state == StateClosingResponsePending && _closingInitiator))
 1441                {
 11442                    if (_instance.traceLevel() >= 2)
 1443                    {
 11444                        _instance.logger().trace(
 11445                            _instance.traceCategory(),
 11446                            "sent " + protocol() + " connection close frame\n" + ToString());
 1447                    }
 1448
 11449                    if (_state == StateClosingRequestPending && !_closingInitiator)
 1450                    {
 11451                        _writeState = WriteStateHeader;
 11452                        _state = StateClosingResponsePending;
 11453                        return false;
 1454                    }
 1455                    else
 1456                    {
 11457                        throw new Ice.ConnectionLostException();
 1458                    }
 1459                }
 01460                else if (_state == StateClosed)
 1461                {
 01462                    return false;
 1463                }
 1464
 01465                _state = _nextState;
 01466                _nextState = StateOpened;
 01467                _writeState = WriteStateHeader;
 1468            }
 1469            else
 1470            {
 11471                return status == SocketOperation.None;
 1472            }
 1473        }
 1474
 11475        if ((!_incoming || buf.b.position() == 0) && _writePayloadLength > 0)
 1476        {
 11477            if (!_writeBuffer.b.hasRemaining())
 1478            {
 11479                buf.b.position(_writePayloadLength);
 1480            }
 1481        }
 1482
 11483        if (status == SocketOperation.Write && !buf.b.hasRemaining() && !_writeBuffer.b.hasRemaining())
 1484        {
 1485            //
 1486            // Our buffers are empty but the delegate needs another call to write().
 1487            //
 01488            _writeState = WriteStateFlush;
 01489            return false;
 1490        }
 11491        else if (!buf.b.hasRemaining())
 1492        {
 11493            _writeState = WriteStateHeader;
 11494            if (_state == StatePingPending ||
 11495               _state == StatePongPending ||
 11496               (_state == StateClosingRequestPending && !_closingInitiator) ||
 11497               (_state == StateClosingResponsePending && _closingInitiator))
 1498            {
 11499                return true;
 1500            }
 1501        }
 11502        else if (_state == StateOpened)
 1503        {
 11504            return status == SocketOperation.None;
 1505        }
 1506
 11507        return false;
 1508    }
 1509
 1510    private bool readBuffered(int sz)
 1511    {
 11512        if (_readBufferPos == _readBuffer.b.position())
 1513        {
 11514            _readBuffer.resize(_readBufferSize, true);
 11515            _readBufferPos = 0;
 11516            _readBuffer.b.position(0);
 1517        }
 1518        else
 1519        {
 11520            int available = _readBuffer.b.position() - _readBufferPos;
 11521            if (available < sz)
 1522            {
 11523                if (_readBufferPos > 0)
 1524                {
 11525                    _readBuffer.b.limit(_readBuffer.b.position());
 11526                    _readBuffer.b.position(_readBufferPos);
 11527                    _readBuffer.b.compact();
 1528                    Debug.Assert(_readBuffer.b.position() == available);
 1529                }
 11530                _readBuffer.resize(Math.Max(_readBufferSize, sz), true);
 11531                _readBufferPos = 0;
 11532                _readBuffer.b.position(available);
 1533            }
 1534        }
 1535
 11536        _readStart = _readBuffer.b.position();
 11537        if (_readBufferPos + sz > _readBuffer.b.position())
 1538        {
 11539            return false; // Not enough read.
 1540        }
 1541        Debug.Assert(_readBuffer.b.position() > _readBufferPos);
 11542        return true;
 1543    }
 1544
 1545    private void prepareWriteHeader(byte opCode, int payloadLength)
 1546    {
 1547        //
 1548        // We need to prepare the frame header.
 1549        //
 11550        _writeBuffer.resize(_writeBufferSize, false);
 11551        _writeBuffer.b.limit(_writeBufferSize);
 11552        _writeBuffer.b.position(0);
 1553
 1554        //
 1555        // Set the opcode - this is the one and only data frame.
 1556        //
 11557        _writeBuffer.b.put((byte)(opCode | FLAG_FINAL));
 1558
 1559        //
 1560        // Set the payload length.
 1561        //
 11562        if (payloadLength <= 125)
 1563        {
 11564            _writeBuffer.b.put((byte)payloadLength);
 1565        }
 11566        else if (payloadLength > 125 && payloadLength <= 65535)
 1567        {
 1568            //
 1569            // Use an extra 16 bits to encode the payload length.
 1570            //
 11571            _writeBuffer.b.put(126);
 11572            _writeBuffer.b.putShort((short)payloadLength);
 1573        }
 11574        else if (payloadLength > 65535)
 1575        {
 1576            //
 1577            // Use an extra 64 bits to encode the payload length.
 1578            //
 11579            _writeBuffer.b.put(127);
 11580            _writeBuffer.b.putLong(payloadLength);
 1581        }
 1582
 11583        if (!_incoming)
 1584        {
 1585            //
 1586            // Add a random 32-bit mask to every outgoing frame, copy the payload data,
 1587            // and apply the mask.
 1588            //
 11589            _writeBuffer.b.put(1, (byte)(_writeBuffer.b.get(1) | FLAG_MASKED));
 11590            _rand.NextBytes(_writeMask);
 11591            _writeBuffer.b.put(_writeMask);
 1592        }
 11593    }
 1594
 1595    private ProtocolInstance _instance;
 1596    private Transceiver _delegate;
 1597    private readonly string _host;
 1598    private string _resource;
 1599    private readonly bool _incoming;
 1600
 1601    private const int StateInitializeDelegate = 0;
 1602    private const int StateConnected = 1;
 1603    private const int StateUpgradeRequestPending = 2;
 1604    private const int StateUpgradeResponsePending = 3;
 1605    private const int StateOpened = 4;
 1606    private const int StatePingPending = 5;
 1607    private const int StatePongPending = 6;
 1608    private const int StateClosingRequestPending = 7;
 1609    private const int StateClosingResponsePending = 8;
 1610    private const int StateClosed = 9;
 1611
 1612    private int _state;
 1613    private int _nextState;
 1614
 1615    private HttpParser _parser;
 1616    private string _key;
 1617
 1618    private const int ReadStateOpcode = 0;
 1619    private const int ReadStateHeader = 1;
 1620    private const int ReadStateControlFrame = 2;
 1621    private const int ReadStatePayload = 3;
 1622
 1623    private int _readState;
 1624    private Buffer _readBuffer;
 1625    private int _readBufferPos;
 1626    private int _readBufferSize;
 1627
 1628    private bool _readLastFrame;
 1629    private int _readOpCode;
 1630    private int _readHeaderLength;
 1631    private int _readPayloadLength;
 1632    private int _readStart;
 1633    private int _readFrameStart;
 1634    private byte[] _readMask;
 1635
 1636    private const int WriteStateHeader = 0;
 1637    private const int WriteStatePayload = 1;
 1638    private const int WriteStateControlFrame = 2;
 1639    private const int WriteStateFlush = 3;
 1640
 1641    private int _writeState;
 1642    private Buffer _writeBuffer;
 1643    private int _writeBufferSize;
 1644    private byte[] _writeMask;
 1645    private int _writePayloadLength;
 1646
 1647    private bool _closingInitiator;
 1648    private int _closingReason;
 1649
 1650    private bool _readPending;
 1651    private bool _finishRead;
 1652    private bool _writePending;
 1653
 1654    private byte[] _pingPayload;
 1655
 1656    private Random _rand;
 1657
 1658    //
 1659    // WebSocket opcodes
 1660    //
 1661    private const int OP_CONT = 0x0;    // Continuation frame
 1662    private const int OP_TEXT = 0x1;    // Text frame
 1663    private const int OP_DATA = 0x2;    // Data frame
 1664    // private const int OP_RES_0x3 = 0x3;    // Reserved
 1665    // private const int OP_RES_0x4 = 0x4;    // Reserved
 1666    // private const int OP_RES_0x5 = 0x5;    // Reserved
 1667    // private const int OP_RES_0x6 = 0x6;    // Reserved
 1668    // private const int OP_RES_0x7 = 0x7;    // Reserved
 1669    private const int OP_CLOSE = 0x8;    // Connection close
 1670    private const int OP_PING = 0x9;    // Ping
 1671    private const int OP_PONG = 0xA;    // Pong
 1672    // private const int OP_RES_0xB = 0xB;    // Reserved
 1673    // private const int OP_RES_0xC = 0xC;    // Reserved
 1674    // private const int OP_RES_0xD = 0xD;    // Reserved
 1675    // private const int OP_RES_0xE = 0xE;    // Reserved
 1676    // private const int OP_RES_0xF = 0xF;    // Reserved
 1677    private const int FLAG_FINAL = 0x80;   // Last frame
 1678    private const int FLAG_MASKED = 0x80;   // Payload is masked
 1679
 1680    private const int CLOSURE_NORMAL = 1000;
 1681    private const int CLOSURE_SHUTDOWN = 1001;
 1682    private const int CLOSURE_PROTOCOL_ERROR = 1002;
 1683
 1684    private const string _iceProtocol = "ice.zeroc.com";
 1685    private const string _wsUUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 1686
 11687    private static readonly UTF8Encoding _utf8 = new UTF8Encoding(false, true);
 1688}