From 66bc8bc0cb0c8d915f866ef92b1dfcbe3903e3fb Mon Sep 17 00:00:00 2001 From: shadow <49539636+shadowninja55@users.noreply.github.com> Date: Wed, 28 Jul 2021 02:22:19 -0400 Subject: [PATCH] vlib: remove many deprecated functions (#10972) --- vlib/arrays/arrays.v | 5 - vlib/builtin/builtin.v | 6 - vlib/builtin/string.v | 9 - vlib/encoding/utf8/utf8_util.v | 6 - vlib/hash/hash_wyhash_test.v | 25 -- vlib/hash/wyhash.v | 55 --- vlib/net/tcp.v | 6 - vlib/net/udp.v | 5 - vlib/net/unix/stream_nix.v | 6 - vlib/net/websocket/websocket_client.v | 6 - vlib/strconv/utilities.v | 36 -- vlib/time/operator_test.v | 2 +- vlib/time/time.v | 12 - vlib/time/time_nix.c.v | 7 - vlib/time/time_windows.c.v | 6 - vlib/vweb/vweb.v | 8 - vlib/x/websocket/events.v | 227 ----------- vlib/x/websocket/handshake.v | 185 --------- vlib/x/websocket/io.v | 100 ----- vlib/x/websocket/message.v | 295 --------------- vlib/x/websocket/uri.v | 16 - vlib/x/websocket/utils.v | 54 --- vlib/x/websocket/websocket_client.v | 497 ------------------------- vlib/x/websocket/websocket_nix.c.v | 10 - vlib/x/websocket/websocket_server.v | 190 ---------- vlib/x/websocket/websocket_windows.c.v | 12 - 26 files changed, 1 insertion(+), 1785 deletions(-) delete mode 100644 vlib/hash/hash_wyhash_test.v delete mode 100644 vlib/x/websocket/events.v delete mode 100644 vlib/x/websocket/handshake.v delete mode 100644 vlib/x/websocket/io.v delete mode 100644 vlib/x/websocket/message.v delete mode 100644 vlib/x/websocket/uri.v delete mode 100644 vlib/x/websocket/utils.v delete mode 100644 vlib/x/websocket/websocket_client.v delete mode 100644 vlib/x/websocket/websocket_nix.c.v delete mode 100644 vlib/x/websocket/websocket_server.v delete mode 100644 vlib/x/websocket/websocket_windows.c.v diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index 71465b7399..26636d1b0d 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -124,8 +124,3 @@ pub fn group(lists ...[]T) [][]T { return [][]T{} } - -[deprecated] -pub fn shuffle(mut a []T, n int) { - panic('Please use rand.util.shuffle() instead') -} diff --git a/vlib/builtin/builtin.v b/vlib/builtin/builtin.v index 3fc73fac7d..d3a14744aa 100644 --- a/vlib/builtin/builtin.v +++ b/vlib/builtin/builtin.v @@ -9,12 +9,6 @@ pub fn isnil(v voidptr) bool { return v == 0 } -[deprecated: 'use os.is_atty(x) instead'] -pub fn is_atty(fd int) int { - panic('use os.is_atty(x) instead') - return 0 -} - /* fn on_panic(f fn(int)int) { // TODO diff --git a/vlib/builtin/string.v b/vlib/builtin/string.v index 78b06bd25f..169ed2d4fb 100644 --- a/vlib/builtin/string.v +++ b/vlib/builtin/string.v @@ -140,15 +140,6 @@ pub fn tos5(s &char) string { return unsafe { tos3(s) } } -[deprecated: 'tos_lit has been deprecated, use _SLIT instead'] -pub fn tos_lit(s &char) string { - return string{ - str: &byte(s) - len: unsafe { C.strlen(s) } - is_lit: 1 - } -} - // vstring converts a C style string to a V string. NB: the string data is reused, NOT copied. // strings returned from this function will be normal V strings beside that (i.e. they would be // freed by V's -autofree mechanism, when they are no longer used). diff --git a/vlib/encoding/utf8/utf8_util.v b/vlib/encoding/utf8/utf8_util.v index 8ab736634b..2e3da0d87a 100644 --- a/vlib/encoding/utf8/utf8_util.v +++ b/vlib/encoding/utf8/utf8_util.v @@ -33,12 +33,6 @@ pub fn len(s string) int { return count } -// char_len calculate the length in bytes of a utf8 char -[deprecated: 'use builtin utf8_char_len'] -pub fn char_len(b byte) int { - return ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + 1 -} - // get_uchar convert a unicode glyph in string[index] into a int unicode char pub fn get_uchar(s string, index int) int { mut res := 0 diff --git a/vlib/hash/hash_wyhash_test.v b/vlib/hash/hash_wyhash_test.v deleted file mode 100644 index 79b752d462..0000000000 --- a/vlib/hash/hash_wyhash_test.v +++ /dev/null @@ -1,25 +0,0 @@ -module hash - -struct WyHashTest { - s string - seed u64 - expected u64 -} - -fn test_wyhash() { - tests := [WyHashTest{'', 0, 0x0}, WyHashTest{'v', 1, 0xc72a8f8bdfdd82}, - WyHashTest{'is', 2, 0xa1099c1c58fc13e}, WyHashTest{'the best', 3, 0x1b1215ef0b0b94c}, - WyHashTest{'abcdefghijklmnopqrstuvwxyz', 4, 0x6db0e773d1503fac}, - WyHashTest{'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 5, 0xe062dfda99413626}, - ] - for test in tests { - got := wyhash64(test.s.str, u64(test.s.len), test.seed) - // println(' # GOT: $got | $got.hex()') - // println(' # EXPECTED: $test.expected | $test.expected.hex()') - assert got == test.expected - } - - s := '/v/vmaster/vlib/v/fmt/tests/maps_of_fns_with_string_keys_keep.vv' - x := sum64_string(s, 5).hex_full() - println(x) -} diff --git a/vlib/hash/wyhash.v b/vlib/hash/wyhash.v index 8158bd9d12..bd89c4216e 100644 --- a/vlib/hash/wyhash.v +++ b/vlib/hash/wyhash.v @@ -33,61 +33,6 @@ pub fn sum64(key []byte, seed u64) u64 { return wyhash_c(&byte(key.data), u64(key.len), seed) } -// This is an outdated version of wyhash with memory errors! -[deprecated; inline] -fn wyhash64(key &byte, len u64, seed_ u64) u64 { - if len == 0 { - return 0 - } - mut p := unsafe { key } - mut seed := seed_ - mut i := len & 63 - seed = unsafe { - match i { - 0...3 { - wymum(wyr3(p, i) ^ seed ^ hash.wyp0, seed ^ hash.wyp1) - } - 4...8 { - wymum(wyr4(p) ^ seed ^ hash.wyp0, wyr4(p + i - 4) ^ seed ^ hash.wyp1) - } - 9...16 { - wymum(wyr8(p) ^ seed ^ hash.wyp0, wyr8(p + i - 8) ^ seed ^ hash.wyp1) - } - 17...24 { - wymum(wyr8(p) ^ seed ^ hash.wyp0, wyr8(p + 8) ^ seed ^ hash.wyp1) ^ wymum(wyr8(p + i - 8) ^ seed ^ hash.wyp2, - seed ^ hash.wyp3) - } - 25...32 { - wymum(wyr8(p) ^ seed ^ hash.wyp0, wyr8(p + 8) ^ seed ^ hash.wyp1) ^ wymum(wyr8(p + - 16) ^ seed ^ hash.wyp2, wyr8(p + i - 8) ^ seed ^ hash.wyp3) - } - else { - wymum(wyr8(p) ^ seed ^ hash.wyp0, wyr8(p + 8) ^ seed ^ hash.wyp1) ^ wymum(wyr8(p + - 16) ^ seed ^ hash.wyp2, wyr8(p + 24) ^ seed ^ hash.wyp3) ^ wymum(wyr8(p + i - 32) ^ seed ^ hash.wyp1, - wyr8(p + i - 24) ^ seed ^ hash.wyp2) ^ wymum(wyr8(p + i - 16) ^ seed ^ hash.wyp3, - wyr8(p + i - 8) ^ seed ^ hash.wyp0) - } - } - } - if i == len { - return wymum(seed, len ^ hash.wyp4) - } - mut see1 := seed - mut see2 := seed - mut see3 := seed - unsafe { - p = p + i - for i = len - i; i >= 64; i -= 64 { - seed = wymum(wyr8(p) ^ seed ^ hash.wyp0, wyr8(p + 8) ^ seed ^ hash.wyp1) - see1 = wymum(wyr8(p + 16) ^ see1 ^ hash.wyp2, wyr8(p + 24) ^ see1 ^ hash.wyp3) - see2 = wymum(wyr8(p + 32) ^ see2 ^ hash.wyp1, wyr8(p + 40) ^ see2 ^ hash.wyp2) - see3 = wymum(wyr8(p + 48) ^ see3 ^ hash.wyp3, wyr8(p + 56) ^ see3 ^ hash.wyp0) - p = p + 64 - } - } - return wymum(seed ^ see1 ^ see2, see3 ^ len ^ hash.wyp4) -} - [inline] fn wyrotr(v u64, k u32) u64 { return (v >> k) | (v << (64 - k)) diff --git a/vlib/net/tcp.v b/vlib/net/tcp.v index b7a34944ce..9a11db93f7 100644 --- a/vlib/net/tcp.v +++ b/vlib/net/tcp.v @@ -78,12 +78,6 @@ pub fn (mut c TcpConn) write(bytes []byte) ?int { return c.write_ptr(bytes.data, bytes.len) } -// write_str blocks and attempts to write all data -[deprecated: 'use TcpConn.write_string() instead'] -pub fn (mut c TcpConn) write_str(s string) ?int { - return c.write_ptr(s.str, s.len) -} - // write_string blocks and attempts to write all data pub fn (mut c TcpConn) write_string(s string) ?int { return c.write_ptr(s.str, s.len) diff --git a/vlib/net/udp.v b/vlib/net/udp.v index cf69619f00..874b2b7a0b 100644 --- a/vlib/net/udp.v +++ b/vlib/net/udp.v @@ -67,11 +67,6 @@ pub fn (mut c UdpConn) write(buf []byte) ?int { return c.write_ptr(buf.data, buf.len) } -[deprecated: 'use UdpConn.write_string() instead'] -pub fn (mut c UdpConn) write_str(s string) ?int { - return c.write_string(s) -} - pub fn (mut c UdpConn) write_string(s string) ?int { return c.write_ptr(s.str, s.len) } diff --git a/vlib/net/unix/stream_nix.v b/vlib/net/unix/stream_nix.v index 580716897c..d13c811a2b 100644 --- a/vlib/net/unix/stream_nix.v +++ b/vlib/net/unix/stream_nix.v @@ -199,12 +199,6 @@ pub fn (mut c StreamConn) write(bytes []byte) ?int { return c.write_ptr(bytes.data, bytes.len) } -// write_str blocks and attempts to write all data -[deprecated: 'use StreamConn.write_string() instead'] -pub fn (mut c StreamConn) write_str(s string) ?int { - return c.write_string(s) -} - // write_string blocks and attempts to write all data pub fn (mut c StreamConn) write_string(s string) ?int { return c.write_ptr(s.str, s.len) diff --git a/vlib/net/websocket/websocket_client.v b/vlib/net/websocket/websocket_client.v index 96699477f9..4408c7e4d1 100644 --- a/vlib/net/websocket/websocket_client.v +++ b/vlib/net/websocket/websocket_client.v @@ -310,12 +310,6 @@ pub fn (mut ws Client) write(bytes []byte, code OPCode) ?int { return ws.write_ptr(&byte(bytes.data), bytes.len, code) } -// write_string, writes a string with a websocket texttype to socket -[deprecated: 'use Client.write_string() instead'] -pub fn (mut ws Client) write_str(str string) ?int { - return ws.write_string(str) -} - // write_str, writes a string with a websocket texttype to socket pub fn (mut ws Client) write_string(str string) ?int { return ws.write_ptr(str.str, str.len, .text_frame) diff --git a/vlib/strconv/utilities.v b/vlib/strconv/utilities.v index 3cb234d76c..2098a73f64 100644 --- a/vlib/strconv/utilities.v +++ b/vlib/strconv/utilities.v @@ -73,33 +73,6 @@ fn get_string_special(neg bool, expZero bool, mantZero bool) string { /* 32 bit functions */ -// decimal_len_32 return the number of decimal digits of the input -[deprecated] -pub fn decimal_len_32(u u32) int { - // Function precondition: u is not a 10-digit number. - // (9 digits are sufficient for round-tripping.) - // This benchmarked faster than the log2 approach used for u64. - assert1(u < 1000000000, 'too big') - - if u >= 100000000 { - return 9 - } else if u >= 10000000 { - return 8 - } else if u >= 1000000 { - return 7 - } else if u >= 100000 { - return 6 - } else if u >= 10000 { - return 5 - } else if u >= 1000 { - return 4 - } else if u >= 100 { - return 3 - } else if u >= 10 { - return 2 - } - return 1 -} fn mul_shift_32(m u32, mul u64, ishift int) u32 { // QTODO @@ -174,15 +147,6 @@ fn pow5_bits(e int) int { 64 bit functions */ -// decimal_len_64 return the number of decimal digits of the input -[deprecated] -pub fn decimal_len_64(u u64) int { - // http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 - log2 := 64 - bits.leading_zeros_64(u) - 1 - t := (log2 + 1) * 1233 >> 12 - return t - bool_to_int(u < powers_of_10[t]) + 1 -} - fn shift_right_128(v Uint128, shift int) u64 { // The shift value is always modulo 64. // In the current implementation of the 64-bit version diff --git a/vlib/time/operator_test.v b/vlib/time/operator_test.v index add305a96e..5f3e1b7a3e 100644 --- a/vlib/time/operator_test.v +++ b/vlib/time/operator_test.v @@ -1,7 +1,7 @@ module time fn assert_greater_time(ms int, t1 Time) { - wait(ms * millisecond) + sleep(ms * millisecond) t2 := now() assert t2 > t1 } diff --git a/vlib/time/time.v b/vlib/time/time.v index 8cb14757cd..14f0c7afd7 100644 --- a/vlib/time/time.v +++ b/vlib/time/time.v @@ -336,18 +336,6 @@ pub fn sleep(seconds int) { } */ -// sleep_ms makes the calling thread sleep for a given number of milliseconds. -[deprecated: 'call time.sleep(n * time.millisecond)'] -pub fn sleep_ms(milliseconds int) { - wait(milliseconds * time.millisecond) -} - -// usleep makes the calling thread sleep for a given number of microseconds. -[deprecated: 'call time.sleep(n * time.microsecond)'] -pub fn usleep(microseconds int) { - wait(microseconds * time.microsecond) -} - // is_leap_year checks if a given a year is a leap year. pub fn is_leap_year(year int) bool { return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0) diff --git a/vlib/time/time_nix.c.v b/vlib/time/time_nix.c.v index c96d4e7a49..82ff5485af 100644 --- a/vlib/time/time_nix.c.v +++ b/vlib/time/time_nix.c.v @@ -129,13 +129,6 @@ pub fn zero_timespec() C.timespec { return ts } -// wait makes the calling thread sleep for a given duration (in nanoseconds). -[deprecated: 'call time.sleep(n * time.second)'] -pub fn wait(duration Duration) { - ts := &C.timespec{duration / second, duration % second} - C.nanosleep(ts, C.NULL) -} - // sleep makes the calling thread sleep for a given duration (in nanoseconds). pub fn sleep(duration Duration) { mut req := C.timespec{duration / second, duration % second} diff --git a/vlib/time/time_windows.c.v b/vlib/time/time_windows.c.v index 363bede08d..d44e6c9413 100644 --- a/vlib/time/time_windows.c.v +++ b/vlib/time/time_windows.c.v @@ -215,12 +215,6 @@ pub struct C.timeval { tv_usec u64 } -// wait makes the calling thread sleep for a given duration (in nanoseconds). -[deprecated: 'call time.sleep(n * time.second)'] -pub fn wait(duration Duration) { - C.Sleep(int(duration / millisecond)) -} - // sleep makes the calling thread sleep for a given duration (in nanoseconds). pub fn sleep(duration Duration) { C.Sleep(int(duration / millisecond)) diff --git a/vlib/vweb/vweb.v b/vlib/vweb/vweb.v index 32a2436cc4..956e7f66e4 100644 --- a/vlib/vweb/vweb.v +++ b/vlib/vweb/vweb.v @@ -212,14 +212,6 @@ pub fn (mut ctx Context) set_cookie(cookie Cookie) { ctx.add_header('Set-Cookie', '$cookie.name=$cookie.value; $data') } -// Old function -[deprecated] -pub fn (mut ctx Context) set_cookie_old(key string, val string) { - // TODO support directives, escape cookie value (https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie) - // ctx.add_header('Set-Cookie', '${key}=${val}; Secure; HttpOnly') - ctx.add_header('Set-Cookie', '$key=$val; HttpOnly') -} - // Sets the response content type pub fn (mut ctx Context) set_content_type(typ string) { ctx.content_type = typ diff --git a/vlib/x/websocket/events.v b/vlib/x/websocket/events.v deleted file mode 100644 index a442dafc7b..0000000000 --- a/vlib/x/websocket/events.v +++ /dev/null @@ -1,227 +0,0 @@ -module websocket - -// MessageEventHandler represents a callback on a new message -struct MessageEventHandler { - handler SocketMessageFn // callback function - handler2 SocketMessageFn2 // callback function with reference - is_ref bool // true if has a reference object - ref voidptr // referenced object -} - -// ErrorEventHandler represents a callback on error -struct ErrorEventHandler { - handler SocketErrorFn // callback function - handler2 SocketErrorFn2 // callback function with reference - is_ref bool // true if has a reference object - ref voidptr // referenced object -} - -// OpenEventHandler represents a callback when connection is opened -struct OpenEventHandler { - handler SocketOpenFn // callback function - handler2 SocketOpenFn2 // callback function with reference - is_ref bool // true if has a reference object - ref voidptr // referenced object -} - -// CloseEventHandler represents a callback on a closing event -struct CloseEventHandler { - handler SocketCloseFn // callback function - handler2 SocketCloseFn2 // callback function with reference - is_ref bool // true if has a reference object - ref voidptr // referenced object -} - -pub type AcceptClientFn = fn (mut c ServerClient) ?bool - -pub type SocketMessageFn = fn (mut c Client, msg &Message) ? - -pub type SocketMessageFn2 = fn (mut c Client, msg &Message, v voidptr) ? - -pub type SocketErrorFn = fn (mut c Client, err string) ? - -pub type SocketErrorFn2 = fn (mut c Client, err string, v voidptr) ? - -pub type SocketOpenFn = fn (mut c Client) ? - -pub type SocketOpenFn2 = fn (mut c Client, v voidptr) ? - -pub type SocketCloseFn = fn (mut c Client, code int, reason string) ? - -pub type SocketCloseFn2 = fn (mut c Client, code int, reason string, v voidptr) ? - -// on_connect registers a callback when client connects to the server -pub fn (mut s Server) on_connect(fun AcceptClientFn) ? { - if s.accept_client_callbacks.len > 0 { - return error('only one callback can be registered for accept client') - } - s.accept_client_callbacks << fun -} - -// on_message registers a callback on new messages -pub fn (mut s Server) on_message(fun SocketMessageFn) { - s.message_callbacks << MessageEventHandler{ - handler: fun - } -} - -// on_message_ref registers a callback on new messages and provides a reference object -pub fn (mut s Server) on_message_ref(fun SocketMessageFn2, ref voidptr) { - s.message_callbacks << MessageEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// on_close registers a callback on closed socket -pub fn (mut s Server) on_close(fun SocketCloseFn) { - s.close_callbacks << CloseEventHandler{ - handler: fun - } -} - -// on_close_ref registers a callback on closed socket and provides a reference object -pub fn (mut s Server) on_close_ref(fun SocketCloseFn2, ref voidptr) { - s.close_callbacks << CloseEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// on_message registers a callback on new messages -pub fn (mut ws Client) on_message(fun SocketMessageFn) { - ws.message_callbacks << MessageEventHandler{ - handler: fun - } -} - -// on_message_ref registers a callback on new messages and provides a reference object -pub fn (mut ws Client) on_message_ref(fun SocketMessageFn2, ref voidptr) { - ws.message_callbacks << MessageEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// on_error registers a callback on errors -pub fn (mut ws Client) on_error(fun SocketErrorFn) { - ws.error_callbacks << ErrorEventHandler{ - handler: fun - } -} - -// on_error_ref registers a callback on errors and provides a reference object -pub fn (mut ws Client) on_error_ref(fun SocketErrorFn2, ref voidptr) { - ws.error_callbacks << ErrorEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// on_open registers a callback on successful opening the websocket -pub fn (mut ws Client) on_open(fun SocketOpenFn) { - ws.open_callbacks << OpenEventHandler{ - handler: fun - } -} - -// on_open_ref registers a callback on successful opening the websocket -// and provides a reference object -pub fn (mut ws Client) on_open_ref(fun SocketOpenFn2, ref voidptr) { - ws.open_callbacks << OpenEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// on_close registers a callback on closed socket -pub fn (mut ws Client) on_close(fun SocketCloseFn) { - ws.close_callbacks << CloseEventHandler{ - handler: fun - } -} - -// on_close_ref registers a callback on closed socket and provides a reference object -pub fn (mut ws Client) on_close_ref(fun SocketCloseFn2, ref voidptr) { - ws.close_callbacks << CloseEventHandler{ - handler2: fun - ref: ref - is_ref: true - } -} - -// send_connect_event invokes the on_connect callback -fn (mut s Server) send_connect_event(mut c ServerClient) ?bool { - if s.accept_client_callbacks.len == 0 { - // If no callback all client will be accepted - return true - } - fun := s.accept_client_callbacks[0] - res := fun(mut c) ? - return res -} - -// send_message_event invokes the on_message callback -fn (mut ws Client) send_message_event(msg &Message) { - ws.debug_log('sending on_message event') - for ev_handler in ws.message_callbacks { - if !ev_handler.is_ref { - ev_handler.handler(ws, msg) or { ws.logger.error('send_message_event error: $err') } - } else { - ev_handler.handler2(ws, msg, ev_handler.ref) or { - ws.logger.error('send_message_event error: $err') - } - } - } -} - -// send_error_event invokes the on_error callback -fn (mut ws Client) send_error_event(error string) { - ws.debug_log('sending on_error event') - for ev_handler in ws.error_callbacks { - if !ev_handler.is_ref { - ev_handler.handler(mut ws, error) or { - ws.logger.error('send_error_event error: $error, err: $err') - } - } else { - ev_handler.handler2(mut ws, error, ev_handler.ref) or { - ws.logger.error('send_error_event error: $error, err: $err') - } - } - } -} - -// send_close_event invokes the on_close callback -fn (mut ws Client) send_close_event(code int, reason string) { - ws.debug_log('sending on_close event') - for ev_handler in ws.close_callbacks { - if !ev_handler.is_ref { - ev_handler.handler(mut ws, code, reason) or { - ws.logger.error('send_close_event error: $err') - } - } else { - ev_handler.handler2(mut ws, code, reason, ev_handler.ref) or { - ws.logger.error('send_close_event error: $err') - } - } - } -} - -// send_open_event invokes the on_open callback -fn (mut ws Client) send_open_event() { - ws.debug_log('sending on_open event') - for ev_handler in ws.open_callbacks { - if !ev_handler.is_ref { - ev_handler.handler(mut ws) or { ws.logger.error('send_open_event error: $err') } - } else { - ev_handler.handler2(mut ws, ev_handler.ref) or { - ws.logger.error('send_open_event error: $err') - } - } - } -} diff --git a/vlib/x/websocket/handshake.v b/vlib/x/websocket/handshake.v deleted file mode 100644 index 9f3ab000e0..0000000000 --- a/vlib/x/websocket/handshake.v +++ /dev/null @@ -1,185 +0,0 @@ -[manualfree] -module websocket - -import encoding.base64 -import strings - -// handshake manages the websocket handshake process -fn (mut ws Client) handshake() ? { - nonce := get_nonce(ws.nonce_size) - seckey := base64.encode_str(nonce) - mut sb := strings.new_builder(1024) - defer { - unsafe { sb.free() } - } - sb.write_string('GET ') - sb.write_string(ws.uri.resource) - sb.write_string(ws.uri.querystring) - sb.write_string(' HTTP/1.1\r\nHost: ') - sb.write_string(ws.uri.hostname) - sb.write_string(':') - sb.write_string(ws.uri.port) - sb.write_string('\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n') - sb.write_string('Sec-WebSocket-Key: ') - sb.write_string(seckey) - sb.write_string('\r\nSec-WebSocket-Version: 13') - for key in ws.header.keys() { - val := ws.header.custom_values(key).join(',') - sb.write_string('\r\n$key:$val') - } - sb.write_string('\r\n\r\n') - handshake := sb.str() - defer { - unsafe { handshake.free() } - } - handshake_bytes := handshake.bytes() - ws.debug_log('sending handshake: $handshake') - ws.socket_write(handshake_bytes) ? - ws.read_handshake(seckey) ? - unsafe { handshake_bytes.free() } -} - -// handle_server_handshake manages websocket server handshake process -fn (mut s Server) handle_server_handshake(mut c Client) ?(string, &ServerClient) { - msg := c.read_handshake_str() ? - handshake_response, client := s.parse_client_handshake(msg, mut c) ? - unsafe { msg.free() } - return handshake_response, client -} - -// parse_client_handshake parses result from handshake process -fn (mut s Server) parse_client_handshake(client_handshake string, mut c Client) ?(string, &ServerClient) { - s.logger.debug('server-> client handshake:\n$client_handshake') - lines := client_handshake.split_into_lines() - get_tokens := lines[0].split(' ') - if get_tokens.len < 3 { - return error_with_code('unexpected get operation, $get_tokens', 1) - } - if get_tokens[0].trim_space() != 'GET' { - return error_with_code("unexpected request '${get_tokens[0]}', expected 'GET'", - 2) - } - if get_tokens[2].trim_space() != 'HTTP/1.1' { - return error_with_code("unexpected request $get_tokens, expected 'HTTP/1.1'", - 3) - } - mut seckey := '' - mut flags := []Flag{} - mut key := '' - for i in 1 .. lines.len { - if lines[i].len <= 0 || lines[i] == '\r\n' { - continue - } - keys := lines[i].split(':') - match keys[0] { - 'Upgrade', 'upgrade' { - flags << .has_upgrade - } - 'Connection', 'connection' { - flags << .has_connection - } - 'Sec-WebSocket-Key', 'sec-websocket-key' { - key = keys[1].trim_space() - s.logger.debug('server-> got key: $key') - seckey = create_key_challenge_response(key) ? - s.logger.debug('server-> challenge: $seckey, response: ${keys[1]}') - flags << .has_accept - } - else { - // we ignore other headers like protocol for now - } - } - unsafe { keys.free() } - } - if flags.len < 3 { - return error_with_code('invalid client handshake, $client_handshake', 4) - } - server_handshake := 'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $seckey\r\n\r\n' - server_client := &ServerClient{ - resource_name: get_tokens[1] - client_key: key - client: unsafe { c } - server: unsafe { s } - } - unsafe { - lines.free() - flags.free() - get_tokens.free() - seckey.free() - key.free() - } - return server_handshake, server_client -} - -// read_handshake_str returns the handshake response -fn (mut ws Client) read_handshake_str() ?string { - mut total_bytes_read := 0 - mut msg := [1024]byte{} - mut buffer := [1]byte{} - for total_bytes_read < 1024 { - bytes_read := ws.socket_read_ptr(&buffer[0], 1) ? - if bytes_read == 0 { - return error_with_code('unexpected no response from handshake', 5) - } - msg[total_bytes_read] = buffer[0] - total_bytes_read++ - if total_bytes_read > 5 && msg[total_bytes_read - 1] == `\n` - && msg[total_bytes_read - 2] == `\r` && msg[total_bytes_read - 3] == `\n` - && msg[total_bytes_read - 4] == `\r` { - break - } - } - res := msg[..total_bytes_read].bytestr() - return res -} - -// read_handshake reads the handshake result and check if valid -fn (mut ws Client) read_handshake(seckey string) ? { - mut msg := ws.read_handshake_str() ? - ws.check_handshake_response(msg, seckey) ? - unsafe { msg.free() } -} - -// check_handshake_response checks the response from handshake and returns -// the response and secure key provided by the websocket client -fn (mut ws Client) check_handshake_response(handshake_response string, seckey string) ? { - ws.debug_log('handshake response:\n$handshake_response') - lines := handshake_response.split_into_lines() - header := lines[0] - if !header.starts_with('HTTP/1.1 101') && !header.starts_with('HTTP/1.0 101') { - return error_with_code('handshake_handler: invalid HTTP status response code, $header', - 6) - } - for i in 1 .. lines.len { - if lines[i].len <= 0 || lines[i] == '\r\n' { - continue - } - keys := lines[i].split(':') - match keys[0] { - 'Upgrade', 'upgrade' { - ws.flags << .has_upgrade - } - 'Connection', 'connection' { - ws.flags << .has_connection - } - 'Sec-WebSocket-Accept', 'sec-websocket-accept' { - ws.debug_log('seckey: $seckey') - challenge := create_key_challenge_response(seckey) ? - ws.debug_log('challenge: $challenge, response: ${keys[1]}') - if keys[1].trim_space() != challenge { - return error_with_code('handshake_handler: Sec-WebSocket-Accept header does not match computed sha1/base64 response.', - 7) - } - ws.flags << .has_accept - unsafe { challenge.free() } - } - else {} - } - unsafe { keys.free() } - } - unsafe { lines.free() } - if ws.flags.len < 3 { - ws.close(1002, 'invalid websocket HTTP headers') ? - return error_with_code('invalid websocket HTTP headers', 8) - } -} diff --git a/vlib/x/websocket/io.v b/vlib/x/websocket/io.v deleted file mode 100644 index 5408a4ed55..0000000000 --- a/vlib/x/websocket/io.v +++ /dev/null @@ -1,100 +0,0 @@ -module websocket - -import net -import time - -// socket_read reads from socket into the provided buffer -fn (mut ws Client) socket_read(mut buffer []byte) ?int { - lock { - if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { - return error('socket_read: trying to read a closed socket') - } - if ws.is_ssl { - r := ws.ssl_conn.read_into(mut buffer) ? - return r - } else { - for { - r := ws.conn.read(mut buffer) or { - if err.code == net.err_timed_out_code { - continue - } - return err - } - return r - } - } - } - return none -} - -// socket_read reads from socket into the provided byte pointer and length -fn (mut ws Client) socket_read_ptr(buf_ptr &byte, len int) ?int { - lock { - if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { - return error('socket_read_ptr: trying to read a closed socket') - } - if ws.is_ssl { - r := ws.ssl_conn.socket_read_into_ptr(buf_ptr, len) ? - return r - } else { - for { - r := ws.conn.read_ptr(buf_ptr, len) or { - if err.code == net.err_timed_out_code { - continue - } - return err - } - return r - } - } - } - return none -} - -// socket_write writes the provided byte array to the socket -fn (mut ws Client) socket_write(bytes []byte) ?int { - lock { - if ws.state == .closed || ws.conn.sock.handle <= 1 { - ws.debug_log('socket_write: Socket allready closed') - return error('socket_write: trying to write on a closed socket') - } - if ws.is_ssl { - return ws.ssl_conn.write(bytes) - } else { - for { - n := ws.conn.write(bytes) or { - if err.code == net.err_timed_out_code { - continue - } - return err - } - return n - } - panic('reached unreachable code') - } - } -} - -// shutdown_socket shuts down the socket properly when connection is closed -fn (mut ws Client) shutdown_socket() ? { - ws.debug_log('shutting down socket') - if ws.is_ssl { - ws.ssl_conn.shutdown() ? - } else { - ws.conn.close() ? - } -} - -// dial_socket connects tcp socket and initializes default configurations -fn (mut ws Client) dial_socket() ?&net.TcpConn { - tcp_address := '$ws.uri.hostname:$ws.uri.port' - mut t := net.dial_tcp(tcp_address) ? - optval := int(1) - t.sock.set_option_int(.keep_alive, optval) ? - t.set_read_timeout(30 * time.second) - t.set_write_timeout(30 * time.second) - if ws.is_ssl { - ws.ssl_conn.connect(mut t, ws.uri.hostname) ? - } - return t -} diff --git a/vlib/x/websocket/message.v b/vlib/x/websocket/message.v deleted file mode 100644 index 4c57232e92..0000000000 --- a/vlib/x/websocket/message.v +++ /dev/null @@ -1,295 +0,0 @@ -module websocket - -import encoding.utf8 - -const ( - header_len_offset = 2 // offset for lengthpart of websocket header - buffer_size = 256 // default buffer size - extended_payload16_end_byte = 4 // header length with 16-bit extended payload - extended_payload64_end_byte = 10 // header length with 64-bit extended payload -) - -// Fragment represents a websocket data fragment -struct Fragment { - data []byte // included data payload data in a fragment - opcode OPCode // interpretation of the payload data -} - -// Frame represents a data frame header -struct Frame { -mut: - // length of the websocket header part - header_len int = 2 - // size of total frame - frame_size int = 2 - fin bool // true if final fragment of message - rsv1 bool // reserved for future use in websocket RFC - rsv2 bool // reserved for future use in websocket RFC - rsv3 bool // reserved for future use in websocket RFC - opcode OPCode // interpretation of the payload data - has_mask bool // true if the payload data is masked - payload_len int // payload length - masking_key [4]byte // all frames from client to server is masked with this key -} - -const ( - invalid_close_codes = [999, 1004, 1005, 1006, 1014, 1015, 1016, 1100, 2000, 2999, 5000, 65536] -) - -// validate_client validates client frame rules from RFC6455 -pub fn (mut ws Client) validate_frame(frame &Frame) ? { - if frame.rsv1 || frame.rsv2 || frame.rsv3 { - ws.close(1002, 'rsv cannot be other than 0, not negotiated') ? - return error('rsv cannot be other than 0, not negotiated') - } - if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7) - || (int(frame.opcode) >= 11 && int(frame.opcode) <= 15) { - ws.close(1002, 'use of reserved opcode') ? - return error('use of reserved opcode') - } - if frame.has_mask && !ws.is_server { - // server should never send masked frames - // to client, close connection - ws.close(1002, 'client got masked frame') ? - return error('client sent masked frame') - } - if is_control_frame(frame.opcode) { - if !frame.fin { - ws.close(1002, 'control message must not be fragmented') ? - return error('unexpected control frame with no fin') - } - if frame.payload_len > 125 { - ws.close(1002, 'control frames must not exceed 125 bytes') ? - return error('unexpected control frame payload length') - } - } - if frame.fin == false && ws.fragments.len == 0 && frame.opcode == .continuation { - err_msg := 'unexecpected continuation, there are no frames to continue, $frame' - ws.close(1002, err_msg) ? - return error(err_msg) - } -} - -// is_control_frame returns true if the frame is a control frame -fn is_control_frame(opcode OPCode) bool { - return opcode !in [.text_frame, .binary_frame, .continuation] -} - -// is_data_frame returns true if the frame is a control frame -fn is_data_frame(opcode OPCode) bool { - return opcode in [.text_frame, .binary_frame] -} - -// read_payload reads the message payload from the socket -fn (mut ws Client) read_payload(frame &Frame) ?[]byte { - if frame.payload_len == 0 { - return []byte{} - } - mut buffer := []byte{cap: frame.payload_len} - mut read_buf := [1]byte{} - mut bytes_read := 0 - for bytes_read < frame.payload_len { - len := ws.socket_read_ptr(&read_buf[0], 1) ? - if len != 1 { - return error('expected read all message, got zero') - } - bytes_read += len - buffer << read_buf[0] - } - if bytes_read != frame.payload_len { - return error('failed to read payload') - } - if frame.has_mask { - for i in 0 .. frame.payload_len { - buffer[i] ^= frame.masking_key[i % 4] & 0xff - } - } - return buffer -} - -// validate_utf_8 validates payload for valid utf8 encoding -// - Future implementation needs to support fail fast utf errors for strict autobahn conformance -fn (mut ws Client) validate_utf_8(opcode OPCode, payload []byte) ? { - if opcode in [.text_frame, .close] && !utf8.validate(payload.data, payload.len) { - ws.logger.error('malformed utf8 payload, payload len: ($payload.len)') - ws.send_error_event('Recieved malformed utf8.') - ws.close(1007, 'malformed utf8 payload') ? - return error('malformed utf8 payload') - } -} - -// read_next_message reads 1 to n frames to compose a message -pub fn (mut ws Client) read_next_message() ?Message { - for { - frame := ws.parse_frame_header() ? - ws.validate_frame(&frame) ? - frame_payload := ws.read_payload(&frame) ? - if is_control_frame(frame.opcode) { - // Control frames can interject other frames - // and need to be returned immediately - msg := Message{ - opcode: OPCode(frame.opcode) - payload: frame_payload.clone() - } - unsafe { frame_payload.free() } - return msg - } - // if the message is fragmented we just put it on fragments - // a fragment is allowed to have zero size payload - if !frame.fin { - ws.fragments << &Fragment{ - data: frame_payload.clone() - opcode: frame.opcode - } - unsafe { frame_payload.free() } - continue - } - if ws.fragments.len == 0 { - ws.validate_utf_8(frame.opcode, frame_payload) or { - ws.logger.error('UTF8 validation error: $err, len of payload($frame_payload.len)') - ws.send_error_event('UTF8 validation error: $err, len of payload($frame_payload.len)') - return err - } - msg := Message{ - opcode: OPCode(frame.opcode) - payload: frame_payload.clone() - } - unsafe { frame_payload.free() } - return msg - } - defer { - ws.fragments = [] - } - if is_data_frame(frame.opcode) { - ws.close(0, '') ? - return error('Unexpected frame opcode') - } - payload := ws.payload_from_fragments(frame_payload) ? - opcode := ws.opcode_from_fragments() - ws.validate_utf_8(opcode, payload) ? - msg := Message{ - opcode: opcode - payload: payload.clone() - } - unsafe { - frame_payload.free() - payload.free() - } - return msg - } - return none -} - -// payload_from_fragments returs the whole paylaod from fragmented message -fn (ws Client) payload_from_fragments(fin_payload []byte) ?[]byte { - mut total_size := 0 - for f in ws.fragments { - if f.data.len > 0 { - total_size += f.data.len - } - } - total_size += fin_payload.len - if total_size == 0 { - return []byte{} - } - mut total_buffer := []byte{cap: total_size} - for f in ws.fragments { - if f.data.len > 0 { - total_buffer << f.data - } - } - total_buffer << fin_payload - return total_buffer -} - -// opcode_from_fragments returns the opcode for message from the first fragment sent -fn (ws Client) opcode_from_fragments() OPCode { - return OPCode(ws.fragments[0].opcode) -} - -// parse_frame_header parses next message by decoding the incoming frames -pub fn (mut ws Client) parse_frame_header() ?Frame { - mut buffer := [256]byte{} - mut bytes_read := 0 - mut frame := Frame{} - mut rbuff := [1]byte{} - mut mask_end_byte := 0 - for ws.state == .open { - read_bytes := ws.socket_read_ptr(&rbuff[0], 1) ? - if read_bytes == 0 { - // this is probably a timeout or close - continue - } - buffer[bytes_read] = rbuff[0] - bytes_read++ - // parses the first two header bytes to get basic frame information - if bytes_read == u64(websocket.header_len_offset) { - frame.fin = (buffer[0] & 0x80) == 0x80 - frame.rsv1 = (buffer[0] & 0x40) == 0x40 - frame.rsv2 = (buffer[0] & 0x20) == 0x20 - frame.rsv3 = (buffer[0] & 0x10) == 0x10 - frame.opcode = OPCode(int(buffer[0] & 0x7F)) - frame.has_mask = (buffer[1] & 0x80) == 0x80 - frame.payload_len = buffer[1] & 0x7F - // if has mask set the byte postition where mask ends - if frame.has_mask { - mask_end_byte = if frame.payload_len < 126 { - websocket.header_len_offset + 4 - } else if frame.payload_len == 126 { - websocket.header_len_offset + 6 - } else if frame.payload_len == 127 { - websocket.header_len_offset + 12 - } else { - 0 - } // impossible - } - frame.payload_len = frame.payload_len - frame.frame_size = frame.header_len + frame.payload_len - if !frame.has_mask && frame.payload_len < 126 { - break - } - } - if frame.payload_len == 126 && bytes_read == u64(websocket.extended_payload16_end_byte) { - frame.header_len += 2 - frame.payload_len = 0 - frame.payload_len |= buffer[2] << 8 - frame.payload_len |= buffer[3] - frame.frame_size = frame.header_len + frame.payload_len - if !frame.has_mask { - break - } - } - if frame.payload_len == 127 && bytes_read == u64(websocket.extended_payload64_end_byte) { - frame.header_len += 8 - // these shift operators needs 64 bit on clang with -prod flag - mut payload_len := u64(0) - payload_len |= u64(buffer[2]) << 56 - payload_len |= u64(buffer[3]) << 48 - payload_len |= u64(buffer[4]) << 40 - payload_len |= u64(buffer[5]) << 32 - payload_len |= u64(buffer[6]) << 24 - payload_len |= u64(buffer[7]) << 16 - payload_len |= u64(buffer[8]) << 8 - payload_len |= u64(buffer[9]) - frame.payload_len = int(payload_len) - if !frame.has_mask { - break - } - } - if frame.has_mask && bytes_read == mask_end_byte { - frame.masking_key[0] = buffer[mask_end_byte - 4] - frame.masking_key[1] = buffer[mask_end_byte - 3] - frame.masking_key[2] = buffer[mask_end_byte - 2] - frame.masking_key[3] = buffer[mask_end_byte - 1] - break - } - } - return frame -} - -// unmask_sequence unmask any given sequence -fn (f Frame) unmask_sequence(mut buffer []byte) { - for i in 0 .. buffer.len { - buffer[i] ^= f.masking_key[i % 4] & 0xff - } -} diff --git a/vlib/x/websocket/uri.v b/vlib/x/websocket/uri.v deleted file mode 100644 index 7d388e17d5..0000000000 --- a/vlib/x/websocket/uri.v +++ /dev/null @@ -1,16 +0,0 @@ -module websocket - -// Uri represents an Uri for websocket connections -struct Uri { -mut: - url string // url to the websocket endpoint - hostname string // hostname of the websocket endpoint - port string // port of the websocket endpoint - resource string // resource of the websocket endpoint - querystring string // query string of the websocket endpoint -} - -// str returns the string representation of the Uri -pub fn (u Uri) str() string { - return u.url -} diff --git a/vlib/x/websocket/utils.v b/vlib/x/websocket/utils.v deleted file mode 100644 index 4e48359b06..0000000000 --- a/vlib/x/websocket/utils.v +++ /dev/null @@ -1,54 +0,0 @@ -module websocket - -import rand -import crypto.sha1 -import encoding.base64 - -// htonl64 converts payload length to header bits -fn htonl64(payload_len u64) []byte { - mut ret := []byte{len: 8} - ret[0] = byte(((payload_len & (u64(0xff) << 56)) >> 56) & 0xff) - ret[1] = byte(((payload_len & (u64(0xff) << 48)) >> 48) & 0xff) - ret[2] = byte(((payload_len & (u64(0xff) << 40)) >> 40) & 0xff) - ret[3] = byte(((payload_len & (u64(0xff) << 32)) >> 32) & 0xff) - ret[4] = byte(((payload_len & (u64(0xff) << 24)) >> 24) & 0xff) - ret[5] = byte(((payload_len & (u64(0xff) << 16)) >> 16) & 0xff) - ret[6] = byte(((payload_len & (u64(0xff) << 8)) >> 8) & 0xff) - ret[7] = byte(((payload_len & (u64(0xff) << 0)) >> 0) & 0xff) - return ret -} - -// create_masking_key returs a new masking key to use when masking websocket messages -fn create_masking_key() []byte { - mask_bit := byte(rand.intn(255)) - buf := []byte{len: 4, init: `0`} - unsafe { C.memcpy(buf.data, &mask_bit, 4) } - return buf -} - -// create_key_challenge_response creates a key challange response from security key -fn create_key_challenge_response(seckey string) ?string { - if seckey.len == 0 { - return error('unexpected seckey lengt zero') - } - guid := '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' - sha1buf := seckey + guid - shabytes := sha1buf.bytes() - hash := sha1.sum(shabytes) - b64 := base64.encode(hash) - unsafe { - hash.free() - shabytes.free() - } - return b64 -} - -// get_nonce creates a randomized array used in handshake process -fn get_nonce(nonce_size int) string { - mut nonce := []byte{len: nonce_size, cap: nonce_size} - alphanum := '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz' - for i in 0 .. nonce_size { - nonce[i] = alphanum[rand.intn(alphanum.len)] - } - return unsafe { tos(nonce.data, nonce.len) }.clone() -} diff --git a/vlib/x/websocket/websocket_client.v b/vlib/x/websocket/websocket_client.v deleted file mode 100644 index cd0a7c1167..0000000000 --- a/vlib/x/websocket/websocket_client.v +++ /dev/null @@ -1,497 +0,0 @@ -// websocket module implements websocket client and a websocket server -// attribution: @thecoderr the author of original websocket client -// advice that the implementation is deprecated and moved to the net.websocket module! -// it will be removed in later versions -[manualfree] -module websocket - -import net -import net.http -import net.openssl -import net.urllib -import time -import log -import rand - -const ( - empty_bytearr = []byte{} // used as empty response to avoid allocation -) - -// Client represents websocket client -pub struct Client { - is_server bool -mut: - ssl_conn &openssl.SSLConn // secure connection used when wss is used - flags []Flag // flags used in handshake - fragments []Fragment // current fragments - message_callbacks []MessageEventHandler // all callbacks on_message - error_callbacks []ErrorEventHandler // all callbacks on_error - open_callbacks []OpenEventHandler // all callbacks on_open - close_callbacks []CloseEventHandler // all callbacks on_close -pub: - is_ssl bool // true if secure socket is used - uri Uri // uri of current connection - id string // unique id of client -pub mut: - header http.Header // headers that will be passed when connecting - conn &net.TcpConn // underlying TCP socket connection - nonce_size int = 16 // size of nounce used for masking - panic_on_callback bool // set to true of callbacks can panic - state State // current state of connection - logger &log.Log // logger used to log messages - resource_name string // name of current resource - last_pong_ut u64 // last time in unix time we got a pong message -} - -// Flag represents different types of headers in websocket handshake -enum Flag { - has_accept // Webs - has_connection - has_upgrade -} - -// State represents the state of the websocket connection. -pub enum State { - connecting = 0 - open - closing - closed -} - -// Message represents a whole message combined from 1 to n frames -pub struct Message { -pub: - opcode OPCode // websocket frame type of this message - payload []byte // payload of the message -} - -// OPCode represents the supported websocket frame types -pub enum OPCode { - continuation = 0x00 - text_frame = 0x01 - binary_frame = 0x02 - close = 0x08 - ping = 0x09 - pong = 0x0A -} - -// new_client instance a new websocket client -[deprecated: 'use net.websocket module instead'] -pub fn new_client(address string) ?&Client { - uri := parse_uri(address) ? - return &Client{ - conn: 0 - is_server: false - ssl_conn: openssl.new_ssl_conn() - is_ssl: address.starts_with('wss') - logger: &log.Log{ - level: .info - } - uri: uri - state: .closed - id: rand.uuid_v4() - header: http.new_header() - } -} - -// connect connects to remote websocket server -pub fn (mut ws Client) connect() ? { - ws.assert_not_connected() ? - ws.set_state(.connecting) - ws.logger.info('connecting to host $ws.uri') - ws.conn = ws.dial_socket() ? - // Todo: make setting configurable - ws.conn.set_read_timeout(time.second * 30) - ws.conn.set_write_timeout(time.second * 30) - ws.handshake() ? - ws.set_state(.open) - ws.logger.info('successfully connected to host $ws.uri') - ws.send_open_event() -} - -// listen listens and processes incoming messages -pub fn (mut ws Client) listen() ? { - mut log := 'Starting client listener, server($ws.is_server)...' - ws.logger.info(log) - unsafe { log.free() } - defer { - ws.logger.info('Quit client listener, server($ws.is_server)...') - if ws.state == .open { - ws.close(1000, 'closed by client') or {} - } - } - for ws.state == .open { - msg := ws.read_next_message() or { - if ws.state in [.closed, .closing] { - return - } - ws.debug_log('failed to read next message: $err') - ws.send_error_event('failed to read next message: $err') - return err - } - if ws.state in [.closed, .closing] { - return - } - ws.debug_log('got message: $msg.opcode') - match msg.opcode { - .text_frame { - log = 'read: text' - ws.debug_log(log) - unsafe { log.free() } - ws.send_message_event(msg) - unsafe { msg.free() } - } - .binary_frame { - ws.debug_log('read: binary') - ws.send_message_event(msg) - unsafe { msg.free() } - } - .ping { - ws.debug_log('read: ping, sending pong') - ws.send_control_frame(.pong, 'PONG', msg.payload) or { - ws.logger.error('error in message callback sending PONG: $err') - ws.send_error_event('error in message callback sending PONG: $err') - if ws.panic_on_callback { - panic(err) - } - continue - } - if msg.payload.len > 0 { - unsafe { msg.free() } - } - } - .pong { - ws.debug_log('read: pong') - ws.last_pong_ut = time.now().unix - ws.send_message_event(msg) - if msg.payload.len > 0 { - unsafe { msg.free() } - } - } - .close { - log = 'read: close' - ws.debug_log(log) - unsafe { log.free() } - defer { - ws.manage_clean_close() - } - if msg.payload.len > 0 { - if msg.payload.len == 1 { - ws.close(1002, 'close payload cannot be 1 byte') ? - return error('close payload cannot be 1 byte') - } - code := (int(msg.payload[0]) << 8) + int(msg.payload[1]) - if code in invalid_close_codes { - ws.close(1002, 'invalid close code: $code') ? - return error('invalid close code: $code') - } - reason := if msg.payload.len > 2 { msg.payload[2..] } else { []byte{} } - if reason.len > 0 { - ws.validate_utf_8(.close, reason) ? - } - if ws.state !in [.closing, .closed] { - // sending close back according to spec - ws.debug_log('close with reason, code: $code, reason: $reason') - r := reason.bytestr() - ws.close(code, r) ? - } - unsafe { msg.free() } - } else { - if ws.state !in [.closing, .closed] { - ws.debug_log('close with reason, no code') - // sending close back according to spec - ws.close(1000, 'normal') ? - } - unsafe { msg.free() } - } - return - } - .continuation { - ws.logger.error('unexpected opcode continuation, nothing to continue') - ws.send_error_event('unexpected opcode continuation, nothing to continue') - ws.close(1002, 'nothing to continue') ? - return error('unexpected opcode continuation, nothing to continue') - } - } - } -} - -// manage_clean_close closes connection in a clean websocket way -fn (mut ws Client) manage_clean_close() { - ws.send_close_event(1000, 'closed by client') -} - -// ping sends ping message to server -pub fn (mut ws Client) ping() ? { - ws.send_control_frame(.ping, 'PING', []) ? -} - -// pong sends pong message to server, -pub fn (mut ws Client) pong() ? { - ws.send_control_frame(.pong, 'PONG', []) ? -} - -// write_ptr writes len bytes provided a byteptr with a websocket messagetype -pub fn (mut ws Client) write_ptr(bytes &byte, payload_len int, code OPCode) ?int { - // ws.debug_log('write_ptr code: $code') - if ws.state != .open || ws.conn.sock.handle < 1 { - // todo: send error here later - return error('trying to write on a closed socket!') - } - mut header_len := 2 + if payload_len > 125 { 2 } else { 0 } + - if payload_len > 0xffff { 6 } else { 0 } - if !ws.is_server { - header_len += 4 - } - mut header := []byte{len: header_len, init: `0`} // [`0`].repeat(header_len) - header[0] = byte(int(code)) | 0x80 - masking_key := create_masking_key() - if ws.is_server { - if payload_len <= 125 { - header[1] = byte(payload_len) - } else if payload_len > 125 && payload_len <= 0xffff { - len16 := C.htons(payload_len) - header[1] = 126 - unsafe { C.memcpy(&header[2], &len16, 2) } - } else if payload_len > 0xffff && payload_len <= 0x7fffffff { - len_bytes := htonl64(u64(payload_len)) - header[1] = 127 - unsafe { C.memcpy(&header[2], len_bytes.data, 8) } - } - } else { - if payload_len <= 125 { - header[1] = byte(payload_len | 0x80) - header[2] = masking_key[0] - header[3] = masking_key[1] - header[4] = masking_key[2] - header[5] = masking_key[3] - } else if payload_len > 125 && payload_len <= 0xffff { - len16 := C.htons(payload_len) - header[1] = (126 | 0x80) - unsafe { C.memcpy(&header[2], &len16, 2) } - header[4] = masking_key[0] - header[5] = masking_key[1] - header[6] = masking_key[2] - header[7] = masking_key[3] - } else if payload_len > 0xffff && payload_len <= 0x7fffffff { - len64 := htonl64(u64(payload_len)) - header[1] = (127 | 0x80) - unsafe { C.memcpy(&header[2], len64.data, 8) } - header[10] = masking_key[0] - header[11] = masking_key[1] - header[12] = masking_key[2] - header[13] = masking_key[3] - } else { - ws.close(1009, 'frame too large') ? - return error('frame too large') - } - } - len := header.len + payload_len - mut frame_buf := []byte{len: len} - unsafe { - C.memcpy(&frame_buf[0], &byte(header.data), header.len) - if payload_len > 0 { - C.memcpy(&frame_buf[header.len], bytes, payload_len) - } - } - if !ws.is_server { - for i in 0 .. payload_len { - frame_buf[header_len + i] ^= masking_key[i % 4] & 0xff - } - } - written_len := ws.socket_write(frame_buf) ? - unsafe { - frame_buf.free() - masking_key.free() - header.free() - } - return written_len -} - -// write writes a byte array with a websocket messagetype to socket -pub fn (mut ws Client) write(bytes []byte, code OPCode) ?int { - return ws.write_ptr(&byte(bytes.data), bytes.len, code) -} - -// write_string, writes a string with a websocket texttype to socket -[deprecated: 'use Client.write_string() instead'] -pub fn (mut ws Client) write_str(str string) ?int { - return ws.write_string(str) -} - -// write_str, writes a string with a websocket texttype to socket -pub fn (mut ws Client) write_string(str string) ?int { - return ws.write_ptr(str.str, str.len, .text_frame) -} - -// close closes the websocket connection -pub fn (mut ws Client) close(code int, message string) ? { - ws.debug_log('sending close, $code, $message') - if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { - ws.debug_log('close: Websocket allready closed ($ws.state), $message, $code handle($ws.conn.sock.handle)') - err_msg := 'Socket allready closed: $code' - return error(err_msg) - } - defer { - ws.shutdown_socket() or {} - ws.reset_state() - } - ws.set_state(.closing) - // mut code32 := 0 - if code > 0 { - code_ := C.htons(code) - message_len := message.len + 2 - mut close_frame := []byte{len: message_len} - close_frame[0] = byte(code_ & 0xFF) - close_frame[1] = byte(code_ >> 8) - // code32 = (close_frame[0] << 8) + close_frame[1] - for i in 0 .. message.len { - close_frame[i + 2] = message[i] - } - ws.send_control_frame(.close, 'CLOSE', close_frame) ? - unsafe { close_frame.free() } - } else { - ws.send_control_frame(.close, 'CLOSE', []) ? - } - ws.fragments = [] -} - -// send_control_frame sends a control frame to the server -fn (mut ws Client) send_control_frame(code OPCode, frame_typ string, payload []byte) ? { - ws.debug_log('send control frame $code, frame_type: $frame_typ') - if ws.state !in [.open, .closing] && ws.conn.sock.handle > 1 { - return error('socket is not connected') - } - header_len := if ws.is_server { 2 } else { 6 } - frame_len := header_len + payload.len - mut control_frame := []byte{len: frame_len} - mut masking_key := if !ws.is_server { create_masking_key() } else { websocket.empty_bytearr } - defer { - unsafe { - control_frame.free() - if masking_key.len > 0 { - masking_key.free() - } - } - } - control_frame[0] = byte(int(code) | 0x80) - if !ws.is_server { - control_frame[1] = byte(payload.len | 0x80) - control_frame[2] = masking_key[0] - control_frame[3] = masking_key[1] - control_frame[4] = masking_key[2] - control_frame[5] = masking_key[3] - } else { - control_frame[1] = byte(payload.len) - } - if code == .close { - if payload.len >= 2 { - if !ws.is_server { - mut parsed_payload := []byte{len: payload.len + 1} - unsafe { C.memcpy(parsed_payload.data, &payload[0], payload.len) } - parsed_payload[payload.len] = `\0` - for i in 0 .. payload.len { - control_frame[6 + i] = (parsed_payload[i] ^ masking_key[i % 4]) & 0xff - } - unsafe { parsed_payload.free() } - } else { - unsafe { C.memcpy(&control_frame[2], &payload[0], payload.len) } - } - } - } else { - if !ws.is_server { - if payload.len > 0 { - for i in 0 .. payload.len { - control_frame[header_len + i] = (payload[i] ^ masking_key[i % 4]) & 0xff - } - } - } else { - if payload.len > 0 { - unsafe { C.memcpy(&control_frame[2], &payload[0], payload.len) } - } - } - } - ws.socket_write(control_frame) or { - return error('send_control_frame: error sending $frame_typ control frame.') - } -} - -// parse_uri parses the url to a Uri -fn parse_uri(url string) ?&Uri { - u := urllib.parse(url) ? - request_uri := u.request_uri() - v := request_uri.split('?') - mut port := u.port() - uri := u.str() - if port == '' { - port = if uri.starts_with('ws://') { - '80' - } else if uri.starts_with('wss://') { - '443' - } else { - u.port() - } - } - querystring := if v.len > 1 { '?' + v[1] } else { '' } - return &Uri{ - url: url - hostname: u.hostname() - port: port - resource: v[0] - querystring: querystring - } -} - -// set_state sets current state of the websocket connection -fn (mut ws Client) set_state(state State) { - lock { - ws.state = state - } -} - -// assert_not_connected returns error if the connection is not connected -fn (ws Client) assert_not_connected() ? { - match ws.state { - .connecting { return error('connect: websocket is connecting') } - .open { return error('connect: websocket already open') } - .closing { return error('connect: reconnect on closing websocket not supported, please use new client') } - else {} - } -} - -// reset_state resets the websocket and initialize default settings -fn (mut ws Client) reset_state() { - lock { - ws.state = .closed - ws.ssl_conn = openssl.new_ssl_conn() - ws.flags = [] - ws.fragments = [] - } -} - -// debug_log handles debug logging output for client and server -fn (mut ws Client) debug_log(text string) { - if ws.is_server { - ws.logger.debug('server-> $text') - } else { - ws.logger.debug('client-> $text') - } -} - -// free handles manual free memory of Message struct -pub fn (m &Message) free() { - unsafe { m.payload.free() } -} - -// free handles manual free memory of Client struct -pub fn (c &Client) free() { - unsafe { - c.flags.free() - c.fragments.free() - c.message_callbacks.free() - c.error_callbacks.free() - c.open_callbacks.free() - c.close_callbacks.free() - c.header.free() - } -} diff --git a/vlib/x/websocket/websocket_nix.c.v b/vlib/x/websocket/websocket_nix.c.v deleted file mode 100644 index f986b98f0b..0000000000 --- a/vlib/x/websocket/websocket_nix.c.v +++ /dev/null @@ -1,10 +0,0 @@ -module websocket - -// error_code returns the error code -fn error_code() int { - return C.errno -} - -const ( - error_ewouldblock = C.EWOULDBLOCK // blocking error code -) diff --git a/vlib/x/websocket/websocket_server.v b/vlib/x/websocket/websocket_server.v deleted file mode 100644 index 61903d1f63..0000000000 --- a/vlib/x/websocket/websocket_server.v +++ /dev/null @@ -1,190 +0,0 @@ -module websocket - -import net -import net.openssl -import log -import time -import rand - -// Server represents a websocket server connection -pub struct Server { -mut: - logger &log.Log // logger used to log - ls &net.TcpListener // listener used to get incoming connection to socket - accept_client_callbacks []AcceptClientFn // accept client callback functions - message_callbacks []MessageEventHandler // new message callback functions - close_callbacks []CloseEventHandler // close message callback functions -pub: - family net.AddrFamily = .ip - port int // port used as listen to incoming connections - is_ssl bool // true if secure connection (not supported yet on server) -pub mut: - clients map[string]&ServerClient // clients connected to this server - ping_interval int = 30 // interval for sending ping to clients (seconds) - state State // current state of connection -} - -// ServerClient represents a connected client -struct ServerClient { -pub: - resource_name string // resource that the client access - client_key string // unique key of client -pub mut: - server &Server - client &Client -} - -// new_server instance a new websocket server on provided port and route -[deprecated: 'use net.websocket module instead'] -pub fn new_server(family net.AddrFamily, port int, route string) &Server { - return &Server{ - ls: 0 - family: family - port: port - logger: &log.Log{ - level: .info - } - state: .closed - } -} - -// set_ping_interval sets the interval that the server will send ping messages to clients -pub fn (mut s Server) set_ping_interval(seconds int) { - s.ping_interval = seconds -} - -// listen start listen and process to incoming connections from websocket clients -pub fn (mut s Server) listen() ? { - s.logger.info('websocket server: start listen on port $s.port') - s.ls = net.listen_tcp(s.family, ':$s.port') ? - s.set_state(.open) - go s.handle_ping() - for { - mut c := s.accept_new_client() or { continue } - go s.serve_client(mut c) - } - s.logger.info('websocket server: end listen on port $s.port') -} - -// Close closes server (not implemented yet) -fn (mut s Server) close() { - // TODO: implement close when moving to net from x.net -} - -// handle_ping sends ping to all clients every set interval -fn (mut s Server) handle_ping() { - mut clients_to_remove := []string{} - for s.state == .open { - time.sleep(s.ping_interval * time.second) - for i, _ in s.clients { - mut c := s.clients[i] - if c.client.state == .open { - c.client.ping() or { - s.logger.debug('server-> error sending ping to client') - c.client.close(1002, 'Closing connection: ping send error') or { - // we want to continue even if error - continue - } - clients_to_remove << c.client.id - } - if (time.now().unix - c.client.last_pong_ut) > s.ping_interval * 2 { - clients_to_remove << c.client.id - c.client.close(1000, 'no pong received') or { continue } - } - } - } - // TODO: replace for with s.clients.delete_all(clients_to_remove) if (https://github.com/vlang/v/pull/6020) merges - for client in clients_to_remove { - lock { - s.clients.delete(client) - } - } - clients_to_remove.clear() - } -} - -// serve_client accepts incoming connection and sets up the callbacks -fn (mut s Server) serve_client(mut c Client) ? { - c.logger.debug('server-> Start serve client ($c.id)') - defer { - c.logger.debug('server-> End serve client ($c.id)') - } - mut handshake_response, mut server_client := s.handle_server_handshake(mut c) ? - accept := s.send_connect_event(mut server_client) ? - if !accept { - s.logger.debug('server-> client not accepted') - c.shutdown_socket() ? - return - } - // the client is accepted - c.socket_write(handshake_response.bytes()) ? - lock { - s.clients[server_client.client.id] = server_client - } - s.setup_callbacks(mut server_client) - c.listen() or { - s.logger.error(err.msg) - return err - } -} - -// setup_callbacks initialize all callback functions -fn (mut s Server) setup_callbacks(mut sc ServerClient) { - if s.message_callbacks.len > 0 { - for cb in s.message_callbacks { - if cb.is_ref { - sc.client.on_message_ref(cb.handler2, cb.ref) - } else { - sc.client.on_message(cb.handler) - } - } - } - if s.close_callbacks.len > 0 { - for cb in s.close_callbacks { - if cb.is_ref { - sc.client.on_close_ref(cb.handler2, cb.ref) - } else { - sc.client.on_close(cb.handler) - } - } - } - // set standard close so we can remove client if closed - sc.client.on_close_ref(fn (mut c Client, code int, reason string, mut sc ServerClient) ? { - c.logger.debug('server-> Delete client') - lock { - sc.server.clients.delete(sc.client.id) - } - }, sc) -} - -// accept_new_client creates a new client instance for client that connects to the socket -fn (mut s Server) accept_new_client() ?&Client { - mut new_conn := s.ls.accept() ? - c := &Client{ - is_server: true - conn: new_conn - ssl_conn: openssl.new_ssl_conn() - logger: s.logger - state: .open - last_pong_ut: time.now().unix - id: rand.uuid_v4() - } - return c -} - -// set_state sets current state in a thread safe way -fn (mut s Server) set_state(state State) { - lock { - s.state = state - } -} - -// free manages manual free of memory for Server instance -pub fn (mut s Server) free() { - unsafe { - s.clients.free() - s.accept_client_callbacks.free() - s.message_callbacks.free() - s.close_callbacks.free() - } -} diff --git a/vlib/x/websocket/websocket_windows.c.v b/vlib/x/websocket/websocket_windows.c.v deleted file mode 100644 index e9f4fc3d73..0000000000 --- a/vlib/x/websocket/websocket_windows.c.v +++ /dev/null @@ -1,12 +0,0 @@ -module websocket - -import net - -// error_code returns the error code -fn error_code() int { - return C.WSAGetLastError() -} - -const ( - error_ewouldblock = net.WsaError.wsaewouldblock // blocking error code -)