< Summary

Information
Class: Ice.Internal.WSTransceiver
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/WSTransceiver.cs
Tag: 106_26348240760
Line coverage
79%
Covered lines: 520
Uncovered lines: 132
Coverable lines: 652
Total lines: 1714
Line coverage: 79.7%
Branch coverage
79%
Covered branches: 389
Total branches: 492
Branch coverage: 79%
Method coverage
90%
Covered methods: 27
Fully covered methods: 14
Total methods: 30
Method coverage: 90%
Full method coverage: 46.6%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
fd()100%210%
initialize(...)87.1%756285.11%
closing(...)83.33%272483.33%
close()100%44100%
bind()100%210%
destroy()100%11100%
write(...)93.33%313090%
read(...)88.89%493678.38%
startRead(...)90%121073.68%
finishRead(...)90%101088.89%
startWrite(...)87.5%8881.82%
finishWrite(...)91.67%121286.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%363081.63%
handleResponse()50%592460.71%
preRead(...)65.18%55511267.19%
postRead(...)100%1010100%
preWrite(...)88.1%584279.03%
postWrite(...)78.33%1936066.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                // No extension is negotiated, so the RSV1, RSV2, and RSV3 bits must all be 0.
 968                //
 1969                if ((ch & 0x70) != 0)
 970                {
 0971                    throw new Ice.ProtocolException("invalid WebSocket frame: RSV bits must be 0");
 972                }
 973
 1974                bool finalFrame = (ch & FLAG_FINAL) == FLAG_FINAL;
 975
 976                //
 977                // Remember if last frame if we're going to read a data or
 978                // continuation frame, this is only for protocol
 979                // correctness checking purpose.
 980                //
 1981                if (_readOpCode == OP_DATA)
 982                {
 1983                    if (!_readLastFrame)
 984                    {
 0985                        throw new Ice.ProtocolException("invalid data frame, no FIN on previous frame");
 986                    }
 1987                    _readLastFrame = finalFrame;
 988                }
 1989                else if (_readOpCode == OP_CONT)
 990                {
 0991                    if (_readLastFrame)
 992                    {
 0993                        throw new Ice.ProtocolException("invalid continuation frame, previous frame FIN set");
 994                    }
 0995                    _readLastFrame = finalFrame;
 996                }
 997
 1998                ch = _readBuffer.b.get(_readBufferPos++);
 999
 1000                //
 1001                // Check the MASK bit. Messages sent by a client must be masked;
 1002                // messages sent by a server must not be masked.
 1003                //
 11004                bool masked = (ch & FLAG_MASKED) == FLAG_MASKED;
 11005                if (masked != _incoming)
 1006                {
 01007                    throw new Ice.ProtocolException("invalid masking");
 1008                }
 1009
 1010                //
 1011                // Extract the payload length, which can have the following values:
 1012                //
 1013                // 0-125: The payload length
 1014                // 126:   The subsequent two bytes contain the payload length
 1015                // 127:   The subsequent eight bytes contain the payload length
 1016                //
 11017                _readPayloadLength = ch & 0x7f;
 1018
 1019                //
 1020                // RFC 6455 section 5.5: control frames (close, ping, and pong) must not be fragmented
 1021                // and must have a payload length of 125 bytes or less - they cannot use the 16-bit
 1022                // or 64-bit extended length encoding. Enforce this before allocating any payload
 1023                // buffer.
 1024                //
 11025                if (_readOpCode == OP_CLOSE || _readOpCode == OP_PING || _readOpCode == OP_PONG)
 1026                {
 11027                    if (!finalFrame)
 1028                    {
 01029                        throw new Ice.ProtocolException("invalid WebSocket control frame: the FIN bit is not set");
 1030                    }
 11031                    if (_readPayloadLength > 125)
 1032                    {
 01033                        throw new Ice.ProtocolException(
 01034                            "invalid WebSocket control frame: the payload length exceeds 125 bytes");
 1035                    }
 1036                }
 1037
 11038                if (_readPayloadLength < 126)
 1039                {
 11040                    _readHeaderLength = 0;
 1041                }
 11042                else if (_readPayloadLength == 126)
 1043                {
 11044                    _readHeaderLength = 2; // Need to read a 16-bit payload length.
 1045                }
 1046                else
 1047                {
 11048                    _readHeaderLength = 8; // Need to read a 64-bit payload length.
 1049                }
 11050                if (masked)
 1051                {
 11052                    _readHeaderLength += 4; // Need to read a 32-bit mask.
 1053                }
 1054
 11055                _readState = ReadStateHeader;
 1056            }
 1057
 11058            if (_readState == ReadStateHeader)
 1059            {
 1060                //
 1061                // Is there enough data available to read the header?
 1062                //
 11063                if (_readHeaderLength > 0 && !readBuffered(_readHeaderLength))
 1064                {
 11065                    return true;
 1066                }
 1067
 11068                if (_readPayloadLength == 126)
 1069                {
 11070                    _readPayloadLength = _readBuffer.b.getShort(_readBufferPos); // Uses network byte order.
 11071                    if (_readPayloadLength < 0)
 1072                    {
 01073                        _readPayloadLength += 65536;
 1074                    }
 11075                    _readBufferPos += 2;
 1076                }
 11077                else if (_readPayloadLength == 127)
 1078                {
 11079                    long l = _readBuffer.b.getLong(_readBufferPos); // Uses network byte order.
 11080                    _readBufferPos += 8;
 11081                    if (l < 0 || l > int.MaxValue)
 1082                    {
 01083                        throw new Ice.ProtocolException("invalid WebSocket payload length: " + l);
 1084                    }
 11085                    _readPayloadLength = (int)l;
 1086                }
 1087
 1088                //
 1089                // Read the mask if this is an incoming connection.
 1090                //
 11091                if (_incoming)
 1092                {
 1093                    //
 1094                    // We must have needed to read the mask.
 1095                    //
 1096                    Debug.Assert(_readBuffer.b.position() - _readBufferPos >= 4);
 11097                    for (int i = 0; i < 4; ++i)
 1098                    {
 11099                        _readMask[i] = _readBuffer.b.get(_readBufferPos++); // Copy the mask.
 1100                    }
 1101                }
 1102
 11103                switch (_readOpCode)
 1104                {
 1105                    case OP_TEXT: // Text frame
 1106                    {
 01107                        throw new Ice.ProtocolException("text frames not supported");
 1108                    }
 1109                    case OP_DATA: // Data frame
 1110                    case OP_CONT: // Continuation frame
 1111                    {
 11112                        if (_instance.traceLevel() >= 2)
 1113                        {
 11114                            _instance.logger().trace(
 11115                                _instance.traceCategory(), "received " + protocol() +
 11116                                (_readOpCode == OP_DATA ? " data" : " continuation") +
 11117                                " frame with payload length of " + _readPayloadLength +
 11118                                " bytes\n" + ToString());
 1119                        }
 1120
 11121                        if (_readPayloadLength <= 0)
 1122                        {
 01123                            throw new Ice.ProtocolException("payload length is 0");
 1124                        }
 11125                        _readState = ReadStatePayload;
 1126                        Debug.Assert(buf.b.hasRemaining());
 11127                        _readFrameStart = buf.b.position();
 11128                        break;
 1129                    }
 1130                    case OP_CLOSE: // Connection close
 1131                    {
 11132                        if (_instance.traceLevel() >= 2)
 1133                        {
 11134                            _instance.logger().trace(
 11135                                _instance.traceCategory(),
 11136                                "received " + protocol() + " connection close frame\n" + ToString());
 1137                        }
 1138
 11139                        _readState = ReadStateControlFrame;
 11140                        int s = _nextState == StateOpened ? _state : _nextState;
 11141                        if (s == StateClosingRequestPending)
 1142                        {
 1143                            //
 1144                            // If we receive a close frame while we were actually
 1145                            // waiting to send one, change the role and send a
 1146                            // close frame response.
 1147                            //
 11148                            if (!_closingInitiator)
 1149                            {
 01150                                _closingInitiator = true;
 1151                            }
 11152                            if (_state == StateClosingRequestPending)
 1153                            {
 11154                                _state = StateClosingResponsePending;
 1155                            }
 1156                            else
 1157                            {
 01158                                _nextState = StateClosingResponsePending;
 1159                            }
 11160                            return false; // No longer interested in reading
 1161                        }
 1162                        else
 1163                        {
 11164                            throw new Ice.ConnectionLostException(peerAddress: null);
 1165                        }
 1166                    }
 1167                    case OP_PING:
 1168                    {
 01169                        if (_instance.traceLevel() >= 2)
 1170                        {
 01171                            _instance.logger().trace(
 01172                                _instance.traceCategory(),
 01173                                "received " + protocol() + " connection ping frame\n" + ToString());
 1174                        }
 01175                        _readState = ReadStateControlFrame;
 01176                        break;
 1177                    }
 1178                    case OP_PONG: // Pong
 1179                    {
 01180                        if (_instance.traceLevel() >= 2)
 1181                        {
 01182                            _instance.logger().trace(
 01183                                _instance.traceCategory(),
 01184                                "received " + protocol() + " connection pong frame\n" + ToString());
 1185                        }
 01186                        _readState = ReadStateControlFrame;
 01187                        break;
 1188                    }
 1189                    default:
 1190                    {
 01191                        throw new Ice.ProtocolException("unsupported opcode: " + _readOpCode);
 1192                    }
 1193                }
 1194            }
 1195
 11196            if (_readState == ReadStateControlFrame)
 1197            {
 11198                if (_readPayloadLength > 0 && !readBuffered(_readPayloadLength))
 1199                {
 01200                    return true;
 1201                }
 1202
 11203                if (_readPayloadLength > 0 && _readOpCode == OP_PING)
 1204                {
 01205                    _pingPayload = new byte[_readPayloadLength];
 01206                    System.Buffer.BlockCopy(
 01207                        _readBuffer.b.rawBytes(),
 01208                        _readBufferPos,
 01209                        _pingPayload,
 01210                        0,
 01211                        _readPayloadLength);
 1212                }
 1213
 11214                _readBufferPos += _readPayloadLength;
 11215                _readPayloadLength = 0;
 1216
 11217                if (_readOpCode == OP_PING)
 1218                {
 01219                    if (_state == StateOpened)
 1220                    {
 01221                        _state = StatePongPending; // Send pong frame now
 1222                    }
 01223                    else if (_nextState < StatePongPending)
 1224                    {
 01225                        _nextState = StatePongPending; // Send pong frame next
 1226                    }
 1227                }
 1228
 1229                //
 1230                // We've read the payload of the PING/PONG frame, we're ready
 1231                // to read a new frame.
 1232                //
 11233                _readState = ReadStateOpcode;
 1234            }
 1235
 11236            if (_readState == ReadStatePayload)
 1237            {
 1238                //
 1239                // This must be assigned before the check for the buffer. If the buffer is empty
 1240                // or already read, postRead will return false.
 1241                //
 11242                _readStart = buf.b.position();
 1243
 11244                if (buf.empty() || !buf.b.hasRemaining())
 1245                {
 01246                    return false;
 1247                }
 1248
 11249                int n = Math.Min(_readBuffer.b.position() - _readBufferPos, buf.b.remaining());
 11250                if (n > _readPayloadLength)
 1251                {
 01252                    n = _readPayloadLength;
 1253                }
 11254                if (n > 0)
 1255                {
 11256                    System.Buffer.BlockCopy(
 11257                        _readBuffer.b.rawBytes(),
 11258                        _readBufferPos,
 11259                        buf.b.rawBytes(),
 11260                        buf.b.position(),
 11261                        n);
 11262                    buf.b.position(buf.b.position() + n);
 11263                    _readBufferPos += n;
 1264                }
 1265
 1266                //
 1267                // Continue reading if we didn't read the full message, otherwise give back
 1268                // the control to the connection
 1269                //
 11270                return buf.b.hasRemaining() && n < _readPayloadLength;
 1271            }
 1272        }
 1273    }
 1274
 1275    private bool postRead(Buffer buf)
 1276    {
 11277        if (_readState != ReadStatePayload)
 1278        {
 11279            return _readStart < _readBuffer.b.position(); // Returns true if data was read.
 1280        }
 1281
 11282        if (_readStart == buf.b.position())
 1283        {
 11284            return false; // Nothing was read or nothing to read.
 1285        }
 1286        Debug.Assert(_readStart < buf.b.position());
 1287
 11288        if (_incoming)
 1289        {
 1290            //
 1291            // Unmask the data we just read.
 1292            //
 11293            int pos = buf.b.position();
 11294            byte[] arr = buf.b.rawBytes();
 11295            for (int n = _readStart; n < pos; ++n)
 1296            {
 11297                arr[n] = (byte)(arr[n] ^ _readMask[(n - _readFrameStart) % 4]);
 1298            }
 1299        }
 1300
 11301        _readPayloadLength -= buf.b.position() - _readStart;
 11302        _readStart = buf.b.position();
 11303        if (_readPayloadLength == 0)
 1304        {
 1305            //
 1306            // We've read the complete payload, we're ready to read a new frame.
 1307            //
 11308            _readState = ReadStateOpcode;
 1309        }
 11310        return buf.b.hasRemaining();
 1311    }
 1312
 1313    private bool preWrite(Buffer buf)
 1314    {
 11315        if (_writeState == WriteStateHeader)
 1316        {
 11317            if (_state == StateOpened)
 1318            {
 11319                if (buf.empty() || !buf.b.hasRemaining())
 1320                {
 11321                    return false;
 1322                }
 1323
 1324                Debug.Assert(buf.b.position() == 0);
 11325                prepareWriteHeader((byte)OP_DATA, buf.size());
 1326
 11327                _writeState = WriteStatePayload;
 1328            }
 11329            else if (_state == StatePingPending)
 1330            {
 01331                prepareWriteHeader((byte)OP_PING, 0); // Don't send any payload
 1332
 01333                _writeState = WriteStateControlFrame;
 01334                _writeBuffer.b.flip();
 1335            }
 11336            else if (_state == StatePongPending)
 1337            {
 01338                prepareWriteHeader((byte)OP_PONG, _pingPayload.Length);
 01339                if (_pingPayload.Length > _writeBuffer.b.remaining())
 1340                {
 01341                    int pos = _writeBuffer.b.position();
 01342                    _writeBuffer.resize(pos + _pingPayload.Length, false);
 01343                    _writeBuffer.b.position(pos);
 1344                }
 01345                _writeBuffer.b.put(_pingPayload);
 01346                _pingPayload = [];
 1347
 01348                _writeState = WriteStateControlFrame;
 01349                _writeBuffer.b.flip();
 1350            }
 11351            else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11352                    (_state == StateClosingResponsePending && _closingInitiator))
 1353            {
 11354                prepareWriteHeader((byte)OP_CLOSE, 2);
 1355
 1356                // Write closing reason
 11357                _writeBuffer.b.putShort((short)_closingReason);
 1358
 11359                if (!_incoming)
 1360                {
 1361                    byte b;
 11362                    int pos = _writeBuffer.b.position() - 2;
 11363                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[0]);
 11364                    _writeBuffer.b.put(pos, b);
 11365                    pos++;
 11366                    b = (byte)(_writeBuffer.b.get(pos) ^ _writeMask[1]);
 11367                    _writeBuffer.b.put(pos, b);
 1368                }
 1369
 11370                _writeState = WriteStateControlFrame;
 11371                _writeBuffer.b.flip();
 1372            }
 1373            else
 1374            {
 1375                Debug.Assert(_state != StateClosed);
 11376                return false; // Nothing to write in this state
 1377            }
 1378
 11379            _writePayloadLength = 0;
 1380        }
 1381
 11382        if (_writeState == WriteStatePayload)
 1383        {
 1384            //
 1385            // For an outgoing connection, each message must be masked with a random
 1386            // 32-bit value, so we copy the entire message into the internal buffer
 1387            // for writing. For incoming connections, we just copy the start of the
 1388            // message in the internal buffer after the header. If the message is
 1389            // larger, the reminder is sent directly from the message buffer to avoid
 1390            // copying.
 1391            //
 11392            if (!_incoming && (_writePayloadLength == 0 || !_writeBuffer.b.hasRemaining()))
 1393            {
 11394                if (!_writeBuffer.b.hasRemaining())
 1395                {
 11396                    _writeBuffer.b.position(0);
 1397                }
 1398
 11399                int n = buf.b.position();
 11400                int sz = buf.size();
 11401                int pos = _writeBuffer.b.position();
 11402                int count = Math.Min(sz - n, _writeBuffer.b.remaining());
 11403                byte[] src = buf.b.rawBytes();
 11404                byte[] dest = _writeBuffer.b.rawBytes();
 11405                for (int i = 0; i < count; ++i, ++n, ++pos)
 1406                {
 11407                    dest[pos] = (byte)(src[n] ^ _writeMask[n % 4]);
 1408                }
 11409                _writeBuffer.b.position(pos);
 11410                _writePayloadLength = n;
 1411
 11412                _writeBuffer.b.flip();
 1413            }
 11414            else if (_writePayloadLength == 0)
 1415            {
 1416                Debug.Assert(_incoming);
 11417                if (_writeBuffer.b.hasRemaining())
 1418                {
 1419                    Debug.Assert(buf.b.position() == 0);
 11420                    int n = Math.Min(_writeBuffer.b.remaining(), buf.b.remaining());
 11421                    int pos = _writeBuffer.b.position();
 11422                    System.Buffer.BlockCopy(buf.b.rawBytes(), 0, _writeBuffer.b.rawBytes(), pos, n);
 11423                    _writeBuffer.b.position(pos + n);
 11424                    _writePayloadLength = n;
 1425                }
 11426                _writeBuffer.b.flip();
 1427            }
 11428            return true;
 1429        }
 11430        else if (_writeState == WriteStateControlFrame)
 1431        {
 11432            return _writeBuffer.b.hasRemaining();
 1433        }
 1434        else
 1435        {
 1436            Debug.Assert(_writeState == WriteStateFlush);
 01437            return true;
 1438        }
 1439    }
 1440
 1441    private bool postWrite(Buffer buf, int status)
 1442    {
 11443        if (_state > StateOpened && _writeState == WriteStateControlFrame)
 1444        {
 11445            if (!_writeBuffer.b.hasRemaining())
 1446            {
 11447                if (_state == StatePingPending)
 1448                {
 01449                    if (_instance.traceLevel() >= 2)
 1450                    {
 01451                        _instance.logger().trace(
 01452                            _instance.traceCategory(),
 01453                            "sent " + protocol() + " connection ping frame\n" + ToString());
 1454                    }
 1455                }
 11456                else if (_state == StatePongPending)
 1457                {
 01458                    if (_instance.traceLevel() >= 2)
 1459                    {
 01460                        _instance.logger().trace(
 01461                            _instance.traceCategory(),
 01462                            "sent " + protocol() + " connection pong frame\n" + ToString());
 1463                    }
 1464                }
 11465                else if ((_state == StateClosingRequestPending && !_closingInitiator) ||
 11466                        (_state == StateClosingResponsePending && _closingInitiator))
 1467                {
 11468                    if (_instance.traceLevel() >= 2)
 1469                    {
 11470                        _instance.logger().trace(
 11471                            _instance.traceCategory(),
 11472                            "sent " + protocol() + " connection close frame\n" + ToString());
 1473                    }
 1474
 11475                    if (_state == StateClosingRequestPending && !_closingInitiator)
 1476                    {
 11477                        _writeState = WriteStateHeader;
 11478                        _state = StateClosingResponsePending;
 11479                        return false;
 1480                    }
 1481                    else
 1482                    {
 11483                        throw new Ice.ConnectionLostException(peerAddress: null);
 1484                    }
 1485                }
 01486                else if (_state == StateClosed)
 1487                {
 01488                    return false;
 1489                }
 1490
 01491                _state = _nextState;
 01492                _nextState = StateOpened;
 01493                _writeState = WriteStateHeader;
 1494            }
 1495            else
 1496            {
 11497                return status == SocketOperation.None;
 1498            }
 1499        }
 1500
 11501        if ((!_incoming || buf.b.position() == 0) && _writePayloadLength > 0)
 1502        {
 11503            if (!_writeBuffer.b.hasRemaining())
 1504            {
 11505                buf.b.position(_writePayloadLength);
 1506            }
 1507        }
 1508
 11509        if (status == SocketOperation.Write && !buf.b.hasRemaining() && !_writeBuffer.b.hasRemaining())
 1510        {
 1511            //
 1512            // Our buffers are empty but the delegate needs another call to write().
 1513            //
 01514            _writeState = WriteStateFlush;
 01515            return false;
 1516        }
 11517        else if (!buf.b.hasRemaining())
 1518        {
 11519            _writeState = WriteStateHeader;
 11520            if (_state == StatePingPending ||
 11521               _state == StatePongPending ||
 11522               (_state == StateClosingRequestPending && !_closingInitiator) ||
 11523               (_state == StateClosingResponsePending && _closingInitiator))
 1524            {
 11525                return true;
 1526            }
 1527        }
 11528        else if (_state == StateOpened)
 1529        {
 11530            return status == SocketOperation.None;
 1531        }
 1532
 11533        return false;
 1534    }
 1535
 1536    private bool readBuffered(int sz)
 1537    {
 11538        if (_readBufferPos == _readBuffer.b.position())
 1539        {
 11540            _readBuffer.resize(_readBufferSize, true);
 11541            _readBufferPos = 0;
 11542            _readBuffer.b.position(0);
 1543        }
 1544        else
 1545        {
 11546            int available = _readBuffer.b.position() - _readBufferPos;
 11547            if (available < sz)
 1548            {
 11549                if (_readBufferPos > 0)
 1550                {
 11551                    _readBuffer.b.limit(_readBuffer.b.position());
 11552                    _readBuffer.b.position(_readBufferPos);
 11553                    _readBuffer.b.compact();
 1554                    Debug.Assert(_readBuffer.b.position() == available);
 1555                }
 11556                _readBuffer.resize(Math.Max(_readBufferSize, sz), true);
 11557                _readBufferPos = 0;
 11558                _readBuffer.b.position(available);
 1559            }
 1560        }
 1561
 11562        _readStart = _readBuffer.b.position();
 11563        if (_readBufferPos + sz > _readBuffer.b.position())
 1564        {
 11565            return false; // Not enough read.
 1566        }
 1567        Debug.Assert(_readBuffer.b.position() > _readBufferPos);
 11568        return true;
 1569    }
 1570
 1571    private void prepareWriteHeader(byte opCode, int payloadLength)
 1572    {
 1573        //
 1574        // We need to prepare the frame header.
 1575        //
 11576        _writeBuffer.resize(_writeBufferSize, false);
 11577        _writeBuffer.b.limit(_writeBufferSize);
 11578        _writeBuffer.b.position(0);
 1579
 1580        //
 1581        // Set the opcode - this is the one and only data frame.
 1582        //
 11583        _writeBuffer.b.put((byte)(opCode | FLAG_FINAL));
 1584
 1585        //
 1586        // Set the payload length.
 1587        //
 11588        if (payloadLength <= 125)
 1589        {
 11590            _writeBuffer.b.put((byte)payloadLength);
 1591        }
 11592        else if (payloadLength > 125 && payloadLength <= 65535)
 1593        {
 1594            //
 1595            // Use an extra 16 bits to encode the payload length.
 1596            //
 11597            _writeBuffer.b.put(126);
 11598            _writeBuffer.b.putShort((short)payloadLength);
 1599        }
 11600        else if (payloadLength > 65535)
 1601        {
 1602            //
 1603            // Use an extra 64 bits to encode the payload length.
 1604            //
 11605            _writeBuffer.b.put(127);
 11606            _writeBuffer.b.putLong(payloadLength);
 1607        }
 1608
 11609        if (!_incoming)
 1610        {
 1611            //
 1612            // Add a random 32-bit mask to every outgoing frame, copy the payload data,
 1613            // and apply the mask.
 1614            //
 11615            _writeBuffer.b.put(1, (byte)(_writeBuffer.b.get(1) | FLAG_MASKED));
 11616            _rand.NextBytes(_writeMask);
 11617            _writeBuffer.b.put(_writeMask);
 1618        }
 11619    }
 1620
 1621    private ProtocolInstance _instance;
 1622    private Transceiver _delegate;
 1623    private readonly string _host;
 1624    private string _resource;
 1625    private readonly bool _incoming;
 1626
 1627    private const int StateInitializeDelegate = 0;
 1628    private const int StateConnected = 1;
 1629    private const int StateUpgradeRequestPending = 2;
 1630    private const int StateUpgradeResponsePending = 3;
 1631    private const int StateOpened = 4;
 1632    private const int StatePingPending = 5;
 1633    private const int StatePongPending = 6;
 1634    private const int StateClosingRequestPending = 7;
 1635    private const int StateClosingResponsePending = 8;
 1636    private const int StateClosed = 9;
 1637
 1638    private int _state;
 1639    private int _nextState;
 1640
 1641    private HttpParser _parser;
 1642    private string _key;
 1643
 1644    private const int ReadStateOpcode = 0;
 1645    private const int ReadStateHeader = 1;
 1646    private const int ReadStateControlFrame = 2;
 1647    private const int ReadStatePayload = 3;
 1648
 1649    private int _readState;
 1650    private Buffer _readBuffer;
 1651    private int _readBufferPos;
 1652    private int _readBufferSize;
 1653
 1654    private bool _readLastFrame;
 1655    private int _readOpCode;
 1656    private int _readHeaderLength;
 1657    private int _readPayloadLength;
 1658    private int _readStart;
 1659    private int _readFrameStart;
 1660    private byte[] _readMask;
 1661
 1662    private const int WriteStateHeader = 0;
 1663    private const int WriteStatePayload = 1;
 1664    private const int WriteStateControlFrame = 2;
 1665    private const int WriteStateFlush = 3;
 1666
 1667    private int _writeState;
 1668    private Buffer _writeBuffer;
 1669    private int _writeBufferSize;
 1670    private byte[] _writeMask;
 1671    private int _writePayloadLength;
 1672
 1673    private bool _closingInitiator;
 1674    private int _closingReason;
 1675
 1676    private bool _readPending;
 1677    private bool _finishRead;
 1678    private bool _writePending;
 1679
 1680    private byte[] _pingPayload;
 1681
 1682    private Random _rand;
 1683
 1684    //
 1685    // WebSocket opcodes
 1686    //
 1687    private const int OP_CONT = 0x0;    // Continuation frame
 1688    private const int OP_TEXT = 0x1;    // Text frame
 1689    private const int OP_DATA = 0x2;    // Data frame
 1690    // private const int OP_RES_0x3 = 0x3;    // Reserved
 1691    // private const int OP_RES_0x4 = 0x4;    // Reserved
 1692    // private const int OP_RES_0x5 = 0x5;    // Reserved
 1693    // private const int OP_RES_0x6 = 0x6;    // Reserved
 1694    // private const int OP_RES_0x7 = 0x7;    // Reserved
 1695    private const int OP_CLOSE = 0x8;    // Connection close
 1696    private const int OP_PING = 0x9;    // Ping
 1697    private const int OP_PONG = 0xA;    // Pong
 1698    // private const int OP_RES_0xB = 0xB;    // Reserved
 1699    // private const int OP_RES_0xC = 0xC;    // Reserved
 1700    // private const int OP_RES_0xD = 0xD;    // Reserved
 1701    // private const int OP_RES_0xE = 0xE;    // Reserved
 1702    // private const int OP_RES_0xF = 0xF;    // Reserved
 1703    private const int FLAG_FINAL = 0x80;   // Last frame
 1704    private const int FLAG_MASKED = 0x80;   // Payload is masked
 1705
 1706    private const int CLOSURE_NORMAL = 1000;
 1707    private const int CLOSURE_SHUTDOWN = 1001;
 1708    private const int CLOSURE_PROTOCOL_ERROR = 1002;
 1709
 1710    private const string _iceProtocol = "ice.zeroc.com";
 1711    private const string _wsUUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
 1712
 11713    private static readonly UTF8Encoding _utf8 = new UTF8Encoding(false, true);
 1714}