< Summary

Information
Class: Ice.Internal.WSTransceiver
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/WSTransceiver.cs
Tag: 99_23991109993
Line coverage
80%
Covered lines: 515
Uncovered lines: 128
Coverable lines: 643
Total lines: 1684
Line coverage: 80%
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%58.942460.71%
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)

/_/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            throw new WebSocketException("missing value for Sec-WebSocket-Accept");
 1933        string input = _key + _wsUUID;
 934#pragma warning disable CA5350 // SHA1 is used for compatibility with the WebSocket protocol
 1935        using var sha1 = SHA1.Create();
 1936        byte[] hash = sha1.ComputeHash(_utf8.GetBytes(input));
 937#pragma warning restore CA5350
 1938        if (!val.Equals(Convert.ToBase64String(hash), StringComparison.Ordinal))
 939        {
 0940            throw new WebSocketException("invalid value `" + val + "' for Sec-WebSocket-Accept");
 941        }
 1942    }
 943
 944    private bool preRead(Buffer buf)
 945    {
 946        while (true)
 947        {
 1948            if (_readState == ReadStateOpcode)
 949            {
 950                //
 951                // Is there enough data available to read the opcode?
 952                //
 1953                if (!readBuffered(2))
 954                {
 1955                    return true;
 956                }
 957
 958                //
 959                // Most-significant bit indicates whether this is the
 960                // last frame. Least-significant four bits hold the
 961                // opcode.
 962                //
 1963                int ch = _readBuffer.b.get(_readBufferPos++);
 1964                _readOpCode = ch & 0xf;
 965
 966                //
 967                // Remember if last frame if we're going to read a data or
 968                // continuation frame, this is only for protocol
 969                // correctness checking purpose.
 970                //
 1971                if (_readOpCode == OP_DATA)
 972                {
 1973                    if (!_readLastFrame)
 974                    {
 0975                        throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame");
 976                    }
 1977                    _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
 978                }
 1979                else if (_readOpCode == OP_CONT)
 980                {
 0981                    if (_readLastFrame)
 982                    {
 0983                        throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set");
 984                    }
 0985                    _readLastFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
 986                }
 987
 1988                ch = _readBuffer.b.get(_readBufferPos++);
 989
 990                //
 991                // Check the MASK bit. Messages sent by a client must be masked;
 992                // messages sent by a server must not be masked.
 993                //
 1994                bool masked = (ch & FLAG_MASKED) == FLAG_MASKED;
 1995                if (masked != _incoming)
 996                {
 0997                    throw new Ice.ProtocolException("invalid masking");
 998                }
 999
 1000                //
 1001                // Extract the payload length, which can have the following values:
 1002                //
 1003                // 0-125: The payload length
 1004                // 126:   The subsequent two bytes contain the payload length
 1005                // 127:   The subsequent eight bytes contain the payload length
 1006                //
 11007                _readPayloadLength = ch & 0x7f;
 11008                if (_readPayloadLength < 126)
 1009                {
 11010                    _readHeaderLength = 0;
 1011                }
 11012                else if (_readPayloadLength == 126)
 1013                {
 11014                    _readHeaderLength = 2; // Need to read a 16-bit payload length.
 1015                }
 1016                else
 1017                {
 11018                    _readHeaderLength = 8; // Need to read a 64-bit payload length.
 1019                }
 11020                if (masked)
 1021                {
 11022                    _readHeaderLength += 4; // Need to read a 32-bit mask.
 1023                }
 1024
 11025                _readState = ReadStateHeader;
 1026            }
 1027
 11028            if (_readState == ReadStateHeader)
 1029            {
 1030                //
 1031                // Is there enough data available to read the header?
 1032                //
 11033                if (_readHeaderLength > 0 && !readBuffered(_readHeaderLength))
 1034                {
 11035                    return true;
 1036                }
 1037
 11038                if (_readPayloadLength == 126)
 1039                {
 11040                    _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order.
 11041                    if (_readPayloadLength < 0)
 1042                    {
 01043                        _readPayloadLength += 65536;
 1044                    }
 11045                    _readBufferPos += 2;
 1046                }
 11047                else if (_readPayloadLength == 127)
 1048                {
 11049                    long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order.
 11050                    _readBufferPos += 8;
 11051                    if (l < 0 || l > int.MaxValue)
 1052                    {
 01053                        throw new Ice.ProtocolException("invalid WebSocket payload length: " + l);
 1054                    }
 11055                    _readPayloadLength = (int)l;
 1056                }
 1057
 1058                //
 1059                // Read the mask if this is an incoming connection.
 1060                //
 11061                if (_incoming)
 1062                {
 1063                    //
 1064                    // We must have needed to read the mask.
 1065                    //
 1066                    Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4);
 11067                    for (int i = 0; i < 4; ++i)
 1068                    {
 11069                        _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask.
 1070                    }
 1071                }
 1072
 11073                switch (_readOpCode)
 1074                {
 1075                    case OP_TEXT: // Text frame
 1076                    {
 01077                        throw new Ice.ProtocolException("text frames not supported");
 1078                    }
 1079                    case OP_DATA: // Data frame
 1080                    case OP_CONT: // Continuation frame
 1081                    {
 11082                        if (_instance.traceLevel() >= 2)
 1083                        {
 11084                            _instance.logger().trace(
 11085                                _instance.traceCategory(), "received " + protocol() +
 11086                                (_readOpCode == OP_DATA ? " data" : " continuation") +
 11087                                " frame with payload length of " + _readPayloadLength +
 11088                                " bytes\n" + ToString());
 1089                        }
 1090
 11091                        if (_readPayloadLength <= 0)
 1092                        {
 01093                            throw new Ice.ProtocolException("payload length is 0");
 1094                        }
 11095                        _readState = ReadStatePayload;
 1096                        Debug.Assert(buf.b.hasRemaining());
 11097                        _readFrameStart = buf.b.position();
 11098                        break;
 1099                    }
 1100                    case OP_CLOSE: // Connection close
 1101                    {
 11102                        if (_instance.traceLevel() >= 2)
 1103                        {
 11104                            _instance.logger().trace(
 11105                                _instance.traceCategory(),
 11106                                "received " + protocol() + " connection close frame\n" + ToString());
 1107                        }
 1108
 11109                        _readState = ReadStateControlFrame;
 11110                        int s = _nextState == StateOpened ? _state : _nextState;
 11111                        if (s == StateClosingRequestPending)
 1112                        {
 1113                            //
 1114                            // If we receive a close frame while we were actually
 1115                            // waiting to send one, change the role and send a
 1116                            // close frame response.
 1117                            //
 11118                            if (!_closingInitiator)
 1119                            {
 01120                                _closingInitiator = true;
 1121                            }
 11122                            if (_state == StateClosingRequestPending)
 1123                            {
 11124                                _state = StateClosingResponsePending;
 1125                            }
 1126                            else
 1127                            {
 01128                                _nextState = StateClosingResponsePending;
 1129                            }
 11130                            return false; // No longer interested in reading
 1131                        }
 1132                        else
 1133                        {
 11134                            throw new Ice.ConnectionLostException(peerAddress: null);
 1135                        }
 1136                    }
 1137                    case OP_PING:
 1138                    {
 01139                        if (_instance.traceLevel() >= 2)
 1140                        {
 01141                            _instance.logger().trace(
 01142                                _instance.traceCategory(),
 01143                                "received " + protocol() + " connection ping frame\n" + ToString());
 1144                        }
 01145                        _readState = ReadStateControlFrame;
 01146                        break;
 1147                    }
 1148                    case OP_PONG: // Pong
 1149                    {
 01150                        if (_instance.traceLevel() >= 2)
 1151                        {
 01152                            _instance.logger().trace(
 01153                                _instance.traceCategory(),
 01154                                "received " + protocol() + " connection pong frame\n" + ToString());
 1155                        }
 01156                        _readState = ReadStateControlFrame;
 01157                        break;
 1158                    }
 1159                    default:
 1160                    {
 01161                        throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode);
 1162                    }
 1163                }
 1164            }
 1165
 11166            if (_readState == ReadStateControlFrame)
 1167            {
 11168                if (_readPayloadLength > 0 && !readBuffered(_readPayloadLength))
 1169                {
 01170                    return true;
 1171                }
 1172
 11173                if (_readPayloadLength > 0 && _readOpCode == OP_PING)
 1174                {
 01175                    _pingPayload = new byte[_readPayloadLength];
 01176                    System.Buffer.BlockCopy(
 01177                        _readBuffer.b.rawBytes(),
 01178                        _readBufferPos,
 01179                        _pingPayload,
 01180                        0,
 01181                        _readPayloadLength);
 1182                }
 1183
 11184                _readBufferPos += _readPayloadLength;
 11185                _readPayloadLength = 0;
 1186
 11187                if (_readOpCode == OP_PING)
 1188                {
 01189                    if (_state == StateOpened)
 1190                    {
 01191                        _state = StatePongPending; // Send pong frame now
 1192                    }
 01193                    else if (_nextState < StatePongPending)
 1194                    {
 01195                        _nextState = StatePongPending; // Send pong frame next
 1196                    }
 1197                }
 1198
 1199                //
 1200                // We've read the payload of the PING/PONG frame, we're ready
 1201                // to read a new frame.
 1202                //
 11203                _readState = ReadStateOpcode;
 1204            }
 1205
 11206            if (_readState == ReadStatePayload)
 1207            {
 1208                //
 1209                // This must be assigned before the check for the buffer. If the buffer is empty
 1210                // or already read, postRead will return false.
 1211                //
 11212                _readStart = buf.b.position();
 1213
 11214                if (buf.empty() || !buf.b.hasRemaining())
 1215                {
 01216                    return false;
 1217                }
 1218
 11219                int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining());
 11220                if (n > _readPayloadLength)
 1221                {
 01222                    n = _readPayloadLength;
 1223                }
 11224                if (n > 0)
 1225                {
 11226                    System.Buffer.BlockCopy(
 11227                        _readBuffer.b.rawBytes(),
 11228                        _readBufferPos,
 11229                        buf.b.rawBytes(),
 11230                        buf.b.position(),
 11231                        n);
 11232                    buf.b.position(buf.b.position() + n);
 11233                    _readBufferPos += n;
 1234                }
 1235
 1236                //
 1237                // Continue reading if we didn't read the full message, otherwise give back
 1238                // the control to the connection
 1239                //
 11240                return buf.b.hasRemaining() && n < _readPayloadLength;
 1241            }
 1242        }
 1243    }
 1244
 1245    private bool postRead(Buffer buf)
 1246    {
 11247        if (_readState != ReadStatePayload)
 1248        {
 11249            return _readStart < _readBuffer.b.position(); // Returns true if data was read.
 1250        }
 1251
 11252        if (_readStart == buf.b.position())
 1253        {
 11254            return false; // Nothing was read or nothing to read.
 1255        }
 1256        Debug.Assert(_readStart < buf.b.position());
 1257
 11258        if (_incoming)
 1259        {
 1260            //
 1261            // Unmask the data we just read.
 1262            //
 11263            int pos = buf.b.position();
 11264            byte[] arr = buf.b.rawBytes();
 11265            for (int n = _readStart; n < pos; ++n)
 1266            {
 11267                arr[n] = (byte)(arr[n] ^ _readMask[(n - _readFrameStart) % 4]);
 1268            }
 1269        }
 1270
 11271        _readPayloadLength -= buf.b.position() - _readStart;
 11272        _readStart = buf.b.position();
 11273        if (_readPayloadLength == 0)
 1274        {
 1275            //
 1276            // We've read the complete payload, we're ready to read a new frame.
 1277            //
 11278            _readState = ReadStateOpcode;
 1279        }
 11280        return buf.b.hasRemaining();
 1281    }
 1282
 1283    private bool preWrite(Buffer buf)
 1284    {
 11285        if (_writeState == WriteStateHeader)
 1286        {
 11287            if (_state == StateOpened)
 1288            {
 11289                if (buf.empty() || !buf.b.hasRemaining())
 1290                {
 11291                    return false;
 1292                }
 1293
 1294                Debug.Assert(buf.b.position() == 0);
 11295                prepareWriteHeader((byte)OP_DATA, buf.size());
 1296
 11297                _writeState = WriteStatePayload;
 1298            }
 11299            else if (_state == StatePingPending)
 1300            {
 01301                prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload
 1302
 01303                _writeState = WriteStateControlFrame;
 01304                _writeBuffer.b.flip();
 1305            }
 11306            else if (_state == StatePongPending)
 1307            {
 01308                prepareWriteHeader((byte)OP_PONG, _pingPayload.Length);
 01309                if (_pingPayload.Length > _writeBuffer.b.remaining())
 1310                {
 01311                    int pos = _writeBuffer.b.position();
 01312                    _writeBuffer.resize(pos + _pingPayload.Length, false);
 01313                    _writeBuffer.b.position(pos);
 1314                }
 01315                _writeBuffer.b.put(_pingPayload);
 01316                _pingPayload = [];
 1317
 01318                _writeState = WriteStateControlFrame;
 01319                _writeBuffer.b.flip();
 1320            }
 11321            else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11322                    (_state == StateClosingResponsePending && _closingInitiator))
 1323            {
 11324                prepareWriteHeader((byte)OP_CLOSE, 2);
 1325
 1326                // Write closing reason
 11327                _writeBuffer.b.putShort((short)_closingReason);
 1328
 11329                if (!_incoming)
 1330                {
 1331                    byte b;
 11332                    int pos = _writeBuffer.b.position() - 2;
 11333                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]);
 11334                    _writeBuffer.b.put(pos, b);
 11335                    pos++;
 11336                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]);
 11337                    _writeBuffer.b.put(pos, b);
 1338                }
 1339
 11340                _writeState = WriteStateControlFrame;
 11341                _writeBuffer.b.flip();
 1342            }
 1343            else
 1344            {
 1345                Debug.Assert(_state != StateClosed);
 11346                return false; // Nothing to write in this state
 1347            }
 1348
 11349            _writePayloadLength = 0;
 1350        }
 1351
 11352        if (_writeState == WriteStatePayload)
 1353        {
 1354            //
 1355            // For an outgoing connection, each message must be masked with a random
 1356            // 32-bit value, so we copy the entire message into the internal buffer
 1357            // for writing. For incoming connections, we just copy the start of the
 1358            // message in the internal buffer after the header. If the message is
 1359            // larger, the reminder is sent directly from the message buffer to avoid
 1360            // copying.
 1361            //
 11362            if (!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining()))
 1363            {
 11364                if (!_writeBuffer.b.hasRemaining())
 1365                {
 11366                    _writeBuffer.b.position(0);
 1367                }
 1368
 11369                int n = buf.b.position();
 11370                int sz = buf.size();
 11371                int pos = _writeBuffer.b.position();
 11372                int count = Math.Min(sz - n, _writeBuffer.b.remaining());
 11373                byte[] src = buf.b.rawBytes();
 11374                byte[] dest = _writeBuffer.b.rawBytes();
 11375                for (int i = 0; i < count; ++i, ++n, ++pos)
 1376                {
 11377                    dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]);
 1378                }
 11379                _writeBuffer.b.position(pos);
 11380                _writePayloadLength = n;
 1381
 11382                _writeBuffer.b.flip();
 1383            }
 11384            else if (_writePayloadLength == 0)
 1385            {
 1386                Debug.Assert(_incoming);
 11387                if (_writeBuffer.b.hasRemaining())
 1388                {
 1389                    Debug.Assert(buf.b.position() == 0);
 11390                    int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining());
 11391                    int pos = _writeBuffer.b.position();
 11392                    System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n);
 11393                    _writeBuffer.b.position(pos + n);
 11394                    _writePayloadLength = n;
 1395                }
 11396                _writeBuffer.b.flip();
 1397            }
 11398            return true;
 1399        }
 11400        else if (_writeState == WriteStateControlFrame)
 1401        {
 11402            return _writeBuffer.b.hasRemaining();
 1403        }
 1404        else
 1405        {
 1406            Debug.Assert(_writeState == WriteStateFlush);
 01407            return true;
 1408        }
 1409    }
 1410
 1411    private bool postWrite(Buffer buf, int status)
 1412    {
 11413        if (_state > StateOpened && _writeState == WriteStateControlFrame)
 1414        {
 11415            if (!_writeBuffer.b.hasRemaining())
 1416            {
 11417                if (_state == StatePingPending)
 1418                {
 01419                    if (_instance.traceLevel() >= 2)
 1420                    {
 01421                        _instance.logger().trace(
 01422                            _instance.traceCategory(),
 01423                            "sent " + protocol() + " connection ping frame\n" + ToString());
 1424                    }
 1425                }
 11426                else if (_state == StatePongPending)
 1427                {
 01428                    if (_instance.traceLevel() >= 2)
 1429                    {
 01430                        _instance.logger().trace(
 01431                            _instance.traceCategory(),
 01432                            "sent " + protocol() + " connection pong frame\n" + ToString());
 1433                    }
 1434                }
 11435                else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11436                        (_state == StateClosingResponsePending && _closingInitiator))
 1437                {
 11438                    if (_instance.traceLevel() >= 2)
 1439                    {
 11440                        _instance.logger().trace(
 11441                            _instance.traceCategory(),
 11442                            "sent " + protocol() + " connection close frame\n" + ToString());
 1443                    }
 1444
 11445                    if (_state == StateClosingRequestPending && !_closingInitiator)
 1446                    {
 11447                        _writeState = WriteStateHeader;
 11448                        _state = StateClosingResponsePending;
 11449                        return false;
 1450                    }
 1451                    else
 1452                    {
 11453                        throw new Ice.ConnectionLostException(peerAddress: null);
 1454                    }
 1455                }
 01456                else if (_state == StateClosed)
 1457                {
 01458                    return false;
 1459                }
 1460
 01461                _state = _nextState;
 01462                _nextState = StateOpened;
 01463                _writeState = WriteStateHeader;
 1464            }
 1465            else
 1466            {
 11467                return status == SocketOperation.None;
 1468            }
 1469        }
 1470
 11471        if ((!_incoming || buf.b.position() == 0) && _writePayloadLength > 0)
 1472        {
 11473            if (!_writeBuffer.b.hasRemaining())
 1474            {
 11475                buf.b.position(_writePayloadLength);
 1476            }
 1477        }
 1478
 11479        if (status == SocketOperation.Write && !buf.b.hasRemaining() && !_writeBuffer.b.hasRemaining())
 1480        {
 1481            //
 1482            // Our buffers are empty but the delegate needs another call to write().
 1483            //
 01484            _writeState = WriteStateFlush;
 01485            return false;
 1486        }
 11487        else if (!buf.b.hasRemaining())
 1488        {
 11489            _writeState = WriteStateHeader;
 11490            if (_state == StatePingPending ||
 11491               _state == StatePongPending ||
 11492               (_state == StateClosingRequestPending && !_closingInitiator) ||
 11493               (_state == StateClosingResponsePending && _closingInitiator))
 1494            {
 11495                return true;
 1496            }
 1497        }
 11498        else if (_state == StateOpened)
 1499        {
 11500            return status == SocketOperation.None;
 1501        }
 1502
 11503        return false;
 1504    }
 1505
 1506    private bool readBuffered(int sz)
 1507    {
 11508        if (_readBufferPos == _readBuffer.b.position())
 1509        {
 11510            _readBuffer.resize(_readBufferSize, true);
 11511            _readBufferPos = 0;
 11512            _readBuffer.b.position(0);
 1513        }
 1514        else
 1515        {
 11516            int available = _readBuffer.b.position() - _readBufferPos;
 11517            if (available < sz)
 1518            {
 11519                if (_readBufferPos > 0)
 1520                {
 11521                    _readBuffer.b.limit(_readBuffer.b.position());
 11522                    _readBuffer.b.position(_readBufferPos);
 11523                    _readBuffer.b.compact();
 1524                    Debug.Assert(_readBuffer.b.position() == available);
 1525                }
 11526                _readBuffer.resize(Math.Max(_readBufferSize, sz), true);
 11527                _readBufferPos = 0;
 11528                _readBuffer.b.position(available);
 1529            }
 1530        }
 1531
 11532        _readStart = _readBuffer.b.position();
 11533        if (_readBufferPos + sz > _readBuffer.b.position())
 1534        {
 11535            return false; // Not enough read.
 1536        }
 1537        Debug.Assert(_readBuffer.b.position() > _readBufferPos);
 11538        return true;
 1539    }
 1540
 1541    private void prepareWriteHeader(byte opCode, int payloadLength)
 1542    {
 1543        //
 1544        // We need to prepare the frame header.
 1545        //
 11546        _writeBuffer.resize(_writeBufferSize, false);
 11547        _writeBuffer.b.limit(_writeBufferSize);
 11548        _writeBuffer.b.position(0);
 1549
 1550        //
 1551        // Set the opcode - this is the one and only data frame.
 1552        //
 11553        _writeBuffer.b.put((byte)(opCode | FLAG_FINAL));
 1554
 1555        //
 1556        // Set the payload length.
 1557        //
 11558        if (payloadLength <= 125)
 1559        {
 11560            _writeBuffer.b.put((byte)payloadLength);
 1561        }
 11562        else if (payloadLength > 125 && payloadLength <= 65535)
 1563        {
 1564            //
 1565            // Use an extra 16 bits to encode the payload length.
 1566            //
 11567            _writeBuffer.b.put(126);
 11568            _writeBuffer.b.putShort((short)payloadLength);
 1569        }
 11570        else if (payloadLength > 65535)
 1571        {
 1572            //
 1573            // Use an extra 64 bits to encode the payload length.
 1574            //
 11575            _writeBuffer.b.put(127);
 11576            _writeBuffer.b.putLong(payloadLength);
 1577        }
 1578
 11579        if (!_incoming)
 1580        {
 1581            //
 1582            // Add a random 32-bit mask to every outgoing frame, copy the payload data,
 1583            // and apply the mask.
 1584            //
 11585            _writeBuffer.b.put(1, (byte)(_writeBuffer.b.get(1) | FLAG_MASKED));
 11586            _rand.NextBytes(_writeMask);
 11587            _writeBuffer.b.put(_writeMask);
 1588        }
 11589    }
 1590
 1591    private ProtocolInstance _instance;
 1592    private Transceiver _delegate;
 1593    private readonly string _host;
 1594    private string _resource;
 1595    private readonly bool _incoming;
 1596
 1597    private const int StateInitializeDelegate = 0;
 1598    private const int StateConnected = 1;
 1599    private const int StateUpgradeRequestPending = 2;
 1600    private const int StateUpgradeResponsePending = 3;
 1601    private const int StateOpened = 4;
 1602    private const int StatePingPending = 5;
 1603    private const int StatePongPending = 6;
 1604    private const int StateClosingRequestPending = 7;
 1605    private const int StateClosingResponsePending = 8;
 1606    private const int StateClosed = 9;
 1607
 1608    private int _state;
 1609    private int _nextState;
 1610
 1611    private HttpParser _parser;
 1612    private string _key;
 1613
 1614    private const int ReadStateOpcode = 0;
 1615    private const int ReadStateHeader = 1;
 1616    private const int ReadStateControlFrame = 2;
 1617    private const int ReadStatePayload = 3;
 1618
 1619    private int _readState;
 1620    private Buffer _readBuffer;
 1621    private int _readBufferPos;
 1622    private int _readBufferSize;
 1623
 1624    private bool _readLastFrame;
 1625    private int _readOpCode;
 1626    private int _readHeaderLength;
 1627    private int _readPayloadLength;
 1628    private int _readStart;
 1629    private int _readFrameStart;
 1630    private byte[] _readMask;
 1631
 1632    private const int WriteStateHeader = 0;
 1633    private const int WriteStatePayload = 1;
 1634    private const int WriteStateControlFrame = 2;
 1635    private const int WriteStateFlush = 3;
 1636
 1637    private int _writeState;
 1638    private Buffer _writeBuffer;
 1639    private int _writeBufferSize;
 1640    private byte[] _writeMask;
 1641    private int _writePayloadLength;
 1642
 1643    private bool _closingInitiator;
 1644    private int _closingReason;
 1645
 1646    private bool _readPending;
 1647    private bool _finishRead;
 1648    private bool _writePending;
 1649
 1650    private byte[] _pingPayload;
 1651
 1652    private Random _rand;
 1653
 1654    //
 1655    // WebSocket opcodes
 1656    //
 1657    private const int OP_CONT = 0x0;    // Continuation frame
 1658    private const int OP_TEXT = 0x1;    // Text frame
 1659    private const int OP_DATA = 0x2;    // Data frame
 1660    // private const int OP_RES_0x3 = 0x3;    // Reserved
 1661    // private const int OP_RES_0x4 = 0x4;    // Reserved
 1662    // private const int OP_RES_0x5 = 0x5;    // Reserved
 1663    // private const int OP_RES_0x6 = 0x6;    // Reserved
 1664    // private const int OP_RES_0x7 = 0x7;    // Reserved
 1665    private const int OP_CLOSE = 0x8;    // Connection close
 1666    private const int OP_PING = 0x9;    // Ping
 1667    private const int OP_PONG = 0xA;    // Pong
 1668    // private const int OP_RES_0xB = 0xB;    // Reserved
 1669    // private const int OP_RES_0xC = 0xC;    // Reserved
 1670    // private const int OP_RES_0xD = 0xD;    // Reserved
 1671    // private const int OP_RES_0xE = 0xE;    // Reserved
 1672    // private const int OP_RES_0xF = 0xF;    // Reserved
 1673    private const int FLAG_FINAL = 0x80;   // Last frame
 1674    private const int FLAG_MASKED = 0x80;   // Payload is masked
 1675
 1676    private const int CLOSURE_NORMAL = 1000;
 1677    private const int CLOSURE_SHUTDOWN = 1001;
 1678    private const int CLOSURE_PROTOCOL_ERROR = 1002;
 1679
 1680    private const string _iceProtocol = "ice.zeroc.com";
 1681    private const string _wsUUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 1682
 11683    private static readonly UTF8Encoding _utf8 = new UTF8Encoding(false, true);
 1684}