mirror of
https://github.com/defold/extension-websocket.git
synced 2025-06-27 01:47:42 +02:00
first embryo
This commit is contained in:
parent
e36953369b
commit
e781c84283
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,4 +7,7 @@ Thumbs.db
|
||||
*.pyc
|
||||
.project
|
||||
.cproject
|
||||
builtins
|
||||
builtins
|
||||
lws_source
|
||||
lws_build
|
||||
*.profraw
|
@ -1,30 +0,0 @@
|
||||
function init(self)
|
||||
local s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
local reverse_s = myextension.reverse(s)
|
||||
print(reverse_s) --> ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba
|
||||
end
|
||||
|
||||
function final(self)
|
||||
-- Add finalization code here
|
||||
-- Remove this function if not needed
|
||||
end
|
||||
|
||||
function update(self, dt)
|
||||
-- Add update code here
|
||||
-- Remove this function if not needed
|
||||
end
|
||||
|
||||
function on_message(self, message_id, message, sender)
|
||||
-- Add message-handling code here
|
||||
-- Remove this function if not needed
|
||||
end
|
||||
|
||||
function on_input(self, action_id, action)
|
||||
-- Add input-handling code here
|
||||
-- Remove this function if not needed
|
||||
end
|
||||
|
||||
function on_reload(self)
|
||||
-- Add reload-handling code here
|
||||
-- Remove this function if not needed
|
||||
end
|
144
examples/assets/button.gui
Normal file
144
examples/assets/button.gui
Normal file
@ -0,0 +1,144 @@
|
||||
script: ""
|
||||
fonts {
|
||||
name: "example"
|
||||
font: "/examples/assets/fonts/example.font"
|
||||
}
|
||||
textures {
|
||||
name: "ui"
|
||||
texture: "/examples/assets/ui.atlas"
|
||||
}
|
||||
background_color {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 45.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
texture: "ui/green_button08"
|
||||
id: "button"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
layer: "below"
|
||||
inherit_alpha: true
|
||||
slice9 {
|
||||
x: 8.0
|
||||
y: 8.0
|
||||
z: 8.0
|
||||
w: 8.0
|
||||
}
|
||||
clipping_mode: CLIPPING_MODE_NONE
|
||||
clipping_visible: true
|
||||
clipping_inverted: false
|
||||
alpha: 1.0
|
||||
template_node_child: false
|
||||
size_mode: SIZE_MODE_MANUAL
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 40.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: "FOOBAR"
|
||||
font: "example"
|
||||
id: "label"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: false
|
||||
parent: "button"
|
||||
layer: "text"
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
template_node_child: false
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
layers {
|
||||
name: "below"
|
||||
}
|
||||
layers {
|
||||
name: "text"
|
||||
}
|
||||
layers {
|
||||
name: "above"
|
||||
}
|
||||
material: "/builtins/materials/gui.material"
|
||||
adjust_reference: ADJUST_REFERENCE_PARENT
|
||||
max_nodes: 512
|
3
examples/assets/fonts/example.font
Normal file
3
examples/assets/fonts/example.font
Normal file
@ -0,0 +1,3 @@
|
||||
font: "/builtins/fonts/vera_mo_bd.ttf"
|
||||
material: "/builtins/fonts/font.material"
|
||||
size: 15
|
BIN
examples/assets/images/green_button08.png
Executable file
BIN
examples/assets/images/green_button08.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 267 B |
6
examples/assets/ui.atlas
Normal file
6
examples/assets/ui.atlas
Normal file
@ -0,0 +1,6 @@
|
||||
images {
|
||||
image: "/examples/assets/images/green_button08.png"
|
||||
}
|
||||
margin: 0
|
||||
extrude_borders: 2
|
||||
inner_padding: 0
|
6
example/example.collection → examples/websocket.collection
Executable file → Normal file
6
example/example.collection → examples/websocket.collection
Executable file → Normal file
@ -1,10 +1,10 @@
|
||||
name: "main"
|
||||
name: "default"
|
||||
scale_along_z: 0
|
||||
embedded_instances {
|
||||
id: "go"
|
||||
data: "components {\n"
|
||||
" id: \"example\"\n"
|
||||
" component: \"/example/example.script\"\n"
|
||||
" id: \"gui\"\n"
|
||||
" component: \"/examples/websocket.gui\"\n"
|
||||
" position {\n"
|
||||
" x: 0.0\n"
|
||||
" y: 0.0\n"
|
724
examples/websocket.gui
Normal file
724
examples/websocket.gui
Normal file
@ -0,0 +1,724 @@
|
||||
script: "/examples/websocket.gui_script"
|
||||
fonts {
|
||||
name: "example"
|
||||
font: "/examples/assets/fonts/example.font"
|
||||
}
|
||||
textures {
|
||||
name: "ui"
|
||||
texture: "/examples/assets/ui.atlas"
|
||||
}
|
||||
background_color {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 10.0
|
||||
y: 1113.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 620.0
|
||||
y: 600.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: ""
|
||||
font: "example"
|
||||
id: "log"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_NW
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: true
|
||||
layer: ""
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
template_node_child: false
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 214.0
|
||||
y: 568.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 100.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEMPLATE
|
||||
id: "connect_ws"
|
||||
layer: ""
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
template: "/examples/assets/button.gui"
|
||||
template_node_child: false
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 45.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
texture: "ui/green_button08"
|
||||
id: "connect_ws/button"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
parent: "connect_ws"
|
||||
layer: "below"
|
||||
inherit_alpha: true
|
||||
slice9 {
|
||||
x: 8.0
|
||||
y: 8.0
|
||||
z: 8.0
|
||||
w: 8.0
|
||||
}
|
||||
clipping_mode: CLIPPING_MODE_NONE
|
||||
clipping_visible: true
|
||||
clipping_inverted: false
|
||||
alpha: 1.0
|
||||
template_node_child: true
|
||||
size_mode: SIZE_MODE_MANUAL
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 40.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: "CONNECT ws://"
|
||||
font: "example"
|
||||
id: "connect_ws/label"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: false
|
||||
parent: "connect_ws/button"
|
||||
layer: "text"
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
overridden_fields: 8
|
||||
template_node_child: true
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 320.0
|
||||
y: 438.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 100.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEMPLATE
|
||||
id: "close"
|
||||
layer: ""
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
template: "/examples/assets/button.gui"
|
||||
template_node_child: false
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 45.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
texture: "ui/green_button08"
|
||||
id: "close/button"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
parent: "close"
|
||||
layer: "below"
|
||||
inherit_alpha: true
|
||||
slice9 {
|
||||
x: 8.0
|
||||
y: 8.0
|
||||
z: 8.0
|
||||
w: 8.0
|
||||
}
|
||||
clipping_mode: CLIPPING_MODE_NONE
|
||||
clipping_visible: true
|
||||
clipping_inverted: false
|
||||
alpha: 1.0
|
||||
template_node_child: true
|
||||
size_mode: SIZE_MODE_MANUAL
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 40.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: "CLOSE"
|
||||
font: "example"
|
||||
id: "close/label"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: false
|
||||
parent: "close/button"
|
||||
layer: "text"
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
overridden_fields: 8
|
||||
template_node_child: true
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 320.0
|
||||
y: 503.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 100.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEMPLATE
|
||||
id: "send"
|
||||
layer: ""
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
template: "/examples/assets/button.gui"
|
||||
template_node_child: false
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 45.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
texture: "ui/green_button08"
|
||||
id: "send/button"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
parent: "send"
|
||||
layer: "below"
|
||||
inherit_alpha: true
|
||||
slice9 {
|
||||
x: 8.0
|
||||
y: 8.0
|
||||
z: 8.0
|
||||
w: 8.0
|
||||
}
|
||||
clipping_mode: CLIPPING_MODE_NONE
|
||||
clipping_visible: true
|
||||
clipping_inverted: false
|
||||
alpha: 1.0
|
||||
template_node_child: true
|
||||
size_mode: SIZE_MODE_MANUAL
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 40.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: "SEND"
|
||||
font: "example"
|
||||
id: "send/label"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: false
|
||||
parent: "send/button"
|
||||
layer: "text"
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
overridden_fields: 8
|
||||
template_node_child: true
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 429.0
|
||||
y: 568.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 100.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEMPLATE
|
||||
id: "connect_wss"
|
||||
layer: ""
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
template: "/examples/assets/button.gui"
|
||||
template_node_child: false
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 45.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_BOX
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
texture: "ui/green_button08"
|
||||
id: "connect_wss/button"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
parent: "connect_wss"
|
||||
layer: "below"
|
||||
inherit_alpha: true
|
||||
slice9 {
|
||||
x: 8.0
|
||||
y: 8.0
|
||||
z: 8.0
|
||||
w: 8.0
|
||||
}
|
||||
clipping_mode: CLIPPING_MODE_NONE
|
||||
clipping_visible: true
|
||||
clipping_inverted: false
|
||||
alpha: 1.0
|
||||
template_node_child: true
|
||||
size_mode: SIZE_MODE_MANUAL
|
||||
}
|
||||
nodes {
|
||||
position {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
rotation {
|
||||
x: 0.0
|
||||
y: 0.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
scale {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
size {
|
||||
x: 200.0
|
||||
y: 40.0
|
||||
z: 0.0
|
||||
w: 1.0
|
||||
}
|
||||
color {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
type: TYPE_TEXT
|
||||
blend_mode: BLEND_MODE_ALPHA
|
||||
text: "CONNECT wss://"
|
||||
font: "example"
|
||||
id: "connect_wss/label"
|
||||
xanchor: XANCHOR_NONE
|
||||
yanchor: YANCHOR_NONE
|
||||
pivot: PIVOT_CENTER
|
||||
outline {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
shadow {
|
||||
x: 1.0
|
||||
y: 1.0
|
||||
z: 1.0
|
||||
w: 1.0
|
||||
}
|
||||
adjust_mode: ADJUST_MODE_FIT
|
||||
line_break: false
|
||||
parent: "connect_wss/button"
|
||||
layer: "text"
|
||||
inherit_alpha: true
|
||||
alpha: 1.0
|
||||
outline_alpha: 1.0
|
||||
shadow_alpha: 1.0
|
||||
overridden_fields: 8
|
||||
template_node_child: true
|
||||
text_leading: 1.0
|
||||
text_tracking: 0.0
|
||||
}
|
||||
layers {
|
||||
name: "below"
|
||||
}
|
||||
layers {
|
||||
name: "dynamic"
|
||||
}
|
||||
layers {
|
||||
name: "text"
|
||||
}
|
||||
layers {
|
||||
name: "above"
|
||||
}
|
||||
material: "/builtins/materials/gui.material"
|
||||
adjust_reference: ADJUST_REFERENCE_PARENT
|
||||
max_nodes: 512
|
120
examples/websocket.gui_script
Normal file
120
examples/websocket.gui_script
Normal file
@ -0,0 +1,120 @@
|
||||
--local websocket_async = require "websocket.client_async"
|
||||
|
||||
local URL="://echo.websocket.org"
|
||||
|
||||
local function click_button(node, action)
|
||||
--print("mouse", action.x, action.y)
|
||||
|
||||
return gui.is_enabled(node) and action.released and gui.pick_node(node, action.x, action.y)
|
||||
end
|
||||
|
||||
local function update_buttons(self)
|
||||
if self.connection then
|
||||
gui.set_enabled(self.connect_ws_node, false)
|
||||
gui.set_enabled(self.connect_wss_node, false)
|
||||
gui.set_enabled(self.send_node, true)
|
||||
gui.set_enabled(self.close_node, true)
|
||||
else
|
||||
gui.set_enabled(self.connect_ws_node, true)
|
||||
gui.set_enabled(self.connect_wss_node, true)
|
||||
gui.set_enabled(self.send_node, false)
|
||||
gui.set_enabled(self.close_node, false)
|
||||
end
|
||||
end
|
||||
|
||||
local function log(...)
|
||||
local text = ""
|
||||
local len = select("#", ...)
|
||||
for i=1,len do
|
||||
text = text .. tostring(select(i, ...)) .. (i == len and "" or ", ")
|
||||
end
|
||||
|
||||
print(text)
|
||||
local node = gui.get_node("log")
|
||||
gui.set_text(node, gui.get_text(node) .. "\n" .. text)
|
||||
end
|
||||
|
||||
function init(self)
|
||||
msg.post(".", "acquire_input_focus")
|
||||
msg.post("@render:", "clear_color", { color = vmath.vector4(0.2, 0.4, 0.8, 1.0) })
|
||||
self.connect_ws_node = gui.get_node("connect_ws/button")
|
||||
self.connect_wss_node = gui.get_node("connect_wss/button")
|
||||
self.send_node = gui.get_node("send/button")
|
||||
self.close_node = gui.get_node("close/button")
|
||||
update_buttons(self)
|
||||
|
||||
self.connection = nil
|
||||
end
|
||||
|
||||
function final(self)
|
||||
msg.post(".", "release_input_focus")
|
||||
end
|
||||
|
||||
function update(self, dt)
|
||||
if self.ws then
|
||||
self.ws.step()
|
||||
end
|
||||
end
|
||||
|
||||
local function websocket_callback(self, conn, data)
|
||||
if data.event == websocket.EVENT_DISCONNECTED then
|
||||
self.connection = nil
|
||||
update_buttons(self)
|
||||
elseif data.event == websocket.EVENT_CONNECTED then
|
||||
if data.error then
|
||||
log("on_connected error", data.error)
|
||||
self.connection = nil
|
||||
end
|
||||
update_buttons(self)
|
||||
elseif data.event == websocket.EVENT_MESSAGE then
|
||||
log("Receiving: '" .. tostring(data.message) .. "'")
|
||||
end
|
||||
end
|
||||
|
||||
local function connect(self, scheme)
|
||||
local params = {
|
||||
mode = "client" --only supported mode currently
|
||||
--mode = "client",
|
||||
--protocol = "tlsv1_2",
|
||||
--verify = "none",
|
||||
--options = "all",
|
||||
}
|
||||
|
||||
local url = scheme .. URL
|
||||
|
||||
log("Connecting to " .. url)
|
||||
local conn, err = websocket.connect(url, params, websocket_callback)
|
||||
if conn == nil then
|
||||
print("Failed to connect to " .. url .. ": " .. err)
|
||||
else
|
||||
self.connection = conn
|
||||
end
|
||||
end
|
||||
|
||||
local function disconnect(self)
|
||||
if self.connection ~= nil then
|
||||
websocket.disconnect(self.connection)
|
||||
end
|
||||
self.connection = nil
|
||||
end
|
||||
|
||||
|
||||
function on_input(self, action_id, action)
|
||||
--print("MAWE", action_id)
|
||||
if click_button(self.connect_ws_node, action) then
|
||||
print("MAWE click connect_ws_node")
|
||||
connect(self, "ws")
|
||||
elseif click_button(self.connect_wss_node, action) then
|
||||
print("MAWE click connect_wss_node")
|
||||
connect(self, "wss")
|
||||
elseif click_button(self.close_node, action) then
|
||||
disconnect(self)
|
||||
elseif click_button(gui.get_node("send/button"), action) then
|
||||
local message_to_send = 'sending to server'
|
||||
local ok, was_clean, code, reason = websocket.send(self.connection, message_to_send)
|
||||
log("Sending '" .. message_to_send .. "'", ok, was_clean, code, reason)
|
||||
elseif click_button(gui.get_node("close/button"), action) then
|
||||
log("Closing")
|
||||
self.ws:close()
|
||||
end
|
||||
end
|
@ -1,5 +1,5 @@
|
||||
[bootstrap]
|
||||
main_collection = /example/example.collectionc
|
||||
main_collection = /examples/websocket.collectionc
|
||||
|
||||
[script]
|
||||
shared_state = 1
|
||||
@ -9,8 +9,9 @@ width = 960
|
||||
height = 640
|
||||
|
||||
[project]
|
||||
title = myextension
|
||||
title = extension-websocket
|
||||
_dependencies = https://github.com/GameAnalytics/defold-openssl/archive/1.0.0.zip
|
||||
|
||||
[library]
|
||||
include_dirs = myextension
|
||||
include_dirs = websocket
|
||||
|
||||
|
@ -1,2 +0,0 @@
|
||||
# C++ symbol in your extension
|
||||
name: "MyExtension"
|
@ -1,112 +0,0 @@
|
||||
// myextension.cpp
|
||||
// Extension lib defines
|
||||
#define LIB_NAME "MyExtension"
|
||||
#define MODULE_NAME "myextension"
|
||||
|
||||
// include the Defold SDK
|
||||
#include <dmsdk/sdk.h>
|
||||
|
||||
static int Reverse(lua_State* L)
|
||||
{
|
||||
// The number of expected items to be on the Lua stack
|
||||
// once this struct goes out of scope
|
||||
DM_LUA_STACK_CHECK(L, 1);
|
||||
|
||||
// Check and get parameter string from stack
|
||||
char* str = (char*)luaL_checkstring(L, 1);
|
||||
|
||||
// Reverse the string
|
||||
int len = strlen(str);
|
||||
for(int i = 0; i < len / 2; i++) {
|
||||
const char a = str[i];
|
||||
const char b = str[len - i - 1];
|
||||
str[i] = b;
|
||||
str[len - i - 1] = a;
|
||||
}
|
||||
|
||||
// Put the reverse string on the stack
|
||||
lua_pushstring(L, str);
|
||||
|
||||
// Return 1 item
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Functions exposed to Lua
|
||||
static const luaL_reg Module_methods[] =
|
||||
{
|
||||
{"reverse", Reverse},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
static void LuaInit(lua_State* L)
|
||||
{
|
||||
int top = lua_gettop(L);
|
||||
|
||||
// Register lua names
|
||||
luaL_register(L, MODULE_NAME, Module_methods);
|
||||
|
||||
lua_pop(L, 1);
|
||||
assert(top == lua_gettop(L));
|
||||
}
|
||||
|
||||
dmExtension::Result AppInitializeMyExtension(dmExtension::AppParams* params)
|
||||
{
|
||||
dmLogInfo("AppInitializeMyExtension\n");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result InitializeMyExtension(dmExtension::Params* params)
|
||||
{
|
||||
// Init Lua
|
||||
LuaInit(params->m_L);
|
||||
dmLogInfo("Registered %s Extension\n", MODULE_NAME);
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result AppFinalizeMyExtension(dmExtension::AppParams* params)
|
||||
{
|
||||
dmLogInfo("AppFinalizeMyExtension\n");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("FinalizeMyExtension\n");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result OnUpdateMyExtension(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("OnUpdateMyExtension\n");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
void OnEventMyExtension(dmExtension::Params* params, const dmExtension::Event* event)
|
||||
{
|
||||
switch(event->m_Event)
|
||||
{
|
||||
case dmExtension::EVENT_ID_ACTIVATEAPP:
|
||||
dmLogInfo("OnEventMyExtension - EVENT_ID_ACTIVATEAPP\n");
|
||||
break;
|
||||
case dmExtension::EVENT_ID_DEACTIVATEAPP:
|
||||
dmLogInfo("OnEventMyExtension - EVENT_ID_DEACTIVATEAPP\n");
|
||||
break;
|
||||
case dmExtension::EVENT_ID_ICONIFYAPP:
|
||||
dmLogInfo("OnEventMyExtension - EVENT_ID_ICONIFYAPP\n");
|
||||
break;
|
||||
case dmExtension::EVENT_ID_DEICONIFYAPP:
|
||||
dmLogInfo("OnEventMyExtension - EVENT_ID_DEICONIFYAPP\n");
|
||||
break;
|
||||
default:
|
||||
dmLogWarning("OnEventMyExtension - Unknown event id\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Defold SDK uses a macro for setting up extension entry points:
|
||||
//
|
||||
// DM_DECLARE_EXTENSION(symbol, name, app_init, app_final, init, update, on_event, final)
|
||||
|
||||
// MyExtension is the C++ symbol that holds all relevant extension data.
|
||||
// It must match the name field in the `ext.manifest`
|
||||
DM_DECLARE_EXTENSION(MyExtension, LIB_NAME, AppInitializeMyExtension, AppFinalizeMyExtension, InitializeMyExtension, OnUpdateMyExtension, OnEventMyExtension, FinalizeMyExtension)
|
8
websocket/ext.manifest
Normal file
8
websocket/ext.manifest
Normal file
@ -0,0 +1,8 @@
|
||||
# C++ symbol in your extension
|
||||
name: "Websocket"
|
||||
|
||||
platforms:
|
||||
common:
|
||||
context:
|
||||
includes: ["upload/websocket/include/wslay"]
|
||||
defines: ["FOO"]
|
10
websocket/include/wslay/config.h
Normal file
10
websocket/include/wslay/config.h
Normal file
@ -0,0 +1,10 @@
|
||||
/* This configuration file is used only by CMake build system. */
|
||||
#ifndef CONFIG_H
|
||||
#define CONFIG_H
|
||||
|
||||
#define HAVE_ARPA_INET_H
|
||||
#define HAVE_NETINET_IN_H
|
||||
/* #undef HAVE_WINSOCK2_H */
|
||||
/* #undef WORDS_BIGENDIAN */
|
||||
|
||||
#endif /* CONFIG_H */
|
772
websocket/include/wslay/wslay.h
Normal file
772
websocket/include/wslay/wslay.h
Normal file
@ -0,0 +1,772 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_H
|
||||
#define WSLAY_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
/*
|
||||
* wslay/wslayver.h is generated from wslay/wslayver.h.in by
|
||||
* configure. The projects which do not use autotools can set
|
||||
* WSLAY_VERSION macro from outside to avoid to generating wslayver.h
|
||||
*/
|
||||
#ifndef WSLAY_VERSION
|
||||
# include <wslay/wslayver.h>
|
||||
#endif /* WSLAY_VERSION */
|
||||
|
||||
enum wslay_error {
|
||||
WSLAY_ERR_WANT_READ = -100,
|
||||
WSLAY_ERR_WANT_WRITE = -101,
|
||||
WSLAY_ERR_PROTO = -200,
|
||||
WSLAY_ERR_INVALID_ARGUMENT = -300,
|
||||
WSLAY_ERR_INVALID_CALLBACK = -301,
|
||||
WSLAY_ERR_NO_MORE_MSG = -302,
|
||||
WSLAY_ERR_CALLBACK_FAILURE = -400,
|
||||
WSLAY_ERR_WOULDBLOCK = -401,
|
||||
WSLAY_ERR_NOMEM = -500
|
||||
};
|
||||
|
||||
/*
|
||||
* Status codes defined in RFC6455
|
||||
*/
|
||||
enum wslay_status_code {
|
||||
WSLAY_CODE_NORMAL_CLOSURE = 1000,
|
||||
WSLAY_CODE_GOING_AWAY = 1001,
|
||||
WSLAY_CODE_PROTOCOL_ERROR = 1002,
|
||||
WSLAY_CODE_UNSUPPORTED_DATA = 1003,
|
||||
WSLAY_CODE_NO_STATUS_RCVD = 1005,
|
||||
WSLAY_CODE_ABNORMAL_CLOSURE = 1006,
|
||||
WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA = 1007,
|
||||
WSLAY_CODE_POLICY_VIOLATION = 1008,
|
||||
WSLAY_CODE_MESSAGE_TOO_BIG = 1009,
|
||||
WSLAY_CODE_MANDATORY_EXT = 1010,
|
||||
WSLAY_CODE_INTERNAL_SERVER_ERROR = 1011,
|
||||
WSLAY_CODE_TLS_HANDSHAKE = 1015
|
||||
};
|
||||
|
||||
enum wslay_io_flags {
|
||||
/*
|
||||
* There is more data to send.
|
||||
*/
|
||||
WSLAY_MSG_MORE = 1
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function used by wslay_frame_send() function when it needs
|
||||
* to send data. The implementation of this function must send at most
|
||||
* len bytes of data in data. flags is the bitwise OR of zero or more
|
||||
* of the following flag:
|
||||
*
|
||||
* WSLAY_MSG_MORE
|
||||
* There is more data to send
|
||||
*
|
||||
* It provides some hints to tune performance and behaviour. user_data
|
||||
* is one given in wslay_frame_context_init() function. The
|
||||
* implementation of this function must return the number of bytes
|
||||
* sent. If there is an error, return -1. The return value 0 is also
|
||||
* treated an error by the library.
|
||||
*/
|
||||
typedef ssize_t (*wslay_frame_send_callback)(const uint8_t *data, size_t len,
|
||||
int flags, void *user_data);
|
||||
/*
|
||||
* Callback function used by wslay_frame_recv() function when it needs
|
||||
* more data. The implementation of this function must fill at most
|
||||
* len bytes of data into buf. The memory area of buf is allocated by
|
||||
* library and not be freed by the application code. flags is always 0
|
||||
* in this version. user_data is one given in
|
||||
* wslay_frame_context_init() function. The implementation of this
|
||||
* function must return the number of bytes filled. If there is an
|
||||
* error, return -1. The return value 0 is also treated an error by
|
||||
* the library.
|
||||
*/
|
||||
typedef ssize_t (*wslay_frame_recv_callback)(uint8_t *buf, size_t len,
|
||||
int flags, void *user_data);
|
||||
/*
|
||||
* Callback function used by wslay_frame_send() function when it needs
|
||||
* new mask key. The implementation of this function must write
|
||||
* exactly len bytes of mask key to buf. user_data is one given in
|
||||
* wslay_frame_context_init() function. The implementation of this
|
||||
* function return 0 on success. If there is an error, return -1.
|
||||
*/
|
||||
typedef int (*wslay_frame_genmask_callback)(uint8_t *buf, size_t len,
|
||||
void *user_data);
|
||||
|
||||
struct wslay_frame_callbacks {
|
||||
wslay_frame_send_callback send_callback;
|
||||
wslay_frame_recv_callback recv_callback;
|
||||
wslay_frame_genmask_callback genmask_callback;
|
||||
};
|
||||
|
||||
/*
|
||||
* The opcode defined in RFC6455.
|
||||
*/
|
||||
enum wslay_opcode {
|
||||
WSLAY_CONTINUATION_FRAME = 0x0u,
|
||||
WSLAY_TEXT_FRAME = 0x1u,
|
||||
WSLAY_BINARY_FRAME = 0x2u,
|
||||
WSLAY_CONNECTION_CLOSE = 0x8u,
|
||||
WSLAY_PING = 0x9u,
|
||||
WSLAY_PONG = 0xau
|
||||
};
|
||||
|
||||
/*
|
||||
* Macro that returns 1 if opcode is control frame opcode, otherwise
|
||||
* returns 0.
|
||||
*/
|
||||
#define wslay_is_ctrl_frame(opcode) ((opcode >> 3) & 1)
|
||||
|
||||
/*
|
||||
* Macros that represent and return reserved bits: RSV1, RSV2, RSV3.
|
||||
* These macros assume that rsv is constructed by ((RSV1 << 2) |
|
||||
* (RSV2 << 1) | RSV3)
|
||||
*/
|
||||
#define WSLAY_RSV_NONE ((uint8_t) 0)
|
||||
#define WSLAY_RSV1_BIT (((uint8_t) 1) << 2)
|
||||
#define WSLAY_RSV2_BIT (((uint8_t) 1) << 1)
|
||||
#define WSLAY_RSV3_BIT (((uint8_t) 1) << 0)
|
||||
|
||||
#define wslay_get_rsv1(rsv) ((rsv >> 2) & 1)
|
||||
#define wslay_get_rsv2(rsv) ((rsv >> 1) & 1)
|
||||
#define wslay_get_rsv3(rsv) (rsv & 1)
|
||||
|
||||
struct wslay_frame_iocb {
|
||||
/* 1 for fragmented final frame, 0 for otherwise */
|
||||
uint8_t fin;
|
||||
/*
|
||||
* reserved 3 bits. rsv = ((RSV1 << 2) | (RSV << 1) | RSV3).
|
||||
* RFC6455 requires 0 unless extensions are negotiated.
|
||||
*/
|
||||
uint8_t rsv;
|
||||
/* 4 bit opcode */
|
||||
uint8_t opcode;
|
||||
/* payload length [0, 2**63-1] */
|
||||
uint64_t payload_length;
|
||||
/* 1 for masked frame, 0 for unmasked */
|
||||
uint8_t mask;
|
||||
/* part of payload data */
|
||||
const uint8_t *data;
|
||||
/* bytes of data defined above */
|
||||
size_t data_length;
|
||||
};
|
||||
|
||||
struct wslay_frame_context;
|
||||
typedef struct wslay_frame_context *wslay_frame_context_ptr;
|
||||
|
||||
/*
|
||||
* Initializes ctx using given callbacks and user_data. This function
|
||||
* allocates memory for struct wslay_frame_context and stores the
|
||||
* result to *ctx. The callback functions specified in callbacks are
|
||||
* copied to ctx. user_data is stored in ctx and it will be passed to
|
||||
* callback functions. When the user code finished using ctx, it must
|
||||
* call wslay_frame_context_free to deallocate memory.
|
||||
*/
|
||||
int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
|
||||
const struct wslay_frame_callbacks *callbacks,
|
||||
void *user_data);
|
||||
|
||||
/*
|
||||
* Deallocates memory pointed by ctx.
|
||||
*/
|
||||
void wslay_frame_context_free(wslay_frame_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Send WebSocket frame specified in iocb. ctx must be initialized
|
||||
* using wslay_frame_context_init() function. iocb->fin must be 1 if
|
||||
* this is a fin frame, otherwise 0. iocb->rsv is reserved bits.
|
||||
* iocb->opcode must be the opcode of this frame. iocb->mask must be
|
||||
* 1 if this is masked frame, otherwise 0. iocb->payload_length is
|
||||
* the payload_length of this frame. iocb->data must point to the
|
||||
* payload data to be sent. iocb->data_length must be the length of
|
||||
* the data. This function calls send_callback function if it needs
|
||||
* to send bytes. This function calls gen_mask_callback function if
|
||||
* it needs new mask key. This function returns the number of payload
|
||||
* bytes sent. Please note that it does not include any number of
|
||||
* header bytes. If it cannot send any single bytes of payload, it
|
||||
* returns WSLAY_ERR_WANT_WRITE. If the library detects error in iocb,
|
||||
* this function returns WSLAY_ERR_INVALID_ARGUMENT. If callback
|
||||
* functions report a failure, this function returns
|
||||
* WSLAY_ERR_INVALID_CALLBACK. This function does not always send all
|
||||
* given data in iocb. If there are remaining data to be sent, adjust
|
||||
* data and data_length in iocb accordingly and call this function
|
||||
* again.
|
||||
*/
|
||||
ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
|
||||
struct wslay_frame_iocb *iocb);
|
||||
|
||||
/*
|
||||
* Receives WebSocket frame and stores it in iocb. This function
|
||||
* returns the number of payload bytes received. This does not
|
||||
* include header bytes. In this case, iocb will be populated as
|
||||
* follows: iocb->fin is 1 if received frame is fin frame, otherwise
|
||||
* 0. iocb->rsv is reserved bits of received frame. iocb->opcode is
|
||||
* opcode of received frame. iocb->mask is 1 if received frame is
|
||||
* masked, otherwise 0. iocb->payload_length is the payload length of
|
||||
* received frame. iocb->data is pointed to the buffer containing
|
||||
* received payload data. This buffer is allocated by the library and
|
||||
* must be read-only. iocb->data_length is the number of payload
|
||||
* bytes recieved. This function calls recv_callback if it needs to
|
||||
* receive additional bytes. If it cannot receive any single bytes of
|
||||
* payload, it returns WSLAY_ERR_WANT_READ. If the library detects
|
||||
* protocol violation in a received frame, this function returns
|
||||
* WSLAY_ERR_PROTO. If callback functions report a failure, this
|
||||
* function returns WSLAY_ERR_INVALID_CALLBACK. This function does
|
||||
* not always receive whole frame in a single call. If there are
|
||||
* remaining data to be received, call this function again. This
|
||||
* function ensures frame alignment.
|
||||
*/
|
||||
ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
|
||||
struct wslay_frame_iocb *iocb);
|
||||
|
||||
struct wslay_event_context;
|
||||
/* Pointer to the event-based API context */
|
||||
typedef struct wslay_event_context *wslay_event_context_ptr;
|
||||
|
||||
struct wslay_event_on_msg_recv_arg {
|
||||
/* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
|
||||
uint8_t rsv;
|
||||
/* opcode */
|
||||
uint8_t opcode;
|
||||
/* received message */
|
||||
const uint8_t *msg;
|
||||
/* message length */
|
||||
size_t msg_length;
|
||||
/*
|
||||
* Status code iff opcode == WSLAY_CONNECTION_CLOSE. If no status
|
||||
* code is included in the close control frame, it is set to 0.
|
||||
*/
|
||||
uint16_t status_code;
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_recv() when a message is
|
||||
* completely received.
|
||||
*/
|
||||
typedef void (*wslay_event_on_msg_recv_callback)
|
||||
(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
|
||||
|
||||
struct wslay_event_on_frame_recv_start_arg {
|
||||
/* fin bit; 1 for final frame, or 0. */
|
||||
uint8_t fin;
|
||||
/* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
|
||||
uint8_t rsv;
|
||||
/* opcode of the frame */
|
||||
uint8_t opcode;
|
||||
/* payload length of ths frame */
|
||||
uint64_t payload_length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_recv() when a new frame
|
||||
* starts to be received. This callback function is only invoked once
|
||||
* for each frame.
|
||||
*/
|
||||
typedef void (*wslay_event_on_frame_recv_start_callback)
|
||||
(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data);
|
||||
|
||||
struct wslay_event_on_frame_recv_chunk_arg {
|
||||
/* chunk of payload data */
|
||||
const uint8_t *data;
|
||||
/* length of data */
|
||||
size_t data_length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_recv() when a chunk of
|
||||
* frame payload is received.
|
||||
*/
|
||||
typedef void (*wslay_event_on_frame_recv_chunk_callback)
|
||||
(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data);
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_recv() when a frame is
|
||||
* completely received.
|
||||
*/
|
||||
typedef void (*wslay_event_on_frame_recv_end_callback)
|
||||
(wslay_event_context_ptr ctx, void *user_data);
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_recv() when it wants to
|
||||
* receive more data from peer. The implementation of this callback
|
||||
* function must read data at most len bytes from peer and store them
|
||||
* in buf and return the number of bytes read. flags is always 0 in
|
||||
* this version.
|
||||
*
|
||||
* If there is an error, return -1 and set error code
|
||||
* WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
|
||||
* event-based API on the whole assumes non-blocking I/O. If the cause
|
||||
* of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
|
||||
* instead. This is important because it tells wslay_event_recv() to
|
||||
* stop receiving further data and return.
|
||||
*/
|
||||
typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx,
|
||||
uint8_t *buf, size_t len,
|
||||
int flags, void *user_data);
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_send() when it wants to
|
||||
* send more data to peer. The implementation of this callback
|
||||
* function must send data at most len bytes to peer and return the
|
||||
* number of bytes sent. flags is the bitwise OR of zero or more of
|
||||
* the following flag:
|
||||
*
|
||||
* WSLAY_MSG_MORE
|
||||
* There is more data to send
|
||||
*
|
||||
* It provides some hints to tune performance and behaviour.
|
||||
*
|
||||
* If there is an error, return -1 and set error code
|
||||
* WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
|
||||
* event-based API on the whole assumes non-blocking I/O. If the cause
|
||||
* of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
|
||||
* instead. This is important because it tells wslay_event_send() to
|
||||
* stop sending data and return.
|
||||
*/
|
||||
typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx,
|
||||
const uint8_t *data, size_t len,
|
||||
int flags, void *user_data);
|
||||
|
||||
/*
|
||||
* Callback function invoked by wslay_event_send() when it wants new
|
||||
* mask key. As described in RFC6455, only the traffic from WebSocket
|
||||
* client is masked, so this callback function is only needed if an
|
||||
* event-based API is initialized for WebSocket client use.
|
||||
*/
|
||||
typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx,
|
||||
uint8_t *buf, size_t len,
|
||||
void *user_data);
|
||||
|
||||
struct wslay_event_callbacks {
|
||||
wslay_event_recv_callback recv_callback;
|
||||
wslay_event_send_callback send_callback;
|
||||
wslay_event_genmask_callback genmask_callback;
|
||||
wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback;
|
||||
wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback;
|
||||
wslay_event_on_frame_recv_end_callback on_frame_recv_end_callback;
|
||||
wslay_event_on_msg_recv_callback on_msg_recv_callback;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initializes ctx as WebSocket Server. user_data is an arbitrary
|
||||
* pointer, which is directly passed to each callback functions as
|
||||
* user_data argument.
|
||||
*
|
||||
* On success, returns 0. On error, returns one of following negative
|
||||
* values:
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int wslay_event_context_server_init
|
||||
(wslay_event_context_ptr *ctx,
|
||||
const struct wslay_event_callbacks *callbacks, void *user_data);
|
||||
|
||||
/*
|
||||
* Initializes ctx as WebSocket client. user_data is an arbitrary
|
||||
* pointer, which is directly passed to each callback functions as
|
||||
* user_data argument.
|
||||
*
|
||||
* On success, returns 0. On error, returns one of following negative
|
||||
* values:
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int wslay_event_context_client_init
|
||||
(wslay_event_context_ptr *ctx,
|
||||
const struct wslay_event_callbacks *callbacks, void *user_data);
|
||||
|
||||
/*
|
||||
* Releases allocated resources for ctx.
|
||||
*/
|
||||
void wslay_event_context_free(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Sets a bit mask of allowed reserved bits.
|
||||
* Currently only permitted values are WSLAY_RSV1_BIT to allow PMCE
|
||||
* extension (see RFC-7692) or WSLAY_RSV_NONE to disable.
|
||||
*
|
||||
* Default: WSLAY_RSV_NONE
|
||||
*/
|
||||
void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx,
|
||||
uint8_t rsv);
|
||||
|
||||
/*
|
||||
* Enables or disables buffering of an entire message for non-control
|
||||
* frames. If val is 0, buffering is enabled. Otherwise, buffering is
|
||||
* disabled. If wslay_event_on_msg_recv_callback is invoked when
|
||||
* buffering is disabled, the msg_length member of struct
|
||||
* wslay_event_on_msg_recv_arg is set to 0.
|
||||
*
|
||||
* The control frames are always buffered regardless of this function call.
|
||||
*
|
||||
* This function must not be used after the first invocation of
|
||||
* wslay_event_recv() function.
|
||||
*/
|
||||
void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val);
|
||||
|
||||
/*
|
||||
* Sets maximum length of a message that can be received. The length
|
||||
* of message is checked by wslay_event_recv() function. If the length
|
||||
* of a message is larger than this value, reading operation is
|
||||
* disabled (same effect with wslay_event_shutdown_read() call) and
|
||||
* close control frame with WSLAY_CODE_MESSAGE_TOO_BIG is queued. If
|
||||
* buffering for non-control frames is disabled, the library checks
|
||||
* each frame payload length and does not check length of entire
|
||||
* message.
|
||||
*
|
||||
* The default value is (1u << 31)-1.
|
||||
*/
|
||||
void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx,
|
||||
uint64_t val);
|
||||
|
||||
/*
|
||||
* Sets callbacks to ctx. The callbacks previouly set by this function
|
||||
* or wslay_event_context_server_init() or
|
||||
* wslay_event_context_client_init() are replaced with callbacks.
|
||||
*/
|
||||
void wslay_event_config_set_callbacks
|
||||
(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks);
|
||||
|
||||
/*
|
||||
* Receives messages from peer. When receiving
|
||||
* messages, it uses wslay_event_recv_callback function. Single call
|
||||
* of this function receives multiple messages until
|
||||
* wslay_event_recv_callback function sets error code
|
||||
* WSLAY_ERR_WOULDBLOCK.
|
||||
*
|
||||
* When close control frame is received, this function automatically
|
||||
* queues close control frame. Also this function calls
|
||||
* wslay_event_set_read_enabled() with second argument 0 to disable
|
||||
* further read from peer.
|
||||
*
|
||||
* When ping control frame is received, this function automatically
|
||||
* queues pong control frame.
|
||||
*
|
||||
* In case of a fatal errror which leads to negative return code, this
|
||||
* function calls wslay_event_set_read_enabled() with second argument
|
||||
* 0 to disable further read from peer.
|
||||
*
|
||||
* wslay_event_recv() returns 0 if it succeeds, or one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* WSLAY_ERR_CALLBACK_FAILURE
|
||||
* User defined callback function is failed.
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*
|
||||
* When negative error code is returned, application must not make any
|
||||
* further call of wslay_event_recv() and must close WebSocket
|
||||
* connection.
|
||||
*/
|
||||
int wslay_event_recv(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Sends queued messages to peer. When sending a
|
||||
* message, it uses wslay_event_send_callback function. Single call of
|
||||
* wslay_event_send() sends multiple messages until
|
||||
* wslay_event_send_callback sets error code WSLAY_ERR_WOULDBLOCK.
|
||||
*
|
||||
* If ctx is initialized for WebSocket client use, wslay_event_send()
|
||||
* uses wslay_event_genmask_callback to get new mask key.
|
||||
*
|
||||
* When a message queued using wslay_event_queue_fragmented_msg() is
|
||||
* sent, wslay_event_send() invokes
|
||||
* wslay_event_fragmented_msg_callback for that message.
|
||||
*
|
||||
* After close control frame is sent, this function calls
|
||||
* wslay_event_set_write_enabled() with second argument 0 to disable
|
||||
* further transmission to peer.
|
||||
*
|
||||
* If there are any pending messages, wslay_event_want_write() returns
|
||||
* 1, otherwise returns 0.
|
||||
*
|
||||
* In case of a fatal errror which leads to negative return code, this
|
||||
* function calls wslay_event_set_write_enabled() with second argument
|
||||
* 0 to disable further transmission to peer.
|
||||
*
|
||||
* wslay_event_send() returns 0 if it succeeds, or one of the
|
||||
* following negative error codes:
|
||||
*
|
||||
* WSLAY_ERR_CALLBACK_FAILURE
|
||||
* User defined callback function is failed.
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*
|
||||
* When negative error code is returned, application must not make any
|
||||
* further call of wslay_event_send() and must close WebSocket
|
||||
* connection.
|
||||
*/
|
||||
int wslay_event_send(wslay_event_context_ptr ctx);
|
||||
|
||||
struct wslay_event_msg {
|
||||
uint8_t opcode;
|
||||
const uint8_t *msg;
|
||||
size_t msg_length;
|
||||
};
|
||||
|
||||
/*
|
||||
* Queues message specified in arg.
|
||||
*
|
||||
* This function supports both control and non-control messages and
|
||||
* the given message is sent without fragmentation. If fragmentation
|
||||
* is needed, use wslay_event_queue_fragmented_msg() function instead.
|
||||
*
|
||||
* This function just queues a message and does not send
|
||||
* it. wslay_event_send() function call sends these queued messages.
|
||||
*
|
||||
* wslay_event_queue_msg() returns 0 if it succeeds, or returns the
|
||||
* following negative error codes:
|
||||
*
|
||||
* WSLAY_ERR_NO_MORE_MSG
|
||||
* Could not queue given message. The one of possible reason is that
|
||||
* close control frame has been queued/sent and no further queueing
|
||||
* message is not allowed.
|
||||
*
|
||||
* WSLAY_ERR_INVALID_ARGUMENT
|
||||
* The given message is invalid.
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int wslay_event_queue_msg(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_msg *arg);
|
||||
|
||||
/*
|
||||
* Extended version of wslay_event_queue_msg which allows to set reserved bits.
|
||||
*/
|
||||
int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_msg *arg, uint8_t rsv);
|
||||
|
||||
/*
|
||||
* Specify "source" to generate message.
|
||||
*/
|
||||
union wslay_event_msg_source {
|
||||
int fd;
|
||||
void *data;
|
||||
};
|
||||
|
||||
/*
|
||||
* Callback function called by wslay_event_send() to read message data
|
||||
* from source. The implementation of
|
||||
* wslay_event_fragmented_msg_callback must store at most len bytes of
|
||||
* data to buf and return the number of stored bytes. If all data is
|
||||
* read (i.e., EOF), set *eof to 1. If no data can be generated at the
|
||||
* moment, return 0. If there is an error, return -1 and set error
|
||||
* code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error().
|
||||
*/
|
||||
typedef ssize_t (*wslay_event_fragmented_msg_callback)
|
||||
(wslay_event_context_ptr ctx,
|
||||
uint8_t *buf, size_t len, const union wslay_event_msg_source *source,
|
||||
int *eof, void *user_data);
|
||||
|
||||
struct wslay_event_fragmented_msg {
|
||||
/* opcode */
|
||||
uint8_t opcode;
|
||||
/* "source" to generate message data */
|
||||
union wslay_event_msg_source source;
|
||||
/* Callback function to read message data from source. */
|
||||
wslay_event_fragmented_msg_callback read_callback;
|
||||
};
|
||||
|
||||
/*
|
||||
* Queues a fragmented message specified in arg.
|
||||
*
|
||||
* This function supports non-control messages only. For control frames,
|
||||
* use wslay_event_queue_msg() or wslay_event_queue_close().
|
||||
*
|
||||
* This function just queues a message and does not send
|
||||
* it. wslay_event_send() function call sends these queued messages.
|
||||
*
|
||||
* wslay_event_queue_fragmented_msg() returns 0 if it succeeds, or
|
||||
* returns the following negative error codes:
|
||||
*
|
||||
* WSLAY_ERR_NO_MORE_MSG
|
||||
* Could not queue given message. The one of possible reason is that
|
||||
* close control frame has been queued/sent and no further queueing
|
||||
* message is not allowed.
|
||||
*
|
||||
* WSLAY_ERR_INVALID_ARGUMENT
|
||||
* The given message is invalid.
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int wslay_event_queue_fragmented_msg
|
||||
(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg);
|
||||
|
||||
/*
|
||||
* Extended version of wslay_event_queue_fragmented_msg which allows to set
|
||||
* reserved bits.
|
||||
*/
|
||||
int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx,
|
||||
const struct wslay_event_fragmented_msg *arg, uint8_t rsv);
|
||||
|
||||
/*
|
||||
* Queues close control frame. This function is provided just for
|
||||
* convenience. wslay_event_queue_msg() can queue a close control
|
||||
* frame as well. status_code is the status code of close control
|
||||
* frame. reason is the close reason encoded in UTF-8. reason_length
|
||||
* is the length of reason in bytes. reason_length must be less than
|
||||
* 123 bytes.
|
||||
*
|
||||
* If status_code is 0, reason and reason_length is not used and close
|
||||
* control frame with zero-length payload will be queued.
|
||||
*
|
||||
* This function just queues a message and does not send
|
||||
* it. wslay_event_send() function call sends these queued messages.
|
||||
*
|
||||
* wslay_event_queue_close() returns 0 if it succeeds, or returns the
|
||||
* following negative error codes:
|
||||
*
|
||||
* WSLAY_ERR_NO_MORE_MSG
|
||||
* Could not queue given message. The one of possible reason is that
|
||||
* close control frame has been queued/sent and no further queueing
|
||||
* message is not allowed.
|
||||
*
|
||||
* WSLAY_ERR_INVALID_ARGUMENT
|
||||
* The given message is invalid.
|
||||
*
|
||||
* WSLAY_ERR_NOMEM
|
||||
* Out of memory.
|
||||
*/
|
||||
int wslay_event_queue_close(wslay_event_context_ptr ctx,
|
||||
uint16_t status_code,
|
||||
const uint8_t *reason, size_t reason_length);
|
||||
|
||||
/*
|
||||
* Sets error code to tell the library there is an error. This
|
||||
* function is typically used in user defined callback functions. See
|
||||
* the description of callback function to know which error code
|
||||
* should be used.
|
||||
*/
|
||||
void wslay_event_set_error(wslay_event_context_ptr ctx, int val);
|
||||
|
||||
/*
|
||||
* Query whehter the library want to read more data from peer.
|
||||
*
|
||||
* wslay_event_want_read() returns 1 if the library want to read more
|
||||
* data from peer, or returns 0.
|
||||
*/
|
||||
int wslay_event_want_read(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Query whehter the library want to send more data to peer.
|
||||
*
|
||||
* wslay_event_want_write() returns 1 if the library want to send more
|
||||
* data to peer, or returns 0.
|
||||
*/
|
||||
int wslay_event_want_write(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Prevents the event-based API context from reading any further data
|
||||
* from peer.
|
||||
*
|
||||
* This function may be used with wslay_event_queue_close() if the
|
||||
* application detects error in the data received and wants to fail
|
||||
* WebSocket connection.
|
||||
*/
|
||||
void wslay_event_shutdown_read(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Prevents the event-based API context from sending any further data
|
||||
* to peer.
|
||||
*/
|
||||
void wslay_event_shutdown_write(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns 1 if the event-based API context allows read operation, or
|
||||
* return 0.
|
||||
*
|
||||
* After wslay_event_shutdown_read() is called,
|
||||
* wslay_event_get_read_enabled() returns 0.
|
||||
*/
|
||||
int wslay_event_get_read_enabled(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns 1 if the event-based API context allows write operation, or
|
||||
* return 0.
|
||||
*
|
||||
* After wslay_event_shutdown_write() is called,
|
||||
* wslay_event_get_write_enabled() returns 0.
|
||||
*/
|
||||
int wslay_event_get_write_enabled(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns 1 if a close control frame has been received from peer, or
|
||||
* returns 0.
|
||||
*/
|
||||
int wslay_event_get_close_received(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns 1 if a close control frame has been sent to peer, or
|
||||
* returns 0.
|
||||
*/
|
||||
int wslay_event_get_close_sent(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns status code received in close control frame. If no close
|
||||
* control frame has not been received, returns
|
||||
* WSLAY_CODE_ABNORMAL_CLOSURE. If received close control frame has no
|
||||
* status code, returns WSLAY_CODE_NO_STATUS_RCVD.
|
||||
*/
|
||||
uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns status code sent in close control frame. If no close
|
||||
* control frame has not been sent, returns
|
||||
* WSLAY_CODE_ABNORMAL_CLOSURE. If sent close control frame has no
|
||||
* status code, returns WSLAY_CODE_NO_STATUS_RCVD.
|
||||
*/
|
||||
uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns the number of queued messages.
|
||||
*/
|
||||
size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx);
|
||||
|
||||
/*
|
||||
* Returns the sum of queued message length. It only counts the
|
||||
* message length queued using wslay_event_queue_msg() or
|
||||
* wslay_event_queue_close().
|
||||
*/
|
||||
size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WSLAY_H */
|
142
websocket/include/wslay/wslay_event.h
Normal file
142
websocket/include/wslay/wslay_event.h
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_EVENT_H
|
||||
#define WSLAY_EVENT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <wslay/wslay.h>
|
||||
|
||||
struct wslay_stack;
|
||||
struct wslay_queue;
|
||||
|
||||
struct wslay_event_byte_chunk {
|
||||
uint8_t *data;
|
||||
size_t data_length;
|
||||
};
|
||||
|
||||
struct wslay_event_imsg {
|
||||
uint8_t fin;
|
||||
uint8_t rsv;
|
||||
uint8_t opcode;
|
||||
uint32_t utf8state;
|
||||
struct wslay_queue *chunks;
|
||||
size_t msg_length;
|
||||
};
|
||||
|
||||
enum wslay_event_msg_type {
|
||||
WSLAY_NON_FRAGMENTED,
|
||||
WSLAY_FRAGMENTED
|
||||
};
|
||||
|
||||
struct wslay_event_omsg {
|
||||
uint8_t fin;
|
||||
uint8_t opcode;
|
||||
uint8_t rsv;
|
||||
enum wslay_event_msg_type type;
|
||||
|
||||
uint8_t *data;
|
||||
size_t data_length;
|
||||
|
||||
union wslay_event_msg_source source;
|
||||
wslay_event_fragmented_msg_callback read_callback;
|
||||
};
|
||||
|
||||
struct wslay_event_frame_user_data {
|
||||
wslay_event_context_ptr ctx;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
enum wslay_event_close_status {
|
||||
WSLAY_CLOSE_RECEIVED = 1 << 0,
|
||||
WSLAY_CLOSE_QUEUED = 1 << 1,
|
||||
WSLAY_CLOSE_SENT = 1 << 2
|
||||
};
|
||||
|
||||
enum wslay_event_config {
|
||||
WSLAY_CONFIG_NO_BUFFERING = 1 << 0
|
||||
};
|
||||
|
||||
struct wslay_event_context {
|
||||
/* config status, bitwise OR of enum wslay_event_config values*/
|
||||
uint32_t config;
|
||||
/* maximum message length that can be received */
|
||||
uint64_t max_recv_msg_length;
|
||||
/* 1 if initialized for server, otherwise 0 */
|
||||
uint8_t server;
|
||||
/* bitwise OR of enum wslay_event_close_status values */
|
||||
uint8_t close_status;
|
||||
/* status code in received close control frame */
|
||||
uint16_t status_code_recv;
|
||||
/* status code in sent close control frame */
|
||||
uint16_t status_code_sent;
|
||||
wslay_frame_context_ptr frame_ctx;
|
||||
/* 1 if reading is enabled, otherwise 0. Upon receiving close
|
||||
control frame this value set to 0. If any errors in read
|
||||
operation will also set this value to 0. */
|
||||
uint8_t read_enabled;
|
||||
/* 1 if writing is enabled, otherwise 0 Upon completing sending
|
||||
close control frame, this value set to 0. If any errors in write
|
||||
opration will also set this value to 0. */
|
||||
uint8_t write_enabled;
|
||||
/* imsg buffer to allow interleaved control frame between
|
||||
non-control frames. */
|
||||
struct wslay_event_imsg imsgs[2];
|
||||
/* Pointer to imsgs to indicate current used buffer. */
|
||||
struct wslay_event_imsg *imsg;
|
||||
/* payload length of frame currently being received. */
|
||||
uint64_t ipayloadlen;
|
||||
/* next byte offset of payload currently being received. */
|
||||
uint64_t ipayloadoff;
|
||||
/* error value set by user callback */
|
||||
int error;
|
||||
/* Pointer to the message currently being sent. NULL if no message
|
||||
is currently sent. */
|
||||
struct wslay_event_omsg *omsg;
|
||||
/* Queue for non-control frames */
|
||||
struct wslay_queue/*<wslay_omsg*>*/ *send_queue;
|
||||
/* Queue for control frames */
|
||||
struct wslay_queue/*<wslay_omsg*>*/ *send_ctrl_queue;
|
||||
/* Size of send_queue + size of send_ctrl_queue */
|
||||
size_t queued_msg_count;
|
||||
/* The sum of message length in send_queue */
|
||||
size_t queued_msg_length;
|
||||
/* Buffer used for fragmented messages */
|
||||
uint8_t obuf[4096];
|
||||
uint8_t *obuflimit;
|
||||
uint8_t *obufmark;
|
||||
/* payload length of frame currently being sent. */
|
||||
uint64_t opayloadlen;
|
||||
/* next byte offset of payload currently being sent. */
|
||||
uint64_t opayloadoff;
|
||||
struct wslay_event_callbacks callbacks;
|
||||
struct wslay_event_frame_user_data frame_user_data;
|
||||
void *user_data;
|
||||
uint8_t allowed_rsv_bits;
|
||||
};
|
||||
|
||||
#endif /* WSLAY_EVENT_H */
|
76
websocket/include/wslay/wslay_frame.h
Normal file
76
websocket/include/wslay/wslay_frame.h
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_FRAME_H
|
||||
#define WSLAY_FRAME_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <wslay/wslay.h>
|
||||
|
||||
enum wslay_frame_state {
|
||||
PREP_HEADER,
|
||||
SEND_HEADER,
|
||||
SEND_PAYLOAD,
|
||||
RECV_HEADER1,
|
||||
RECV_PAYLOADLEN,
|
||||
RECV_EXT_PAYLOADLEN,
|
||||
RECV_MASKKEY,
|
||||
RECV_PAYLOAD
|
||||
};
|
||||
|
||||
struct wslay_frame_opcode_memo {
|
||||
uint8_t fin;
|
||||
uint8_t opcode;
|
||||
uint8_t rsv;
|
||||
};
|
||||
|
||||
struct wslay_frame_context {
|
||||
uint8_t ibuf[4096];
|
||||
uint8_t *ibufmark;
|
||||
uint8_t *ibuflimit;
|
||||
struct wslay_frame_opcode_memo iom;
|
||||
uint64_t ipayloadlen;
|
||||
uint64_t ipayloadoff;
|
||||
uint8_t imask;
|
||||
uint8_t imaskkey[4];
|
||||
enum wslay_frame_state istate;
|
||||
size_t ireqread;
|
||||
|
||||
uint8_t oheader[14];
|
||||
uint8_t *oheadermark;
|
||||
uint8_t *oheaderlimit;
|
||||
uint64_t opayloadlen;
|
||||
uint64_t opayloadoff;
|
||||
uint8_t omask;
|
||||
uint8_t omaskkey[4];
|
||||
enum wslay_frame_state ostate;
|
||||
|
||||
struct wslay_frame_callbacks callbacks;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
#endif /* WSLAY_FRAME_H */
|
54
websocket/include/wslay/wslay_net.h
Normal file
54
websocket/include/wslay/wslay_net.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_NET_H
|
||||
#define WSLAY_NET_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <wslay/wslay.h>
|
||||
|
||||
#ifdef HAVE_ARPA_INET_H
|
||||
# include <arpa/inet.h>
|
||||
#endif /* HAVE_ARPA_INET_H */
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
# include <netinet/in.h>
|
||||
#endif /* HAVE_NETINET_IN_H */
|
||||
/* For Mingw build */
|
||||
#ifdef HAVE_WINSOCK2_H
|
||||
# include <winsock2.h>
|
||||
#endif /* HAVE_WINSOCK2_H */
|
||||
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
# define ntoh64(x) (x)
|
||||
# define hton64(x) (x)
|
||||
#else /* !WORDS_BIGENDIAN */
|
||||
uint64_t wslay_byteswap64(uint64_t x);
|
||||
# define ntoh64(x) wslay_byteswap64(x)
|
||||
# define hton64(x) wslay_byteswap64(x)
|
||||
#endif /* !WORDS_BIGENDIAN */
|
||||
|
||||
#endif /* WSLAY_NET_H */
|
53
websocket/include/wslay/wslay_queue.h
Normal file
53
websocket/include/wslay/wslay_queue.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_QUEUE_H
|
||||
#define WSLAY_QUEUE_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <wslay/wslay.h>
|
||||
|
||||
struct wslay_queue_cell {
|
||||
void *data;
|
||||
struct wslay_queue_cell *next;
|
||||
};
|
||||
|
||||
struct wslay_queue {
|
||||
struct wslay_queue_cell *top;
|
||||
struct wslay_queue_cell *tail;
|
||||
};
|
||||
|
||||
struct wslay_queue* wslay_queue_new(void);
|
||||
void wslay_queue_free(struct wslay_queue *queue);
|
||||
int wslay_queue_push(struct wslay_queue *queue, void *data);
|
||||
int wslay_queue_push_front(struct wslay_queue *queue, void *data);
|
||||
void wslay_queue_pop(struct wslay_queue *queue);
|
||||
void* wslay_queue_top(struct wslay_queue *queue);
|
||||
void* wslay_queue_tail(struct wslay_queue *queue);
|
||||
int wslay_queue_empty(struct wslay_queue *queue);
|
||||
|
||||
#endif /* WSLAY_QUEUE_H */
|
50
websocket/include/wslay/wslay_stack.h
Normal file
50
websocket/include/wslay/wslay_stack.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAY_STACK_H
|
||||
#define WSLAY_STACK_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <wslay/wslay.h>
|
||||
|
||||
struct wslay_stack_cell {
|
||||
void *data;
|
||||
struct wslay_stack_cell *next;
|
||||
};
|
||||
|
||||
struct wslay_stack {
|
||||
struct wslay_stack_cell *top;
|
||||
};
|
||||
|
||||
struct wslay_stack* wslay_stack_new();
|
||||
void wslay_stack_free(struct wslay_stack *stack);
|
||||
int wslay_stack_push(struct wslay_stack *stack, void *data);
|
||||
void wslay_stack_pop(struct wslay_stack *stack);
|
||||
void* wslay_stack_top(struct wslay_stack *stack);
|
||||
int wslay_stack_empty(struct wslay_stack *stack);
|
||||
|
||||
#endif /* WSLAY_STACK_H */
|
31
websocket/include/wslay/wslayver.h
Normal file
31
websocket/include/wslay/wslayver.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#ifndef WSLAYVER_H
|
||||
#define WSLAYVER_H
|
||||
|
||||
/* Version number of wslay release */
|
||||
#define WSLAY_VERSION "1.0.1-DEV"
|
||||
|
||||
#endif /* WSLAYVER_H */
|
174
websocket/src/connection_pool.h
Normal file
174
websocket/src/connection_pool.h
Normal file
@ -0,0 +1,174 @@
|
||||
// Copyright 2020 The Defold Foundation
|
||||
// Licensed under the Defold License version 1.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License.
|
||||
//
|
||||
// You may obtain a copy of the License, together with FAQs at
|
||||
// https://www.defold.com/license
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef DMSDK_CONNECTION_POOL
|
||||
#define DMSDK_CONNECTION_POOL
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dlib/socket.h>
|
||||
#include <dlib/dns.h>
|
||||
|
||||
/**
|
||||
* Connection pooling
|
||||
*/
|
||||
namespace dmConnectionPool
|
||||
{
|
||||
/**
|
||||
* Connection pool handle
|
||||
*/
|
||||
typedef struct ConnectionPool* HPool;
|
||||
|
||||
/**
|
||||
* Connection handle
|
||||
*/
|
||||
typedef uint32_t HConnection;
|
||||
|
||||
/**
|
||||
* Result codes
|
||||
*/
|
||||
enum Result
|
||||
{
|
||||
RESULT_OK = 0, //!< RESULT_OK
|
||||
RESULT_OUT_OF_RESOURCES = -1,//!< RESULT_OUT_OF_RESOURCES
|
||||
RESULT_SOCKET_ERROR = -2, //!< RESULT_SOCKET_ERROR
|
||||
RESULT_HANDSHAKE_FAILED = -3,//!< RESULT_HANDSHAKE_FAILED
|
||||
RESULT_SHUT_DOWN = -4, //<! RESULT_SHUT_DOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* Stats
|
||||
*/
|
||||
struct Stats
|
||||
{
|
||||
uint32_t m_Free;
|
||||
uint32_t m_Connected;
|
||||
uint32_t m_InUse;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parameters
|
||||
*/
|
||||
struct Params
|
||||
{
|
||||
Params()
|
||||
{
|
||||
m_MaxConnections = 64;
|
||||
m_MaxKeepAlive = 10;
|
||||
}
|
||||
|
||||
/// Max connection in pool
|
||||
uint32_t m_MaxConnections;
|
||||
/// Default max-keep-alive time in seconds
|
||||
uint32_t m_MaxKeepAlive;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new connection pool
|
||||
* @param params
|
||||
* @param pool
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result New(const Params* params, HPool* pool);
|
||||
|
||||
/**
|
||||
* Delete connnection pool
|
||||
* @param pool
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Delete(HPool pool);
|
||||
|
||||
/**
|
||||
* Set max keep-alive for sockets in seconds. Sockets older than max_keep_alive
|
||||
* are not reused and hence closed
|
||||
* @param pool
|
||||
* @param max_keep_alive
|
||||
*/
|
||||
void SetMaxKeepAlive(HPool pool, uint32_t max_keep_alive);
|
||||
|
||||
/**
|
||||
* Get statistics
|
||||
* @param pool
|
||||
* @param stats
|
||||
*/
|
||||
void GetStats(HPool pool, Stats* stats);
|
||||
|
||||
/**
|
||||
* Connection to a host/port
|
||||
* @param pool pool
|
||||
* @param host host
|
||||
* @param port port
|
||||
* @param dns_channel The DNS channel that will be used for translating the host to an address
|
||||
* @param ssl true for ssl connection
|
||||
* @param timeout The timeout (micro seconds) for the connection and ssl handshake
|
||||
* @param connection connection (out)
|
||||
* @param sock_res socket-result code on failure
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Dial(HPool pool, const char* host, uint16_t port, dmDNS::HChannel dns_channel, bool ssl, int timeout, HConnection* connection, dmSocket::Result* sock_res);
|
||||
|
||||
/**
|
||||
* Return connection to pool
|
||||
* @param pool
|
||||
* @param connection
|
||||
*/
|
||||
void Return(HPool pool, HConnection connection);
|
||||
|
||||
/**
|
||||
* Close connection. Use this function whenever an error occur in eg http.
|
||||
* @param pool
|
||||
* @param connection
|
||||
*/
|
||||
void Close(HPool pool, HConnection connection);
|
||||
|
||||
/**
|
||||
* Get socket for connection
|
||||
* @param pool
|
||||
* @param connection
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
dmSocket::Socket GetSocket(HPool pool, HConnection connection);
|
||||
|
||||
/**
|
||||
* Get ssl-handle. The returned value is an mbedtls_ssl_context* (mbedtls)
|
||||
* @param pool
|
||||
* @param connection
|
||||
* @return An mbedtls_ssl_context* pointer on success
|
||||
*/
|
||||
void* GetSSLConnection(HPool pool, HConnection connection);
|
||||
|
||||
/**
|
||||
* Get reuse count for a connection
|
||||
* @param pool
|
||||
* @param connection
|
||||
* @return reuse count
|
||||
*/
|
||||
uint32_t GetReuseCount(HPool pool, HConnection connection);
|
||||
|
||||
/**
|
||||
* Shuts down all open sockets in the pool and block new connection attempts. The function can be
|
||||
* called repeatedly on the same pool until it returns no more connections in use.
|
||||
*
|
||||
* @param pool pool
|
||||
* @param how shutdown type to pass to socket shutdown function
|
||||
* @return current number of connections in use
|
||||
*/
|
||||
uint32_t Shutdown(HPool pool, dmSocket::ShutdownType how);
|
||||
|
||||
/**
|
||||
* Reopen the pool from a Shutdown call so it allows Dialing again. This function is here so the pool can be reset
|
||||
* during testing, or subsequent tests will break when the pool has been put in shutdown mode.
|
||||
*/
|
||||
void Reopen(HPool pool);
|
||||
|
||||
}
|
||||
|
||||
#endif // #ifndef DMSDK_CONNECTION_POOL
|
93
websocket/src/dns.h
Normal file
93
websocket/src/dns.h
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright 2020 The Defold Foundation
|
||||
// Licensed under the Defold License version 1.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License.
|
||||
//
|
||||
// You may obtain a copy of the License, together with FAQs at
|
||||
// https://www.defold.com/license
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef DM_DNS_H
|
||||
#define DM_DNS_H
|
||||
|
||||
namespace dmSocket
|
||||
{
|
||||
struct Address;
|
||||
}
|
||||
|
||||
namespace dmDNS
|
||||
{
|
||||
/**
|
||||
* A channel roughly translates to a socket on which to put the name lookup requests on.
|
||||
* Internal implementation resides in dns.cpp.
|
||||
*/
|
||||
struct Channel;
|
||||
typedef Channel* HChannel;
|
||||
|
||||
enum Result
|
||||
{
|
||||
RESULT_OK = 0,
|
||||
RESULT_INIT_ERROR = -1,
|
||||
RESULT_HOST_NOT_FOUND = -2,
|
||||
RESULT_CANCELLED = -3,
|
||||
RESULT_UNKNOWN_ERROR = -4
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the DNS system. Some platforms require socket initialization to be called
|
||||
* before this function. I.e: call dmSocket::Initialize() before dmDNS::Initialize() on win32.
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Initialize();
|
||||
|
||||
/**
|
||||
* Finalize DNS system.
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Finalize();
|
||||
|
||||
/**
|
||||
* Creates a new channel that can be used for DNS queries.
|
||||
* @param channel Pointer to the created channel if successfull, will be left alone otherwise
|
||||
* @return RESULT_OK on succcess
|
||||
*/
|
||||
Result NewChannel(HChannel* channel);
|
||||
|
||||
/**
|
||||
* Stops the current request (if available) on a channel.
|
||||
* @param channel Handle to the channel
|
||||
*/
|
||||
void StopChannel(HChannel channel);
|
||||
|
||||
/**
|
||||
* Deletes the current channel and cancels all requests.
|
||||
* Note: You must always make sure to call StopChannel(channel) before calling this function.
|
||||
* @param channel Handle to the channel
|
||||
*/
|
||||
void DeleteChannel(HChannel channel);
|
||||
|
||||
/**
|
||||
* Refreshes the channel configuration so that the latest DNS servers are used.
|
||||
* @param channel Handle to the channel
|
||||
*/
|
||||
Result RefreshChannel(HChannel channel);
|
||||
|
||||
/**
|
||||
* Get host by name. Note that this function is not entirely thread-safe, even though it is used in a threaded environment.
|
||||
* @param name Hostname to resolve
|
||||
* @param address Host address result
|
||||
* @param channel Channel handle to put the request on. The function does not handle invalid handles - the channel handle must exist.
|
||||
* @param ipv4 Whether or not to search for IPv4 addresses
|
||||
* @param ipv6 Whether or not to search for IPv6 addresses
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result GetHostByName(const char* name, dmSocket::Address* address, HChannel channel, bool ipv4 = true, bool ipv6 = true);
|
||||
|
||||
// Used for unit testing
|
||||
const char* ResultToString(Result r);
|
||||
}
|
||||
|
||||
#endif //DM_DSTRINGS_H
|
98
websocket/src/mbedtls/base64.h
Normal file
98
websocket/src/mbedtls/base64.h
Normal file
@ -0,0 +1,98 @@
|
||||
/**
|
||||
* \file base64.h
|
||||
*
|
||||
* \brief RFC 1521 base64 encoding/decoding
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
* not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||
*/
|
||||
#ifndef MBEDTLS_BASE64_H
|
||||
#define MBEDTLS_BASE64_H
|
||||
|
||||
#if !defined(MBEDTLS_CONFIG_FILE)
|
||||
#include "config.h"
|
||||
#else
|
||||
#include MBEDTLS_CONFIG_FILE
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */
|
||||
#define MBEDTLS_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* \brief Encode a buffer into base64 format
|
||||
*
|
||||
* \param dst destination buffer
|
||||
* \param dlen size of the destination buffer
|
||||
* \param olen number of bytes written
|
||||
* \param src source buffer
|
||||
* \param slen amount of data to be encoded
|
||||
*
|
||||
* \return 0 if successful, or MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL.
|
||||
* *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
* If that length cannot be represented, then no data is
|
||||
* written to the buffer and *olen is set to the maximum
|
||||
* length representable as a size_t.
|
||||
*
|
||||
* \note Call this function with dlen = 0 to obtain the
|
||||
* required buffer size in *olen
|
||||
*/
|
||||
int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
|
||||
const unsigned char *src, size_t slen );
|
||||
|
||||
/**
|
||||
* \brief Decode a base64-formatted buffer
|
||||
*
|
||||
* \param dst destination buffer (can be NULL for checking size)
|
||||
* \param dlen size of the destination buffer
|
||||
* \param olen number of bytes written
|
||||
* \param src source buffer
|
||||
* \param slen amount of data to be decoded
|
||||
*
|
||||
* \return 0 if successful, MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL, or
|
||||
* MBEDTLS_ERR_BASE64_INVALID_CHARACTER if the input data is
|
||||
* not correct. *olen is always updated to reflect the amount
|
||||
* of data that has (or would have) been written.
|
||||
*
|
||||
* \note Call this function with *dst = NULL or dlen = 0 to obtain
|
||||
* the required buffer size in *olen
|
||||
*/
|
||||
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen,
|
||||
const unsigned char *src, size_t slen );
|
||||
|
||||
#if defined(MBEDTLS_SELF_TEST)
|
||||
/**
|
||||
* \brief Checkup routine
|
||||
*
|
||||
* \return 0 if successful, or 1 if the test failed
|
||||
*/
|
||||
int mbedtls_base64_self_test( int verbose );
|
||||
|
||||
#endif /* MBEDTLS_SELF_TEST */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* base64.h */
|
3551
websocket/src/mbedtls/config.h
Normal file
3551
websocket/src/mbedtls/config.h
Normal file
File diff suppressed because it is too large
Load Diff
79
websocket/src/script_util.cpp
Normal file
79
websocket/src/script_util.cpp
Normal file
@ -0,0 +1,79 @@
|
||||
#include "script_util.h"
|
||||
|
||||
namespace dmWebsocket {
|
||||
|
||||
bool luaL_checkbool(lua_State *L, int numArg)
|
||||
{
|
||||
bool b = false;
|
||||
if (lua_isboolean(L, numArg))
|
||||
{
|
||||
b = lua_toboolean(L, numArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
luaL_typerror(L, numArg, lua_typename(L, LUA_TBOOLEAN));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
bool luaL_checkboold(lua_State *L, int numArg, int def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return luaL_checkbool(L, numArg);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
lua_Number luaL_checknumberd(lua_State *L, int numArg, lua_Number def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return luaL_checknumber(L, numArg);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
char* luaL_checkstringd(lua_State *L, int numArg, const char* def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return (char*)luaL_checkstring(L, numArg);
|
||||
}
|
||||
return (char*)def;
|
||||
}
|
||||
|
||||
lua_Number luaL_checktable_number(lua_State *L, int numArg, const char* field, lua_Number def)
|
||||
{
|
||||
lua_Number result = def;
|
||||
if(lua_istable(L, numArg))
|
||||
{
|
||||
lua_getfield(L, numArg, field);
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
result = luaL_checknumber(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char* luaL_checktable_string(lua_State *L, int numArg, const char* field, char* def)
|
||||
{
|
||||
char* result = def;
|
||||
if(lua_istable(L, numArg))
|
||||
{
|
||||
lua_getfield(L, numArg, field);
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
result = (char*)luaL_checkstring(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
12
websocket/src/script_util.h
Normal file
12
websocket/src/script_util.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <dmsdk/sdk.h>
|
||||
|
||||
namespace dmWebsocket {
|
||||
bool luaL_checkbool(lua_State *L, int numArg);
|
||||
bool luaL_checkboold(lua_State *L, int numArg, int def);
|
||||
lua_Number luaL_checknumberd(lua_State *L, int numArg, lua_Number def);
|
||||
char* luaL_checkstringd(lua_State *L, int numArg, const char* def);
|
||||
lua_Number luaL_checktable_number(lua_State *L, int numArg, const char* field, lua_Number def);
|
||||
char* luaL_checktable_string(lua_State *L, int numArg, const char* field, char* def);
|
||||
} // namespace
|
570
websocket/src/socket.h
Normal file
570
websocket/src/socket.h
Normal file
@ -0,0 +1,570 @@
|
||||
// Copyright 2020 The Defold Foundation
|
||||
// Licensed under the Defold License version 1.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License.
|
||||
//
|
||||
// You may obtain a copy of the License, together with FAQs at
|
||||
// https://www.defold.com/license
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef DM_SOCKET_H
|
||||
#define DM_SOCKET_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h> // memset, memcmp
|
||||
|
||||
#if defined(__linux__) || defined(__MACH__) || defined(ANDROID) || defined(__EMSCRIPTEN__) || defined(__NX__)
|
||||
#include <sys/select.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Socket abstraction
|
||||
* @note For Recv* and Send* function ETIMEDOUT is translated to EWOULDBLOCK
|
||||
* on win32 for compatibility with BSD sockets.
|
||||
*/
|
||||
namespace dmSocket
|
||||
{
|
||||
struct Selector
|
||||
{
|
||||
fd_set m_FdSets[3];
|
||||
int m_Nfds;
|
||||
Selector();
|
||||
};
|
||||
|
||||
/**
|
||||
* Socket result
|
||||
*/
|
||||
enum Result
|
||||
{
|
||||
RESULT_OK = 0, //!< RESULT_OK
|
||||
|
||||
RESULT_ACCES = -1, //!< RESULT_ACCES
|
||||
RESULT_AFNOSUPPORT = -2, //!< RESULT_AFNOSUPPORT
|
||||
RESULT_WOULDBLOCK = -3, //!< RESULT_WOULDBLOCK
|
||||
RESULT_BADF = -4, //!< RESULT_BADF
|
||||
RESULT_CONNRESET = -5, //!< RESULT_CONNRESET
|
||||
RESULT_DESTADDRREQ = -6, //!< RESULT_DESTADDRREQ
|
||||
RESULT_FAULT = -7, //!< RESULT_FAULT
|
||||
RESULT_HOSTUNREACH = -8, //!< RESULT_HOSTUNREACH
|
||||
RESULT_INTR = -9, //!< RESULT_INTR
|
||||
RESULT_INVAL = -10, //!< RESULT_INVAL
|
||||
RESULT_ISCONN = -11, //!< RESULT_ISCONN
|
||||
RESULT_MFILE = -12, //!< RESULT_MFILE
|
||||
RESULT_MSGSIZE = -13, //!< RESULT_MSGSIZE
|
||||
RESULT_NETDOWN = -14, //!< RESULT_NETDOWN
|
||||
RESULT_NETUNREACH = -15, //!< RESULT_NETUNREACH
|
||||
//RESULT_NFILE = -16,
|
||||
RESULT_NOBUFS = -17, //!< RESULT_NOBUFS
|
||||
//RESULT_NOENT = -18,
|
||||
//RESULT_NOMEM = -19,
|
||||
RESULT_NOTCONN = -20, //!< RESULT_NOTCONN
|
||||
//RESULT_NOTDIR = -21,
|
||||
RESULT_NOTSOCK = -22, //!< RESULT_NOTSOCK
|
||||
RESULT_OPNOTSUPP = -23, //!< RESULT_OPNOTSUPP
|
||||
RESULT_PIPE = -24, //!< RESULT_PIPE
|
||||
RESULT_PROTONOSUPPORT = -25, //!< RESULT_PROTONOSUPPORT
|
||||
RESULT_PROTOTYPE = -26, //!< RESULT_PROTOTYPE
|
||||
RESULT_TIMEDOUT = -27, //!< RESULT_TIMEDOUT
|
||||
|
||||
RESULT_ADDRNOTAVAIL = -28, //!< RESULT_ADDRNOTAVAIL
|
||||
RESULT_CONNREFUSED = -29, //!< RESULT_CONNREFUSED
|
||||
RESULT_ADDRINUSE = -30, //!< RESULT_ADDRINUSE
|
||||
RESULT_CONNABORTED = -31, //!< RESULT_CONNABORTED
|
||||
RESULT_INPROGRESS = -32, //!< RESULT_INPROGRESS
|
||||
|
||||
// gethostbyname errors
|
||||
RESULT_HOST_NOT_FOUND = -100, //!< RESULT_HOST_NOT_FOUND
|
||||
RESULT_TRY_AGAIN = -101, //!< RESULT_TRY_AGAIN
|
||||
RESULT_NO_RECOVERY = -102, //!< RESULT_NO_RECOVERY
|
||||
RESULT_NO_DATA = -103, //!< RESULT_NO_DATA
|
||||
|
||||
RESULT_UNKNOWN = -1000,//!< RESULT_UNKNOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* Socket handle
|
||||
* @note Use INVALID_SOCKET_HANDLE instead of zero for unset values. This is an exception
|
||||
* from all other handles.
|
||||
*/
|
||||
typedef int Socket;
|
||||
|
||||
enum SelectorKind
|
||||
{
|
||||
SELECTOR_KIND_READ = 0,
|
||||
SELECTOR_KIND_WRITE = 1,
|
||||
SELECTOR_KIND_EXCEPT = 2,
|
||||
};
|
||||
|
||||
enum Flags
|
||||
{
|
||||
FLAGS_UP = (1 << 0),
|
||||
FLAGS_RUNNING = (1 << 1),
|
||||
FLAGS_INET = (1 << 2),
|
||||
FLAGS_LINK = (1 << 3),
|
||||
};
|
||||
|
||||
/**
|
||||
* Invalid socket handle
|
||||
*/
|
||||
const Socket INVALID_SOCKET_HANDLE = 0xffffffff;
|
||||
|
||||
/**
|
||||
* Domain type
|
||||
*/
|
||||
enum Domain
|
||||
{
|
||||
DOMAIN_MISSING, //!< DOMAIN_MISSING
|
||||
DOMAIN_IPV4, //!< DOMAIN_IPV4
|
||||
DOMAIN_IPV6, //!< DOMAIN_IPV6
|
||||
DOMAIN_UNKNOWN, //!< DOMAIN_UNKNOWN
|
||||
};
|
||||
|
||||
/**
|
||||
* Socket type
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
TYPE_STREAM, //!< TYPE_STREAM
|
||||
TYPE_DGRAM, //!< TYPE_DGRAM
|
||||
};
|
||||
|
||||
/**
|
||||
* Network protocol
|
||||
*/
|
||||
enum Protocol
|
||||
{
|
||||
PROTOCOL_TCP, //!< PROTOCOL_TCP
|
||||
PROTOCOL_UDP, //!< PROTOCOL_UDP
|
||||
};
|
||||
|
||||
/**
|
||||
* Socket shutdown type
|
||||
*/
|
||||
enum ShutdownType
|
||||
{
|
||||
SHUTDOWNTYPE_READ,
|
||||
SHUTDOWNTYPE_WRITE,
|
||||
SHUTDOWNTYPE_READWRITE,
|
||||
};
|
||||
|
||||
/**
|
||||
* Network address
|
||||
* Network addresses were previously represented as an uint32_t, but in
|
||||
* order to support IPv6 the internal representation was changed to a
|
||||
* struct.
|
||||
*/
|
||||
struct Address
|
||||
{
|
||||
|
||||
Address() {
|
||||
m_family = dmSocket::DOMAIN_MISSING;
|
||||
memset(m_address, 0x0, sizeof(m_address));
|
||||
}
|
||||
|
||||
Domain m_family;
|
||||
uint32_t m_address[4];
|
||||
};
|
||||
|
||||
/**
|
||||
* Comparison operators for dmSocket::Address (network address).
|
||||
* These operators are required since network code was initially designed
|
||||
* with the assumption that addresses were stored as uint32_t (IPv4), and
|
||||
* thus sortable.
|
||||
*/
|
||||
inline bool operator==(const Address& lhs, const Address& rhs)
|
||||
{
|
||||
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) == 0;
|
||||
}
|
||||
|
||||
inline bool operator< (const Address& lhs, const Address& rhs)
|
||||
{
|
||||
return memcmp(lhs.m_address, rhs.m_address, sizeof(lhs.m_address)) < 0;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Address& lhs, const Address& rhs) { return !operator==(lhs,rhs); }
|
||||
inline bool operator> (const Address& lhs, const Address& rhs) { return operator< (rhs,lhs); }
|
||||
inline bool operator<=(const Address& lhs, const Address& rhs) { return !operator> (lhs,rhs); }
|
||||
inline bool operator>=(const Address& lhs, const Address& rhs) { return !operator< (lhs,rhs); }
|
||||
|
||||
struct IfAddr
|
||||
{
|
||||
char m_Name[128];
|
||||
uint32_t m_Flags;
|
||||
Address m_Address;
|
||||
uint8_t m_MacAddress[6];
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize socket system. Network initialization is required on some platforms.
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Initialize();
|
||||
|
||||
/**
|
||||
* Finalize socket system.
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Finalize();
|
||||
|
||||
/**
|
||||
* Create a new socket. Corresponds to BSD socket function socket().
|
||||
* @note SIGPIPE is disabled on applicable platforms. This has the implication
|
||||
* that Receive can return zero bytes when the connection is closed by remote peer.
|
||||
* @param type Soccket type
|
||||
* @param protocol Protocol
|
||||
* @param socket Pointer to created socket
|
||||
* @return RESULT_OK on succcess
|
||||
*/
|
||||
Result New(Domain domain, Type type, enum Protocol protocol, Socket* socket);
|
||||
|
||||
/**
|
||||
* Delete a socket. Corresponds to BSD socket function close()
|
||||
* @param socket Socket to close
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Delete(Socket socket);
|
||||
|
||||
/**
|
||||
* Get underlying file descriptor
|
||||
* @param socket socket to get fd for
|
||||
* @return file-descriptor
|
||||
*/
|
||||
int GetFD(Socket socket);
|
||||
|
||||
/**
|
||||
* Set reuse socket address option on socket. Socket option SO_REUSEADDR on most platforms
|
||||
* @param socket Socket to set reuse address to
|
||||
* @param reuse True if reuse
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetReuseAddress(Socket socket, bool reuse);
|
||||
|
||||
|
||||
/**
|
||||
* Set broadcast address option on socket. Socket option SO_BROADCAST on most platforms.
|
||||
* @param socket Socket to set reuse address to
|
||||
* @param broadcast True if broadcast
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetBroadcast(Socket socket, bool broadcast);
|
||||
|
||||
/**
|
||||
* Set blocking option on a socket
|
||||
* @param socket Socket to set blocking on
|
||||
* @param blocking True to block
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetBlocking(Socket socket, bool blocking);
|
||||
|
||||
/**
|
||||
* Set TCP_NODELAY on socket
|
||||
* @param socket Socket to set TCP_NODELAY on
|
||||
* @param no_delay True for no delay
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetNoDelay(Socket socket, bool no_delay);
|
||||
|
||||
/**
|
||||
* Set socket send timeout
|
||||
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
|
||||
* larger than or equal to 1000.
|
||||
* @param socket socket
|
||||
* @param timeout timeout in microseconds
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetSendTimeout(Socket socket, uint64_t timeout);
|
||||
|
||||
/**
|
||||
* Set socket receive timeout
|
||||
* @note Timeout resolution might be in milliseconds, e.g. windows. Use values
|
||||
* larger than or equal to 1000
|
||||
* @param socket socket
|
||||
* @param timeout timeout in microseconds
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SetReceiveTimeout(Socket socket, uint64_t timeout);
|
||||
|
||||
/**
|
||||
* Add multicast membership
|
||||
* @param socket socket to add membership on
|
||||
* @param multi_addr multicast address
|
||||
* @param interface_addr interface address
|
||||
* @param ttl multicast package time to live
|
||||
* @return RESULT_OK
|
||||
*/
|
||||
Result AddMembership(Socket socket, Address multi_addr, Address interface_addr, int ttl);
|
||||
|
||||
/**
|
||||
* Set address for outgoing multicast datagrams
|
||||
* @param socket socket to set multicast address for
|
||||
* @param address address of network interface to use
|
||||
* @return RESULT_OK
|
||||
*/
|
||||
Result SetMulticastIf(Socket socket, Address address);
|
||||
|
||||
/**
|
||||
* Accept a connection on a socket
|
||||
* @param socket Socket to accept connections on
|
||||
* @param address Result address parameter
|
||||
* @param accept_socket Pointer to accepted socket (result)
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Accept(Socket socket, Address* address, Socket* accept_socket);
|
||||
|
||||
/**
|
||||
* Bind a name to a socket
|
||||
* @param socket Socket to bind name to
|
||||
* @param address Address to bind
|
||||
* @param port Port to bind to
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Bind(Socket socket, Address address, int port);
|
||||
|
||||
/**
|
||||
* Initiate a connection on a socket
|
||||
* @param socket Socket to initiate connection on
|
||||
* @param address Address to connect to
|
||||
* @param port Port to connect to
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Connect(Socket socket, Address address, int port);
|
||||
|
||||
/**
|
||||
* Listen for connections on a socket
|
||||
* @param socket Socket to listen on
|
||||
* @param backlog Maximum length for the queue of pending connections
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Listen(Socket socket, int backlog);
|
||||
|
||||
/**
|
||||
* Shutdown part of a socket connection
|
||||
* @param socket Socket to shutdown connection ow
|
||||
* @param how Shutdown type
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Shutdown(Socket socket, ShutdownType how);
|
||||
|
||||
/**
|
||||
* Send a message on a socket
|
||||
* @param socket Socket to send a message on
|
||||
* @param buffer Buffer to send
|
||||
* @param length Length of buffer to send
|
||||
* @param sent_bytes Number of bytes sent (result)
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Send(Socket socket, const void* buffer, int length, int* sent_bytes);
|
||||
|
||||
/**
|
||||
* Send a message to a specific address
|
||||
* @param socket Socket to send a message on
|
||||
* @param buffer Buffer to send
|
||||
* @param length Length of buffer to send
|
||||
* @param sent_bytes Number of bytes sent (result)
|
||||
* @param to_addr To address
|
||||
* @param to_port From addres
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result SendTo(Socket socket, const void* buffer, int length, int* sent_bytes, Address to_addr, uint16_t to_port);
|
||||
|
||||
/**
|
||||
* Receive data on a socket
|
||||
* @param socket Socket to receive data on
|
||||
* @param buffer Buffer to receive to
|
||||
* @param length Receive buffer length
|
||||
* @param received_bytes Number of received bytes (result)
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Receive(Socket socket, void* buffer, int length, int* received_bytes);
|
||||
|
||||
/**
|
||||
* Receive from socket
|
||||
* @param socket Socket to receive data on
|
||||
* @param buffer Buffer to receive to
|
||||
* @param length Receive buffer length
|
||||
* @param received_bytes Number of received bytes (result)
|
||||
* @param from_addr From address (result)
|
||||
* @param from_port To address (result)
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result ReceiveFrom(Socket socket, void* buffer, int length, int* received_bytes,
|
||||
Address* from_addr, uint16_t* from_port);
|
||||
|
||||
|
||||
/**
|
||||
* Get name, address and port for socket
|
||||
* @param socket Socket to get name for
|
||||
* @param address Address (result)
|
||||
* @param port Socket (result)
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result GetName(Socket socket, Address*address, uint16_t* port);
|
||||
|
||||
/**
|
||||
* Get local hostname
|
||||
* @param hostname hostname buffer
|
||||
* @param hostname_length hostname buffer length
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result GetHostname(char* hostname, int hostname_length);
|
||||
|
||||
/**
|
||||
* Get first local IP address
|
||||
* The function tries to determine the local IP address. If several
|
||||
* IP addresses are available only a single is returned
|
||||
* @note This function might fallback to 127.0.0.1 if no adapter is found
|
||||
* Sometimes it might be appropriate to run this function periodically
|
||||
* @param address address result
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result GetLocalAddress(Address* address);
|
||||
|
||||
/**
|
||||
* Get address from ip string
|
||||
* @param address IP-string
|
||||
* @return Address
|
||||
*/
|
||||
Address AddressFromIPString(const char* address);
|
||||
|
||||
/**
|
||||
* Convert address to ip string
|
||||
* @param address address to convert
|
||||
* @return IP string. The caller is responsible to free the string using free()
|
||||
*/
|
||||
char* AddressToIPString(Address address);
|
||||
|
||||
/**
|
||||
* Get host by name.
|
||||
* @param name Hostname to resolve
|
||||
* @param address Host address result
|
||||
* @param ipv4 Whether or not to search for IPv4 addresses
|
||||
* @param ipv6 Whether or not to search for IPv6 addresses
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result GetHostByName(const char* name, Address* address, bool ipv4 = true, bool ipv6 = true);
|
||||
|
||||
/**
|
||||
* Get information about network adapters (loopback devices are not included)
|
||||
* @note Make sure that addresses is large enough. If too small
|
||||
* the result is capped.
|
||||
* @param addresses array of if-addresses
|
||||
* @param addresses_count count
|
||||
* @param count actual count
|
||||
*/
|
||||
void GetIfAddresses(IfAddr* addresses, uint32_t addresses_count, uint32_t* count);
|
||||
|
||||
/**
|
||||
* Convert result value to string
|
||||
* @param result Result to convert
|
||||
* @return Result as string
|
||||
*/
|
||||
const char* ResultToString(Result result);
|
||||
|
||||
/**
|
||||
* Converts a native result (error) to dmSocket::Result
|
||||
* Also logs the error
|
||||
* @param filename the file that calls this function
|
||||
* @param line the line number of this call
|
||||
* @param r the native result
|
||||
* @return Result
|
||||
*/
|
||||
Result NativeToResult(const char* filename, int line, int r);
|
||||
|
||||
/**
|
||||
* Check if a network address is empty (all zeroes).
|
||||
* @param address The address to check
|
||||
* @return True if the address is empty, false otherwise
|
||||
*/
|
||||
bool Empty(Address address);
|
||||
|
||||
/**
|
||||
* Return a pointer to the IPv4 buffer of address.
|
||||
* @note Make sure the address family of address is actually AF_INET before
|
||||
* attempting to retrieve the IPv4 buffer, otherwise an assert will trigger.
|
||||
* @param address Pointer to the address containing the buffer
|
||||
* @return Pointer to the buffer that holds the IPv4 address
|
||||
*/
|
||||
uint32_t* IPv4(Address* address);
|
||||
|
||||
/**
|
||||
* Return a pointer to the IPv6 buffer of address.
|
||||
* @note Make sure the address family of address is actually AF_INET6 before
|
||||
* attempting to retrieve the IPv6 buffer, otherwise an assert will trigger.
|
||||
* @param address Pointer to the address containing the buffer
|
||||
* @return Pointer to the buffer that holds the IPv6 address
|
||||
*/
|
||||
uint32_t* IPv6(Address* address);
|
||||
|
||||
/**
|
||||
* Checks if a socket was created for IPv4 (AF_INET).
|
||||
* @param socket The socket to check
|
||||
* @return True if the socket was created for IPv4 communication, false otherwise
|
||||
*/
|
||||
bool IsSocketIPv4(Socket socket);
|
||||
|
||||
/**
|
||||
* Checks if a socket was created for IPv6 (AF_INET6).
|
||||
* @param socket The socket to check
|
||||
* @return True if the socket was created for IPv6 communication, false otherwise
|
||||
*/
|
||||
bool IsSocketIPv6(Socket socket);
|
||||
|
||||
/**
|
||||
* Calculate the number of bits that differs between address a and b.
|
||||
* @note This is used for the Hamming Distance.
|
||||
* @param a The first address to compare
|
||||
* @param b The second address to compare
|
||||
* @return Number of bits that differs between a and b
|
||||
*/
|
||||
uint32_t BitDifference(Address a, Address b);
|
||||
|
||||
struct Selector;
|
||||
|
||||
/**
|
||||
* Clear selector for socket. Similar to FD_CLR
|
||||
* @param selector Selector
|
||||
* @param selector_kind Kind to clear
|
||||
* @param socket Socket to clear
|
||||
*/
|
||||
void SelectorClear(Selector* selector, SelectorKind selector_kind, Socket socket);
|
||||
|
||||
/**
|
||||
* Set selector for socket. Similar to FD_SET
|
||||
* @param selector Selector
|
||||
* @param selector_kind Kind to clear
|
||||
* @param socket Socket to set
|
||||
*/
|
||||
void SelectorSet(Selector* selector, SelectorKind selector_kind, Socket socket);
|
||||
|
||||
/**
|
||||
* Check if selector is set. Similar to FD_ISSET
|
||||
* @param selector Selector
|
||||
* @param selector_kind Selector kind
|
||||
* @param socket Socket to check for
|
||||
* @return True if set.
|
||||
*/
|
||||
bool SelectorIsSet(Selector* selector, SelectorKind selector_kind, Socket socket);
|
||||
|
||||
/**
|
||||
* Clear selector (all kinds). Similar to FD_ZERO
|
||||
* @param selector Selector
|
||||
*/
|
||||
void SelectorZero(Selector* selector);
|
||||
|
||||
/**
|
||||
* Select for pending data
|
||||
* @param selector Selector
|
||||
* @param timeout Timeout. For blocking pass -1
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Select(Selector* selector, int32_t timeout);
|
||||
}
|
||||
|
||||
#endif // DM_SOCKET_H
|
85
websocket/src/uri.h
Normal file
85
websocket/src/uri.h
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2020 The Defold Foundation
|
||||
// Licensed under the Defold License version 1.0 (the "License"); you may not use
|
||||
// this file except in compliance with the License.
|
||||
//
|
||||
// You may obtain a copy of the License, together with FAQs at
|
||||
// https://www.defold.com/license
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software distributed
|
||||
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
// specific language governing permissions and limitations under the License.
|
||||
|
||||
#ifndef DM_URI_H
|
||||
#define DM_URI_H
|
||||
|
||||
namespace dmURI
|
||||
{
|
||||
/**
|
||||
* URI parsing result
|
||||
*/
|
||||
enum Result
|
||||
{
|
||||
RESULT_OK,//!< RESULT_OK
|
||||
};
|
||||
|
||||
const uint32_t MAX_SCHEME_LEN = 8;
|
||||
const uint32_t MAX_LOCATION_LEN = 64;
|
||||
const uint32_t MAX_PATH_LEN = 2048;
|
||||
// Maximum length of an URI
|
||||
// scheme :// location / path
|
||||
const uint32_t MAX_URI_LEN = MAX_SCHEME_LEN + 3 + MAX_LOCATION_LEN + 1 + MAX_PATH_LEN;
|
||||
|
||||
|
||||
/**
|
||||
* URI parsing result parts
|
||||
*/
|
||||
struct Parts
|
||||
{
|
||||
/// Scheme parts, eg http
|
||||
char m_Scheme[MAX_SCHEME_LEN];
|
||||
|
||||
/// Location part, eg foo.com:80
|
||||
char m_Location[MAX_LOCATION_LEN];
|
||||
|
||||
/// Hostname part of location, eg foo.com
|
||||
char m_Hostname[MAX_LOCATION_LEN];
|
||||
|
||||
/// Port part of location, eg 80. -1 if not present
|
||||
int m_Port;
|
||||
|
||||
/// Path part, eg index.html
|
||||
// Increased from 512 to 2048 (DEF-3410). 2048 seems like a reasonable
|
||||
// number based on the following SO answer: https://stackoverflow.com/a/417184/1266551
|
||||
char m_Path[MAX_PATH_LEN];
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse URI and split in three parts. (scheme, location, path)
|
||||
* @note This is a simplified URI parser and does not conform to rfc2396.
|
||||
* Missing features are: parameters, query, fragment part of URI and support for escaped sequences
|
||||
* @note For http m_Port is set to 80 if not specified in uri.
|
||||
* @param uri URI to parse
|
||||
* @param parts Result
|
||||
* @return RESULT_OK on success
|
||||
*/
|
||||
Result Parse(const char* uri, Parts* parts);
|
||||
|
||||
/**
|
||||
* Performs URL encoding of the supplied buffer
|
||||
* @param src String to encode
|
||||
* @param dst Encoded string
|
||||
* @param dst_size size of the provided out buffer
|
||||
*/
|
||||
void Encode(const char* src, char* dst, uint32_t dst_size);
|
||||
|
||||
/**
|
||||
* Undoes URL decoding on a buffer.
|
||||
* @note The output will never be larger than the input.
|
||||
* @param src Input
|
||||
* @param dst Decoded output
|
||||
*/
|
||||
void Decode(const char* src, char* dst);
|
||||
}
|
||||
|
||||
#endif // DM_URI_H
|
1056
websocket/src/websocket.cpp
Normal file
1056
websocket/src/websocket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1027
websocket/src/wslay/wslay_event.c
Normal file
1027
websocket/src/wslay/wslay_event.c
Normal file
File diff suppressed because it is too large
Load Diff
340
websocket/src/wslay/wslay_frame.c
Normal file
340
websocket/src/wslay/wslay_frame.c
Normal file
@ -0,0 +1,340 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "wslay_frame.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "wslay_net.h"
|
||||
|
||||
#define wslay_min(A, B) (((A) < (B)) ? (A) : (B))
|
||||
|
||||
int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
|
||||
const struct wslay_frame_callbacks *callbacks,
|
||||
void *user_data)
|
||||
{
|
||||
*ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context));
|
||||
if(*ctx == NULL) {
|
||||
return -1;
|
||||
}
|
||||
memset(*ctx, 0, sizeof(struct wslay_frame_context));
|
||||
(*ctx)->istate = RECV_HEADER1;
|
||||
(*ctx)->ireqread = 2;
|
||||
(*ctx)->ostate = PREP_HEADER;
|
||||
(*ctx)->user_data = user_data;
|
||||
(*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf;
|
||||
(*ctx)->callbacks = *callbacks;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wslay_frame_context_free(wslay_frame_context_ptr ctx)
|
||||
{
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
|
||||
struct wslay_frame_iocb *iocb)
|
||||
{
|
||||
if(iocb->data_length > iocb->payload_length) {
|
||||
return WSLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if(ctx->ostate == PREP_HEADER) {
|
||||
uint8_t *hdptr = ctx->oheader;
|
||||
memset(ctx->oheader, 0, sizeof(ctx->oheader));
|
||||
*hdptr |= (iocb->fin << 7) & 0x80u;
|
||||
*hdptr |= (iocb->rsv << 4) & 0x70u;
|
||||
*hdptr |= iocb->opcode & 0xfu;
|
||||
++hdptr;
|
||||
*hdptr |= (iocb->mask << 7) & 0x80u;
|
||||
if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
|
||||
return WSLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if(iocb->payload_length < 126) {
|
||||
*hdptr |= iocb->payload_length;
|
||||
++hdptr;
|
||||
} else if(iocb->payload_length < (1 << 16)) {
|
||||
uint16_t len = htons(iocb->payload_length);
|
||||
*hdptr |= 126;
|
||||
++hdptr;
|
||||
memcpy(hdptr, &len, 2);
|
||||
hdptr += 2;
|
||||
} else if(iocb->payload_length < (1ull << 63)) {
|
||||
uint64_t len = hton64(iocb->payload_length);
|
||||
*hdptr |= 127;
|
||||
++hdptr;
|
||||
memcpy(hdptr, &len, 8);
|
||||
hdptr += 8;
|
||||
} else {
|
||||
/* Too large payload length */
|
||||
return WSLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
if(iocb->mask) {
|
||||
if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
|
||||
ctx->user_data) != 0) {
|
||||
return WSLAY_ERR_INVALID_CALLBACK;
|
||||
} else {
|
||||
ctx->omask = 1;
|
||||
memcpy(hdptr, ctx->omaskkey, 4);
|
||||
hdptr += 4;
|
||||
}
|
||||
}
|
||||
ctx->ostate = SEND_HEADER;
|
||||
ctx->oheadermark = ctx->oheader;
|
||||
ctx->oheaderlimit = hdptr;
|
||||
ctx->opayloadlen = iocb->payload_length;
|
||||
ctx->opayloadoff = 0;
|
||||
}
|
||||
if(ctx->ostate == SEND_HEADER) {
|
||||
ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark;
|
||||
ssize_t r;
|
||||
int flags = 0;
|
||||
if(iocb->data_length > 0) {
|
||||
flags |= WSLAY_MSG_MORE;
|
||||
};
|
||||
r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
|
||||
ctx->user_data);
|
||||
if(r > 0) {
|
||||
if(r > len) {
|
||||
return WSLAY_ERR_INVALID_CALLBACK;
|
||||
} else {
|
||||
ctx->oheadermark += r;
|
||||
if(ctx->oheadermark == ctx->oheaderlimit) {
|
||||
ctx->ostate = SEND_PAYLOAD;
|
||||
} else {
|
||||
return WSLAY_ERR_WANT_WRITE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return WSLAY_ERR_WANT_WRITE;
|
||||
}
|
||||
}
|
||||
if(ctx->ostate == SEND_PAYLOAD) {
|
||||
size_t totallen = 0;
|
||||
if(iocb->data_length > 0) {
|
||||
if(ctx->omask) {
|
||||
uint8_t temp[4096];
|
||||
const uint8_t *datamark = iocb->data,
|
||||
*datalimit = iocb->data+iocb->data_length;
|
||||
while(datamark < datalimit) {
|
||||
size_t datalen = datalimit - datamark;
|
||||
const uint8_t *writelimit = datamark+
|
||||
wslay_min(sizeof(temp), datalen);
|
||||
size_t writelen = writelimit-datamark;
|
||||
ssize_t r;
|
||||
size_t i;
|
||||
for(i = 0; i < writelen; ++i) {
|
||||
temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4];
|
||||
}
|
||||
r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
|
||||
if(r > 0) {
|
||||
if((size_t)r > writelen) {
|
||||
return WSLAY_ERR_INVALID_CALLBACK;
|
||||
} else {
|
||||
datamark += r;
|
||||
ctx->opayloadoff += r;
|
||||
totallen += r;
|
||||
}
|
||||
} else {
|
||||
if(totallen > 0) {
|
||||
break;
|
||||
} else {
|
||||
return WSLAY_ERR_WANT_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ssize_t r;
|
||||
r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
|
||||
ctx->user_data);
|
||||
if(r > 0) {
|
||||
if((size_t)r > iocb->data_length) {
|
||||
return WSLAY_ERR_INVALID_CALLBACK;
|
||||
} else {
|
||||
ctx->opayloadoff += r;
|
||||
totallen = r;
|
||||
}
|
||||
} else {
|
||||
return WSLAY_ERR_WANT_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ctx->opayloadoff == ctx->opayloadlen) {
|
||||
ctx->ostate = PREP_HEADER;
|
||||
}
|
||||
return totallen;
|
||||
}
|
||||
return WSLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
static void wslay_shift_ibuf(wslay_frame_context_ptr ctx)
|
||||
{
|
||||
ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark;
|
||||
memmove(ctx->ibuf, ctx->ibufmark, len);
|
||||
ctx->ibuflimit = ctx->ibuf+len;
|
||||
ctx->ibufmark = ctx->ibuf;
|
||||
}
|
||||
|
||||
static ssize_t wslay_recv(wslay_frame_context_ptr ctx)
|
||||
{
|
||||
ssize_t r;
|
||||
if(ctx->ibufmark != ctx->ibuf) {
|
||||
wslay_shift_ibuf(ctx);
|
||||
}
|
||||
r = ctx->callbacks.recv_callback
|
||||
(ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit,
|
||||
0, ctx->user_data);
|
||||
if(r > 0) {
|
||||
ctx->ibuflimit += r;
|
||||
} else {
|
||||
r = WSLAY_ERR_WANT_READ;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
#define WSLAY_AVAIL_IBUF(ctx) ((size_t)(ctx->ibuflimit - ctx->ibufmark))
|
||||
|
||||
ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
|
||||
struct wslay_frame_iocb *iocb)
|
||||
{
|
||||
ssize_t r;
|
||||
if(ctx->istate == RECV_HEADER1) {
|
||||
uint8_t fin, opcode, rsv, payloadlen;
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
if((r = wslay_recv(ctx)) <= 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
return WSLAY_ERR_WANT_READ;
|
||||
}
|
||||
fin = (ctx->ibufmark[0] >> 7) & 1;
|
||||
rsv = (ctx->ibufmark[0] >> 4) & 7;
|
||||
opcode = ctx->ibufmark[0] & 0xfu;
|
||||
ctx->iom.opcode = opcode;
|
||||
ctx->iom.fin = fin;
|
||||
ctx->iom.rsv = rsv;
|
||||
++ctx->ibufmark;
|
||||
ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
|
||||
payloadlen = ctx->ibufmark[0] & 0x7fu;
|
||||
++ctx->ibufmark;
|
||||
if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
|
||||
return WSLAY_ERR_PROTO;
|
||||
}
|
||||
if(payloadlen == 126) {
|
||||
ctx->istate = RECV_EXT_PAYLOADLEN;
|
||||
ctx->ireqread = 2;
|
||||
} else if(payloadlen == 127) {
|
||||
ctx->istate = RECV_EXT_PAYLOADLEN;
|
||||
ctx->ireqread = 8;
|
||||
} else {
|
||||
ctx->ipayloadlen = payloadlen;
|
||||
ctx->ipayloadoff = 0;
|
||||
if(ctx->imask) {
|
||||
ctx->istate = RECV_MASKKEY;
|
||||
ctx->ireqread = 4;
|
||||
} else {
|
||||
ctx->istate = RECV_PAYLOAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ctx->istate == RECV_EXT_PAYLOADLEN) {
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
if((r = wslay_recv(ctx)) <= 0) {
|
||||
return r;
|
||||
}
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
return WSLAY_ERR_WANT_READ;
|
||||
}
|
||||
}
|
||||
ctx->ipayloadlen = 0;
|
||||
ctx->ipayloadoff = 0;
|
||||
memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread),
|
||||
ctx->ibufmark, ctx->ireqread);
|
||||
ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
|
||||
ctx->ibufmark += ctx->ireqread;
|
||||
if(ctx->ireqread == 8) {
|
||||
if(ctx->ipayloadlen < (1 << 16) ||
|
||||
ctx->ipayloadlen & (1ull << 63)) {
|
||||
return WSLAY_ERR_PROTO;
|
||||
}
|
||||
} else if(ctx->ipayloadlen < 126) {
|
||||
return WSLAY_ERR_PROTO;
|
||||
}
|
||||
if(ctx->imask) {
|
||||
ctx->istate = RECV_MASKKEY;
|
||||
ctx->ireqread = 4;
|
||||
} else {
|
||||
ctx->istate = RECV_PAYLOAD;
|
||||
}
|
||||
}
|
||||
if(ctx->istate == RECV_MASKKEY) {
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
if((r = wslay_recv(ctx)) <= 0) {
|
||||
return r;
|
||||
}
|
||||
if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
|
||||
return WSLAY_ERR_WANT_READ;
|
||||
}
|
||||
}
|
||||
memcpy(ctx->imaskkey, ctx->ibufmark, 4);
|
||||
ctx->ibufmark += 4;
|
||||
ctx->istate = RECV_PAYLOAD;
|
||||
}
|
||||
if(ctx->istate == RECV_PAYLOAD) {
|
||||
uint8_t *readlimit, *readmark;
|
||||
uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff;
|
||||
if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
|
||||
if((r = wslay_recv(ctx)) <= 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
readmark = ctx->ibufmark;
|
||||
readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
|
||||
ctx->ibuflimit : ctx->ibufmark+rempayloadlen;
|
||||
if(ctx->imask) {
|
||||
for(; ctx->ibufmark != readlimit;
|
||||
++ctx->ibufmark, ++ctx->ipayloadoff) {
|
||||
ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
|
||||
}
|
||||
} else {
|
||||
ctx->ibufmark = readlimit;
|
||||
ctx->ipayloadoff += readlimit-readmark;
|
||||
}
|
||||
iocb->fin = ctx->iom.fin;
|
||||
iocb->rsv = ctx->iom.rsv;
|
||||
iocb->opcode = ctx->iom.opcode;
|
||||
iocb->payload_length = ctx->ipayloadlen;
|
||||
iocb->mask = ctx->imask;
|
||||
iocb->data = readmark;
|
||||
iocb->data_length = ctx->ibufmark-readmark;
|
||||
if(ctx->ipayloadlen == ctx->ipayloadoff) {
|
||||
ctx->istate = RECV_HEADER1;
|
||||
ctx->ireqread = 2;
|
||||
}
|
||||
return iocb->data_length;
|
||||
}
|
||||
return WSLAY_ERR_INVALID_ARGUMENT;
|
||||
}
|
36
websocket/src/wslay/wslay_net.c
Normal file
36
websocket/src/wslay/wslay_net.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "wslay_net.h"
|
||||
|
||||
#ifndef WORDS_BIGENDIAN
|
||||
|
||||
uint64_t wslay_byteswap64(uint64_t x)
|
||||
{
|
||||
uint64_t u = ntohl(x & 0xffffffffllu);
|
||||
uint64_t l = ntohl(x >> 32);
|
||||
return (u << 32) | l;
|
||||
}
|
||||
|
||||
#endif /* !WORDS_BIGENDIAN */
|
117
websocket/src/wslay/wslay_queue.c
Normal file
117
websocket/src/wslay/wslay_queue.c
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "wslay_queue.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct wslay_queue* wslay_queue_new(void)
|
||||
{
|
||||
struct wslay_queue *queue = (struct wslay_queue*)malloc
|
||||
(sizeof(struct wslay_queue));
|
||||
if(!queue) {
|
||||
return NULL;
|
||||
}
|
||||
queue->top = queue->tail = NULL;
|
||||
return queue;
|
||||
}
|
||||
|
||||
void wslay_queue_free(struct wslay_queue *queue)
|
||||
{
|
||||
if(!queue) {
|
||||
return;
|
||||
} else {
|
||||
struct wslay_queue_cell *p = queue->top;
|
||||
while(p) {
|
||||
struct wslay_queue_cell *next = p->next;
|
||||
free(p);
|
||||
p = next;
|
||||
}
|
||||
free(queue);
|
||||
}
|
||||
}
|
||||
|
||||
int wslay_queue_push(struct wslay_queue *queue, void *data)
|
||||
{
|
||||
struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
|
||||
(sizeof(struct wslay_queue_cell));
|
||||
if(!new_cell) {
|
||||
return WSLAY_ERR_NOMEM;
|
||||
}
|
||||
new_cell->data = data;
|
||||
new_cell->next = NULL;
|
||||
if(queue->tail) {
|
||||
queue->tail->next = new_cell;
|
||||
queue->tail = new_cell;
|
||||
|
||||
} else {
|
||||
queue->top = queue->tail = new_cell;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wslay_queue_push_front(struct wslay_queue *queue, void *data)
|
||||
{
|
||||
struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
|
||||
(sizeof(struct wslay_queue_cell));
|
||||
if(!new_cell) {
|
||||
return WSLAY_ERR_NOMEM;
|
||||
}
|
||||
new_cell->data = data;
|
||||
new_cell->next = queue->top;
|
||||
queue->top = new_cell;
|
||||
if(!queue->tail) {
|
||||
queue->tail = queue->top;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wslay_queue_pop(struct wslay_queue *queue)
|
||||
{
|
||||
struct wslay_queue_cell *top = queue->top;
|
||||
assert(top);
|
||||
queue->top = top->next;
|
||||
if(top == queue->tail) {
|
||||
queue->tail = NULL;
|
||||
}
|
||||
free(top);
|
||||
}
|
||||
|
||||
void* wslay_queue_top(struct wslay_queue *queue)
|
||||
{
|
||||
assert(queue->top);
|
||||
return queue->top->data;
|
||||
}
|
||||
|
||||
void* wslay_queue_tail(struct wslay_queue *queue)
|
||||
{
|
||||
assert(queue->tail);
|
||||
return queue->tail->data;
|
||||
}
|
||||
|
||||
int wslay_queue_empty(struct wslay_queue *queue)
|
||||
{
|
||||
return queue->top == NULL;
|
||||
}
|
86
websocket/src/wslay/wslay_stack.c
Normal file
86
websocket/src/wslay/wslay_stack.c
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Wslay - The WebSocket Library
|
||||
*
|
||||
* Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
#include "wslay_stack.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct wslay_stack* wslay_stack_new()
|
||||
{
|
||||
struct wslay_stack *stack = (struct wslay_stack*)malloc
|
||||
(sizeof(struct wslay_stack));
|
||||
if(!stack) {
|
||||
return NULL;
|
||||
}
|
||||
stack->top = NULL;
|
||||
return stack;
|
||||
}
|
||||
|
||||
void wslay_stack_free(struct wslay_stack *stack)
|
||||
{
|
||||
struct wslay_stack_cell *p;
|
||||
if(!stack) {
|
||||
return;
|
||||
}
|
||||
p = stack->top;
|
||||
while(p) {
|
||||
struct wslay_stack_cell *next = p->next;
|
||||
free(p);
|
||||
p = next;
|
||||
}
|
||||
free(stack);
|
||||
}
|
||||
|
||||
int wslay_stack_push(struct wslay_stack *stack, void *data)
|
||||
{
|
||||
struct wslay_stack_cell *new_cell = (struct wslay_stack_cell*)malloc
|
||||
(sizeof(struct wslay_stack_cell));
|
||||
if(!new_cell) {
|
||||
return WSLAY_ERR_NOMEM;
|
||||
}
|
||||
new_cell->data = data;
|
||||
new_cell->next = stack->top;
|
||||
stack->top = new_cell;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wslay_stack_pop(struct wslay_stack *stack)
|
||||
{
|
||||
struct wslay_stack_cell *top = stack->top;
|
||||
assert(top);
|
||||
stack->top = top->next;
|
||||
free(top);
|
||||
}
|
||||
|
||||
void* wslay_stack_top(struct wslay_stack *stack)
|
||||
{
|
||||
assert(stack->top);
|
||||
return stack->top->data;
|
||||
}
|
||||
|
||||
int wslay_stack_empty(struct wslay_stack *stack)
|
||||
{
|
||||
return stack->top == NULL;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user