LCOV - code coverage report
Current view: top level - extra/webserver/webserver/websocket - SPWebWebsocket.cc (source / functions) Hit Total Coverage
Test: coverage.info Lines: 134 280 47.9 %
Date: 2024-05-12 00:16:13 Functions: 16 27 59.3 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #include "SPWebWebsocket.h"
      24             : #include "SPWebRoot.h"
      25             : 
      26             : namespace STAPPLER_VERSIONIZED stappler::web {
      27             : 
      28        1375 : uint8_t WebsocketFrameWriter::getOpcodeFromType(WebsocketFrameType opcode) {
      29        1375 :         switch (opcode) {
      30           0 :         case WebsocketFrameType::Continue: return 0x0; break;
      31        1350 :         case WebsocketFrameType::Text: return 0x1; break;
      32           0 :         case WebsocketFrameType::Binary: return 0x2; break;
      33          25 :         case WebsocketFrameType::Close: return 0x8; break;
      34           0 :         case WebsocketFrameType::Ping: return 0x9; break;
      35           0 :         case WebsocketFrameType::Pong: return 0xA; break;
      36           0 :         default: break;
      37             :         }
      38           0 :         return 0;
      39             : }
      40             : 
      41        1375 : WebsocketFrameType WebsocketFrameReader::getTypeFromOpcode(uint8_t opcode) {
      42        1375 :         switch (opcode) {
      43           0 :         case 0x0: return WebsocketFrameType::Continue; break;
      44        1350 :         case 0x1: return WebsocketFrameType::Text; break;
      45           0 :         case 0x2: return WebsocketFrameType::Binary; break;
      46          25 :         case 0x8: return WebsocketFrameType::Close; break;
      47           0 :         case 0x9: return WebsocketFrameType::Ping; break;
      48           0 :         case 0xA: return WebsocketFrameType::Pong; break;
      49             :         }
      50           0 :         return WebsocketFrameType::None;
      51             : }
      52             : 
      53        1375 : bool WebsocketFrameReader::isControlFrameType(WebsocketFrameType t) {
      54        1375 :         switch (t) {
      55          25 :         case WebsocketFrameType::Close:
      56             :         case WebsocketFrameType::Continue:
      57             :         case WebsocketFrameType::Ping:
      58             :         case WebsocketFrameType::Pong:
      59          25 :                 return true;
      60             :                 break;
      61        1350 :         default:
      62        1350 :                 return false;
      63             :                 break;
      64             :         }
      65             :         return false;
      66             : }
      67             : 
      68        1375 : size_t WebsocketFrameWriter::getFrameSize(size_t dataSize, bool masked) {
      69        1375 :         size_t sizeSize = (dataSize <= 125) ? 0 : ((dataSize > (size_t)maxOf<uint16_t>())? 8 : 2);
      70        1375 :         size_t frameSize = 2 + sizeSize;
      71        1375 :         if (masked) {
      72        1375 :                 frameSize += 4;
      73             :         }
      74        1375 :         return frameSize + dataSize;
      75             : }
      76             : 
      77        1375 : size_t WebsocketFrameWriter::makeHeader(uint8_t *buf, size_t dataSize, WebsocketFrameType t, bool masked, uint32_t mask) {
      78        1375 :         size_t sizeSize = (dataSize <= 125) ? 0 : ((dataSize > (size_t)maxOf<uint16_t>())? 8 : 2);
      79        1375 :         size_t frameSize = 2 + sizeSize;
      80             : 
      81        1375 :         buf[0] = ((uint8_t)0b10000000 | getOpcodeFromType(t));
      82        1375 :         if (sizeSize == 0) {
      83        1375 :                 buf[1] = ((uint8_t)dataSize);
      84           0 :         } else if (sizeSize == 2) {
      85           0 :                 buf[1] = ((uint8_t)126);
      86           0 :                 uint16_t size = byteorder::HostToNetwork((uint16_t)dataSize);
      87           0 :                 memcpy(buf + 2, &size, sizeof(uint16_t));
      88           0 :         } else if (sizeSize == 8) {
      89           0 :                 buf[1] = ((uint8_t)127);
      90           0 :                 uint64_t size = byteorder::HostToNetwork((uint64_t)dataSize);
      91           0 :                 memcpy(buf + 2, &size, sizeof(uint64_t));
      92             :         }
      93        1375 :         if (masked) {
      94        1375 :                 mask = byteorder::HostToNetwork((uint64_t)mask);
      95        1375 :                 memcpy(buf + frameSize, &mask, sizeof(uint32_t));
      96        1375 :                 buf[1] |= uint8_t(0b10000000);
      97        1375 :                 frameSize += 4;
      98             :         }
      99             : 
     100        1375 :         return frameSize;
     101             : }
     102             : 
     103           0 : void WebsocketFrameWriter::makeHeader(StackBuffer<32> &buf, size_t dataSize, WebsocketFrameType t, bool masked, uint32_t mask) {
     104           0 :         size_t sizeSize = (dataSize <= 125) ? 0 : ((dataSize > (size_t)maxOf<uint16_t>())? 8 : 2);
     105           0 :         size_t frameSize = 2 + sizeSize;
     106           0 :         if (masked) {
     107           0 :                 frameSize += 4;
     108             :         }
     109             : 
     110           0 :         makeHeader(buf.prepare(frameSize), dataSize, t);
     111           0 :         buf.save(nullptr, frameSize);
     112           0 : }
     113             : 
     114        1350 : void WebsocketFrameReader::unmask(uint32_t mask, size_t offset, uint8_t *data, size_t nbytes) {
     115        1350 :         uint8_t j = offset % 4;
     116       23175 :         for (size_t i = 0; i < nbytes; ++i, ++j) {
     117       21825 :                 if (j >= 4) { j = 0; }
     118       21825 :                 data[i] ^= ((mask >> (j * 8)) & 0xFF);
     119             :         }
     120        1350 : }
     121             : 
     122        1400 : WebsocketFrameReader::WebsocketFrameReader(Root *r, pool_t *p)
     123        2800 : : frame(Frame{false, WebsocketFrameType::None, Bytes(), 0, 0})
     124        1400 : , pool(memory::pool::create(p)), root(r) {
     125        1400 :         if (!pool) {
     126           0 :                 error = Error::NotInitialized;
     127             :         } else {
     128        1400 :                 new (&frame.buffer) Bytes(pool); // switch allocator
     129             :         }
     130        1400 : }
     131             : 
     132       10925 : size_t WebsocketFrameReader::getRequiredBytes() const {
     133       10925 :         switch (status) {
     134        2750 :         case Status::Head: return getBufferRequiredBytes(buffer, 2); break;
     135           0 :         case Status::Size16: return getBufferRequiredBytes(buffer, 2); break;
     136           0 :         case Status::Size64: return getBufferRequiredBytes(buffer, 8); break;
     137        2750 :         case Status::Mask: return getBufferRequiredBytes(buffer, 4); break;
     138        5400 :         case Status::Body: return (frame.offset < size) ? (size - frame.offset) : 0; break;
     139          25 :         case Status::Control: return getBufferRequiredBytes(buffer, size); break;
     140           0 :         default: break;
     141             :         }
     142           0 :         return 0;
     143             : }
     144             : 
     145        4100 : uint8_t * WebsocketFrameReader::prepare(size_t &len) {
     146        4100 :         switch (status) {
     147        2750 :         case Status::Head:
     148             :         case Status::Size16:
     149             :         case Status::Size64:
     150             :         case Status::Mask:
     151             :         case Status::Control:
     152        2750 :                 return buffer.prepare_preserve(len); break;
     153        1350 :         case Status::Body:
     154        1350 :                 return frame.buffer.data() + frame.block + frame.offset; break;
     155           0 :         default: break;
     156             :         }
     157           0 :         return nullptr;
     158             : }
     159             : 
     160        4100 : bool WebsocketFrameReader::save(uint8_t *b, size_t nbytes) {
     161        4100 :         switch (status) {
     162        2750 :         case Status::Head:
     163             :         case Status::Size16:
     164             :         case Status::Size64:
     165             :         case Status::Mask:
     166             :         case Status::Control:
     167        2750 :                 buffer.save(b, nbytes); break;
     168        1350 :         case Status::Body:
     169        1350 :                 unmask(mask, frame.offset, b, nbytes);
     170        1350 :                 frame.offset += nbytes;
     171        1350 :                 break;
     172           0 :         default: break;
     173             :         }
     174             : 
     175        4100 :         if (getRequiredBytes() == 0) {
     176        4100 :                 return updateState();
     177             :         }
     178           0 :         return true;
     179             : }
     180             : 
     181        4100 : bool WebsocketFrameReader::updateState() {
     182        4100 :         bool shouldPrepareBody = false;
     183        4100 :         switch (status) {
     184        1375 :         case Status::Head:
     185        1375 :                 size = 0;
     186        1375 :                 mask = 0;
     187        1375 :                 type = WebsocketFrameType::None;
     188             : 
     189        1375 :                 fin =           (buffer[0] & 0b10000000) != 0;
     190        1375 :                 extra =         (buffer[0] & 0b01110000);
     191        1375 :                 type = getTypeFromOpcode
     192        1375 :                                         (buffer[0] & 0b00001111);
     193        1375 :                 masked =        (buffer[1] & 0b10000000) != 0;
     194        1375 :                 size =          (buffer[1] & 0b01111111);
     195             : 
     196        1375 :                 if (extra != 0 || !masked || type == WebsocketFrameType::None) {
     197           0 :                         if (extra != 0) {
     198           0 :                                 error = Error::ExtraIsNotEmpty;
     199           0 :                         } else if (!masked) {
     200           0 :                                 error = Error::NotMasked;
     201             :                         } else {
     202           0 :                                 error = Error::UnknownOpcode;
     203             :                         }
     204           0 :                         root->error("Websocket", "Invalid control flow", Value(toInt(error)));
     205           0 :                         return false;
     206             :                 }
     207             : 
     208        1375 :                 if (!frame.buffer.empty()) {
     209           0 :                         if (!isControlFrameType(type)) {
     210           0 :                                 error = Error::InvalidSegment;
     211           0 :                                 root->error("Websocket", "Invalid segment", Value(toInt(error)));
     212           0 :                                 return false;
     213             :                         }
     214             :                 }
     215             : 
     216        1375 :                 if (size > max) {
     217           0 :                         error = Error::InvalidSize;
     218           0 :                         root->error("Websocket", "Too large query", Value{{
     219           0 :                                 pair("size", Value(size)),
     220           0 :                                 pair("max", Value(max)),
     221           0 :                         }});
     222           0 :                         return false;
     223             :                 }
     224             : 
     225        1375 :                 if (size == 126) {
     226           0 :                         size = 0;
     227           0 :                         status = Status::Size16;
     228        1375 :                 } else if (size == 127) {
     229           0 :                         size = 0;
     230           0 :                         status = Status::Size64;
     231             :                 } else {
     232        1375 :                         status = Status::Mask;
     233             :                 }
     234             : 
     235        1375 :                 buffer.clear();
     236        1375 :                 return true;
     237             :                 break;
     238           0 :         case Status::Size16:
     239           0 :                 size = buffer.get<BytesViewNetwork>().readUnsigned16();
     240           0 :                 if (size > max) {
     241           0 :                         error = Error::InvalidSize;
     242           0 :                         root->error("Websocket", "Too large query", Value{{
     243           0 :                                 pair("size", Value(size)),
     244           0 :                                 pair("max", Value(max)),
     245           0 :                         }});
     246           0 :                         return false;
     247             :                 }
     248           0 :                 status = masked?Status::Mask:Status::Body;
     249           0 :                 buffer.clear();
     250           0 :                 shouldPrepareBody = true;
     251           0 :                 break;
     252           0 :         case Status::Size64:
     253           0 :                 size = buffer.get<BytesViewNetwork>().readUnsigned64();
     254           0 :                 if (size > max) {
     255           0 :                         error = Error::InvalidSize;
     256           0 :                         root->error("Websocket", "Too large query", Value{{
     257           0 :                                 pair("size", Value(size)),
     258           0 :                                 pair("max", Value(max)),
     259           0 :                         }});
     260           0 :                         return false;
     261             :                 }
     262           0 :                 status = masked?Status::Mask:Status::Body;
     263           0 :                 buffer.clear();
     264           0 :                 shouldPrepareBody = true;
     265           0 :                 break;
     266        1375 :         case Status::Mask:
     267        1375 :                 mask = buffer.get<BytesView>().readUnsigned32();
     268        1375 :                 status = Status::Body;
     269        1375 :                 buffer.clear();
     270        1375 :                 shouldPrepareBody = true;
     271        1375 :                 break;
     272           0 :         case Status::Control:
     273           0 :                 break;
     274        1350 :         case Status::Body:
     275        1350 :                 frame.fin = fin;
     276        1350 :                 frame.block += size;
     277        1350 :                 if (type != WebsocketFrameType::Continue) {
     278        1350 :                         frame.type = type;
     279             :                 }
     280        1350 :                 break;
     281           0 :         default:
     282           0 :                 break;
     283             :         }
     284             : 
     285        2725 :         if (shouldPrepareBody && status == Status::Body) {
     286        1375 :                 if (isControlFrameType(type)) {
     287          25 :                         status = Status::Control;
     288             :                 } else {
     289        1350 :                         if (size + frame.block > max) {
     290           0 :                                 error = Error::InvalidSize;
     291           0 :                                 root->error("Websocket", "Too large query", Value{{
     292           0 :                                         pair("size", Value(size + frame.block)),
     293           0 :                                         pair("max", Value(max)),
     294           0 :                                 }});
     295           0 :                                 return false;
     296             :                         }
     297        1350 :                         frame.buffer.resize(size + frame.block);
     298             :                 }
     299             :         }
     300        2725 :         return true;
     301             : }
     302             : 
     303        4100 : bool WebsocketFrameReader::isControlReady() const {
     304        4100 :         if (status == Status::Control && getRequiredBytes() == 0) {
     305          25 :                 return true;
     306             :         }
     307        4075 :         return false;
     308             : }
     309        4075 : bool WebsocketFrameReader::isFrameReady() const {
     310        4075 :         if (status == Status::Body && getRequiredBytes() == 0 && frame.fin) {
     311        1350 :                 return true;
     312             :         }
     313        2725 :         return false;
     314             : }
     315        1375 : void WebsocketFrameReader::popFrame() {
     316        1375 :         switch (status) {
     317          25 :         case Status::Control:
     318          25 :                 buffer.clear();
     319          25 :                 status = Status::Head;
     320          25 :                 break;
     321        1350 :         case Status::Body:
     322        1350 :                 clear();
     323        1350 :                 break;
     324           0 :         default:
     325           0 :                 error = Error::InvalidAction;
     326           0 :                 break;
     327             :         }
     328        1375 : }
     329             : 
     330        1350 : void WebsocketFrameReader::clear() {
     331        1350 :         frame.buffer.force_clear();
     332        1350 :         frame.buffer.clear();
     333        1350 :         memory::pool::clear(pool); // clear frame-related data
     334             : 
     335        1350 :         status = Status::Head;
     336        1350 :         frame.block = 0;
     337        1350 :         frame.offset = 0;
     338        1350 :         frame.fin = true;
     339        1350 :         frame.type = WebsocketFrameType::None;
     340        1350 : }
     341             : 
     342           0 : WebsocketFrameWriter::WriteSlot::WriteSlot(pool_t *p) : pool(p) { }
     343             : 
     344           0 : bool WebsocketFrameWriter::WriteSlot::empty() const {
     345           0 :         return firstData == nullptr;
     346             : }
     347             : 
     348           0 : void WebsocketFrameWriter::WriteSlot::emplace(const uint8_t *data, size_t size) {
     349           0 :         auto mem = pool::palloc(pool, sizeof(Slice) + size);
     350           0 :         Slice *next = (Slice*) mem;
     351           0 :         next->data = (uint8_t*) mem + sizeof(Slice);
     352           0 :         next->size = size;
     353           0 :         next->next = nullptr;
     354             : 
     355           0 :         memcpy(next->data, data, size);
     356             : 
     357           0 :         if (lastData) {
     358           0 :                 lastData->next = next;
     359           0 :                 lastData = next;
     360             :         } else {
     361           0 :                 firstData = next;
     362           0 :                 lastData = next;
     363             :         }
     364             : 
     365           0 :         alloc += size + sizeof(Slice);
     366           0 : }
     367             : 
     368           0 : void WebsocketFrameWriter::WriteSlot::pop(size_t size) {
     369           0 :         if (size >= firstData->size - offset) {
     370           0 :                 firstData = firstData->next;
     371           0 :                 if (!firstData) {
     372           0 :                         lastData = nullptr;
     373             :                 }
     374           0 :                 offset = 0;
     375             :         } else {
     376           0 :                 offset += size;
     377             :         }
     378           0 : }
     379             : 
     380           0 : uint8_t* WebsocketFrameWriter::WriteSlot::getNextBytes() const {
     381           0 :         return firstData->data + offset;
     382             : }
     383             : 
     384           0 : size_t WebsocketFrameWriter::WriteSlot::getNextLength() const {
     385           0 :         return firstData->size - offset;
     386             : }
     387             : 
     388          25 : WebsocketFrameWriter::WebsocketFrameWriter(pool_t *p) : pool(p) { }
     389             : 
     390           0 : bool WebsocketFrameWriter::empty() const {
     391           0 :         return firstSlot == nullptr;
     392             : }
     393             : 
     394           0 : WebsocketFrameWriter::WriteSlot* WebsocketFrameWriter::nextReadSlot() const {
     395           0 :         return firstSlot;
     396             : }
     397             : 
     398           0 : void WebsocketFrameWriter::popReadSlot() {
     399           0 :         if (firstSlot->empty()) {
     400           0 :                 memory::pool::destroy(firstSlot->pool);
     401           0 :                 firstSlot = firstSlot->next;
     402           0 :                 if (!firstSlot) {
     403           0 :                         lastSlot = nullptr;
     404             :                 }
     405             :         }
     406           0 : }
     407             : 
     408           0 : WebsocketFrameWriter::WriteSlot* WebsocketFrameWriter::nextEmplaceSlot(size_t sizeOfData) {
     409           0 :         if (!lastSlot || lastSlot->alloc + sizeOfData > 16_KiB) {
     410           0 :                 auto p = memory::pool::create(pool);
     411           0 :                 WriteSlot *slot = new (p) WriteSlot(p);
     412           0 :                 if (lastSlot) {
     413           0 :                         lastSlot->next = slot;
     414           0 :                         lastSlot = slot;
     415             :                 } else {
     416           0 :                         firstSlot = slot;
     417           0 :                         lastSlot = slot;
     418             :                 }
     419             :         }
     420           0 :         return lastSlot;
     421             : }
     422             : 
     423             : }

Generated by: LCOV version 1.14