Skip to content

Commit 0603733

Browse files
chore: adjust implementation
1 parent a46bce1 commit 0603733

File tree

3 files changed

+51
-74
lines changed

3 files changed

+51
-74
lines changed

src/websocket/frame.mbt

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,14 @@ async fn[R : @io.Reader] read_frame(reader : R) -> Frame {
2121
let byte1 = header[1]
2222
let fin = (byte0.to_int() & 0x80) != 0
2323
let opcode_byte = byte0 & b'\x0F'
24-
let opcode = match OpCode::from_byte(opcode_byte) {
25-
Some(op) => op
26-
None => raise FrameError("Invalid opcode: \{opcode_byte}")
27-
}
24+
let opcode = OpCode::from_byte(opcode_byte)
2825
let masked = (byte1.to_int() & 0x80) != 0
2926
let mut payload_len = (byte1.to_int() & 0x7F).to_int64()
3027

31-
// Validate payload length according to RFC 6455
28+
// Validate payload length according to RFC 6455 Section 5.2
3229
if payload_len == 126L {
3330
let len_bytes = reader.read_exactly(2)
34-
guard len_bytes is [u16be(len), ..]
31+
guard len_bytes is [u16be(len)]
3532
payload_len = len.to_int64()
3633
if payload_len < 126L {
3734
raise FrameError(
@@ -40,7 +37,7 @@ async fn[R : @io.Reader] read_frame(reader : R) -> Frame {
4037
}
4138
} else if payload_len == 127L {
4239
let len_bytes = reader.read_exactly(8)
43-
guard len_bytes is [u64be(len), ..]
40+
guard len_bytes is [u64be(len)]
4441
payload_len = len.reinterpret_as_int64()
4542
if payload_len < 65536L {
4643
raise FrameError(
@@ -49,14 +46,12 @@ async fn[R : @io.Reader] read_frame(reader : R) -> Frame {
4946
}
5047
if payload_len < 0L {
5148
raise FrameError(
52-
"Payload length too large (negative when interpreted as signed)",
49+
"Invalid payload length: MSB must be 0 for 64-bit length",
5350
)
5451
}
5552
}
56-
57-
// Check for reasonable payload size limit (1MB for now)
58-
if payload_len > 1048576L {
59-
raise FrameError("Payload too large: \{payload_len} bytes (max 1MB)")
53+
if payload_len > @int.max_value.to_int64() {
54+
raise FrameError("Payload too large: \{payload_len} bytes")
6055
}
6156

6257
// Read masking key if present
@@ -89,13 +84,6 @@ async fn[W : @io.Writer] write_frame(
8984
masked : Bool,
9085
) -> Unit {
9186
let payload_len = payload.length().to_int64()
92-
93-
// Validate payload size
94-
if payload_len > 1048576L {
95-
raise FrameError(
96-
"Payload too large for sending: \{payload_len} bytes (max 1MB)",
97-
)
98-
}
9987
let mut header_len = 2
10088

10189
// Calculate extended length size
@@ -105,45 +93,37 @@ async fn[W : @io.Writer] write_frame(
10593
header_len += 8
10694
}
10795

108-
// Add mask size if needed
109-
if masked {
110-
header_len += 4
111-
}
112-
11396
// Build header
114-
let header = FixedArray::make(header_len, b'\x00')
115-
let mut offset = 0
97+
let header = if masked {
98+
FixedArray::make(header_len + 4, b'\x00')
99+
} else {
100+
FixedArray::make(header_len, b'\x00')
101+
}
116102

117103
// First byte: FIN + opcode
118-
header[offset] = if fin {
104+
header[0] = if fin {
119105
(0x80 | opcode.to_byte().to_int()).to_byte()
120106
} else {
121107
opcode.to_byte()
122108
}
123-
offset += 1
124109

125110
// Second byte: MASK + payload length
126111
let mask_bit = if masked { 0x80 } else { 0 }
127112
if payload_len < 126L {
128-
header[offset] = (mask_bit | payload_len.to_int()).to_byte()
129-
offset += 1
113+
header[1] = (mask_bit | payload_len.to_int()).to_byte()
130114
} else if payload_len <= 65535L {
131-
header[offset] = (mask_bit | 126).to_byte()
132-
offset += 1
133-
header.unsafe_write_uint16_be(offset, payload_len.to_uint16())
134-
offset += 2
115+
header[1] = (mask_bit | 126).to_byte()
116+
header.unsafe_write_uint16_be(2, payload_len.to_uint16())
135117
} else {
136-
header[offset] = (mask_bit | 127).to_byte()
137-
offset += 1
138-
header.unsafe_write_uint64_be(offset, payload_len.reinterpret_as_uint64())
139-
offset += 8
118+
header[1] = (mask_bit | 127).to_byte()
119+
header.unsafe_write_uint64_be(2, payload_len.reinterpret_as_uint64())
140120
}
141121

142122
// Add masking key and mask payload if needed
143123
let final_payload = if masked {
144124
let mask = generate_mask()
145125
for i = 0; i < 4; i = i + 1 {
146-
header[offset + i] = mask[i]
126+
header[header_len + i] = mask[i]
147127
}
148128
let payload_arr = payload.to_fixedarray()
149129
mask_payload(payload_arr, mask)

src/websocket/server.mbt

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ async fn ServerConnection::handshake(conn : @socket.Tcp) -> ServerConnection? {
4444
if line.is_blank() {
4545
break
4646
}
47+
let line = line[:-1] // Remove trailing \r
4748

4849
// Parse header line
4950
if line.contains(":") {
@@ -53,9 +54,9 @@ async fn ServerConnection::handshake(conn : @socket.Tcp) -> ServerConnection? {
5354
// Join remaining parts in case the value contains colons
5455
let value_parts = parts[1:]
5556
let value = if value_parts.length() == 1 {
56-
value_parts[0].trim(chars=" \t\r").to_string()
57+
value_parts[0].trim(chars=" \t").to_string()
5758
} else {
58-
value_parts.map(_.trim(chars=" \t\r")).join(":").to_string()
59+
value_parts.join(":").trim(chars=" \t").to_string()
5960
}
6061
// Handle multi-value headers by taking the first value
6162
if not(headers.contains(key)) {
@@ -85,11 +86,13 @@ async fn ServerConnection::handshake(conn : @socket.Tcp) -> ServerConnection? {
8586
let accept_key = generate_accept_key(key)
8687

8788
// Send upgrade response
88-
let response = "HTTP/1.1 101 Switching Protocols\r\n" +
89-
"Upgrade: websocket\r\n" +
90-
"Connection: Upgrade\r\n" +
91-
"Sec-WebSocket-Accept: \{accept_key}\r\n" +
92-
"\r\n"
89+
let response =
90+
$|HTTP/1.1 101 Switching Protocols\r
91+
$|Upgrade: websocket\r
92+
$|Connection: Upgrade\r
93+
$|Sec-WebSocket-Accept: \{accept_key}\r
94+
$|\r
95+
$|
9396
conn.write(@encoding/utf8.encode(response))
9497
Some({ conn, closed: None })
9598
}
@@ -157,7 +160,7 @@ pub async fn ServerConnection::pong(
157160
pub async fn ServerConnection::send_close(
158161
self : ServerConnection,
159162
code? : CloseCode = Normal,
160-
reason? : String = "",
163+
reason? : BytesView = "",
161164
) -> Unit {
162165
if self.closed is Some(c) {
163166
raise ConnectionClosed(c)
@@ -172,11 +175,7 @@ pub async fn ServerConnection::send_close(
172175

173176
// Encode reason
174177
if reason != "" {
175-
let reason_bytes = @encoding/utf8.encode(reason)
176-
let reason_arr = reason_bytes.to_fixedarray()
177-
for i = 0; i < reason_arr.length(); i = i + 1 {
178-
payload[2 + i] = reason_arr[i]
179-
}
178+
payload.blit_from_bytesview(2, reason)
180179
}
181180
write_frame(
182181
self.conn,
@@ -205,28 +204,26 @@ pub async fn ServerConnection::receive(self : ServerConnection) -> Message {
205204
OpCode::Close => {
206205
// Parse close code and reason
207206
let mut close_code = Normal
208-
let mut reason = ""
209207
if frame.payload.length() >= 2 {
210208
let payload_arr = frame.payload.to_fixedarray()
211209
let code_int = (payload_arr[0].to_int() << 8) |
212210
payload_arr[1].to_int()
213211
close_code = CloseCode::from_int(code_int).unwrap_or(Normal)
214212
if frame.payload.length() > 2 {
215-
let reason_bytes = FixedArray::make(
216-
frame.payload.length() - 2,
217-
b'\x00',
218-
)
219-
for i = 2; i < frame.payload.length(); i = i + 1 {
220-
reason_bytes[i - 2] = payload_arr[i]
221-
}
222-
reason = @encoding/utf8.decode_lossy(
223-
reason_bytes.unsafe_reinterpret_as_bytes(),
224-
)
213+
// As per spec https://datatracker.ietf.org/doc/html/rfc6455#autoid-27
214+
// The data is not guaranteed to be human readable
215+
// So we do not decode it here
216+
// And we are not using it further
217+
let _reason_bytes = payload_arr.unsafe_reinterpret_as_bytes()[2:]
218+
225219
}
226220
}
227-
// Echo the close frame back and close
228-
self.send_close(code=close_code, reason~)
229-
self.closed = Some(close_code)
221+
// If we didn't send close first, respond with close
222+
if self.closed is None {
223+
// Echo the close frame back and close
224+
self.send_close(code=close_code)
225+
self.closed = Some(close_code)
226+
}
230227
raise ConnectionClosed(close_code)
231228
}
232229
OpCode::Ping => {

src/websocket/types.mbt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,15 @@ fn OpCode::to_byte(self : OpCode) -> Byte {
3636
}
3737

3838
///|
39-
fn OpCode::from_byte(byte : Byte) -> OpCode? {
39+
fn OpCode::from_byte(byte : Byte) -> OpCode raise {
4040
match byte {
41-
b'\x00' => Some(Continuation)
42-
b'\x01' => Some(Text)
43-
b'\x02' => Some(Binary)
44-
b'\x08' => Some(Close)
45-
b'\x09' => Some(Ping)
46-
b'\x0A' => Some(Pong)
47-
_ => None
41+
b'\x00' => Continuation
42+
b'\x01' => Text
43+
b'\x02' => Binary
44+
b'\x08' => Close
45+
b'\x09' => Ping
46+
b'\x0A' => Pong
47+
_ => raise FrameError("Invalid opcode byte: \{byte}")
4848
}
4949
}
5050

0 commit comments

Comments
 (0)