From cfebf15974b1af0f7c23f27b2d48307c1d9b5f83 Mon Sep 17 00:00:00 2001 From: Nomango Date: Tue, 30 Jul 2019 10:46:01 +0800 Subject: [PATCH] separate audio/network/imgui package --- .gitignore | 3 + Kiwano.sln | 32 +- Kiwano/audio/Player.cpp | 137 - Kiwano/audio/Sound.cpp | 219 - Kiwano/audio/Transcoder.cpp | 267 - Kiwano/audio/audio-modules.cpp | 97 - Kiwano/audio/audio-modules.h | 83 - Kiwano/imgui/ImGuiLayer.cpp | 185 - Kiwano/imgui/ImGuiView.cpp | 114 - Kiwano/third-party/dlls/websockets.dll | Bin 175616 -> 0 bytes Kiwano/third-party/libs/websockets.lib | Bin 66876 -> 0 bytes .../third-party/libwebsockets/libwebsockets.h | 5787 ----------------- Kiwano/third-party/libwebsockets/lws_config.h | 156 - {Kiwano => kiwano-audio}/kiwano-audio.h | 8 +- kiwano-audio/kiwano-audio.vcxproj | 186 + kiwano-audio/kiwano-audio.vcxproj.filters | 43 + kiwano-audio/src/Player.cpp | 140 + {Kiwano/audio => kiwano-audio/src}/Player.h | 107 +- kiwano-audio/src/Sound.cpp | 223 + {Kiwano/audio => kiwano-audio/src}/Sound.h | 87 +- kiwano-audio/src/Transcoder.cpp | 271 + .../audio => kiwano-audio/src}/Transcoder.h | 49 +- kiwano-audio/src/audio-modules.cpp | 100 + kiwano-audio/src/audio-modules.h | 86 + {Kiwano/audio => kiwano-audio/src}/audio.cpp | 104 +- {Kiwano/audio => kiwano-audio/src}/audio.h | 47 +- {Kiwano => kiwano-imgui}/kiwano-imgui.h | 6 +- kiwano-imgui/kiwano-imgui.vcxproj | 195 + kiwano-imgui/kiwano-imgui.vcxproj.filters | 76 + kiwano-imgui/src/ImGuiLayer.cpp | 188 + .../imgui => kiwano-imgui/src}/ImGuiLayer.h | 83 +- kiwano-imgui/src/ImGuiView.cpp | 117 + .../imgui => kiwano-imgui/src}/ImGuiView.h | 48 +- .../imgui => kiwano-imgui/src}/imgui_impl.hpp | 0 .../src}/imgui_impl_dx10.cpp | 0 .../src}/imgui_impl_dx10.h | 0 .../src}/imgui_impl_dx11.cpp | 0 .../src}/imgui_impl_dx11.h | 0 .../third-party/ImGui/LICENSE.txt | 0 .../third-party/ImGui/imconfig.h | 0 .../third-party/ImGui/imgui.cpp | 0 .../third-party/ImGui/imgui.h | 0 .../third-party/ImGui/imgui_demo.cpp | 0 .../third-party/ImGui/imgui_draw.cpp | 0 .../third-party/ImGui/imgui_internal.h | 0 .../third-party/ImGui/imgui_widgets.cpp | 0 .../third-party/ImGui/imstb_rectpack.h | 0 .../third-party/ImGui/imstb_textedit.h | 0 .../third-party/ImGui/imstb_truetype.h | 0 .../dlls/libcrypto-1_1.dll | Bin .../dlls/libcurl.dll | Bin .../dlls/libssl-1_1.dll | Bin {Kiwano => kiwano-network}/kiwano-network.h | 10 +- kiwano-network/kiwano-network.vcxproj | 184 + kiwano-network/kiwano-network.vcxproj.filters | 36 + .../src}/HttpClient.cpp | 2 - .../src}/HttpClient.h | 0 .../src}/HttpRequest.h | 0 .../src}/HttpResponse.h | 0 .../network => kiwano-network/src}/helper.h | 0 .../third-party/curl/curl.h | 0 .../third-party/curl/curlbuild.h | 0 .../third-party/curl/curlrules.h | 0 .../third-party/curl/curlver.h | 0 .../third-party/curl/easy.h | 0 .../third-party/curl/mprintf.h | 0 .../third-party/curl/multi.h | 0 .../third-party/curl/stdcheaders.h | 0 .../third-party/curl/typecheck-gcc.h | 0 .../third-party/libs/libcurl.lib | Bin {Kiwano => kiwano}/2d/Action.cpp | 0 {Kiwano => kiwano}/2d/Action.h | 0 {Kiwano => kiwano}/2d/ActionGroup.cpp | 0 {Kiwano => kiwano}/2d/ActionGroup.h | 0 {Kiwano => kiwano}/2d/ActionHelper.h | 0 {Kiwano => kiwano}/2d/ActionManager.cpp | 0 {Kiwano => kiwano}/2d/ActionManager.h | 0 {Kiwano => kiwano}/2d/ActionTween.cpp | 0 {Kiwano => kiwano}/2d/ActionTween.h | 0 {Kiwano => kiwano}/2d/Animation.cpp | 0 {Kiwano => kiwano}/2d/Animation.h | 0 {Kiwano => kiwano}/2d/Canvas.cpp | 0 {Kiwano => kiwano}/2d/Canvas.h | 0 {Kiwano => kiwano}/2d/Color.cpp | 0 {Kiwano => kiwano}/2d/Color.h | 0 {Kiwano => kiwano}/2d/DebugNode.cpp | 0 {Kiwano => kiwano}/2d/DebugNode.h | 0 {Kiwano => kiwano}/2d/Font.hpp | 0 {Kiwano => kiwano}/2d/Frames.cpp | 0 {Kiwano => kiwano}/2d/Frames.h | 0 {Kiwano => kiwano}/2d/Geometry.cpp | 0 {Kiwano => kiwano}/2d/Geometry.h | 0 {Kiwano => kiwano}/2d/GeometryNode.cpp | 0 {Kiwano => kiwano}/2d/GeometryNode.h | 0 {Kiwano => kiwano}/2d/GifImage.cpp | 0 {Kiwano => kiwano}/2d/GifImage.h | 0 {Kiwano => kiwano}/2d/Image.cpp | 0 {Kiwano => kiwano}/2d/Image.h | 0 {Kiwano => kiwano}/2d/Layer.cpp | 0 {Kiwano => kiwano}/2d/Layer.h | 0 {Kiwano => kiwano}/2d/Node.cpp | 3 +- {Kiwano => kiwano}/2d/Node.h | 0 {Kiwano => kiwano}/2d/Scene.cpp | 0 {Kiwano => kiwano}/2d/Scene.h | 0 {Kiwano => kiwano}/2d/Sprite.cpp | 0 {Kiwano => kiwano}/2d/Sprite.h | 0 {Kiwano => kiwano}/2d/Text.cpp | 0 {Kiwano => kiwano}/2d/Text.h | 0 {Kiwano => kiwano}/2d/TextStyle.hpp | 0 {Kiwano => kiwano}/2d/Transform.hpp | 0 {Kiwano => kiwano}/2d/Transition.cpp | 0 {Kiwano => kiwano}/2d/Transition.h | 0 {Kiwano => kiwano}/2d/include-forwards.h | 0 {Kiwano => kiwano}/Kiwano.vcxproj | 61 +- {Kiwano => kiwano}/Kiwano.vcxproj.filters | 117 - {Kiwano => kiwano}/base/AsyncTask.cpp | 0 {Kiwano => kiwano}/base/AsyncTask.h | 0 {Kiwano => kiwano}/base/Component.h | 0 {Kiwano => kiwano}/base/Event.hpp | 0 {Kiwano => kiwano}/base/EventDispatcher.cpp | 0 {Kiwano => kiwano}/base/EventDispatcher.h | 0 {Kiwano => kiwano}/base/EventListener.cpp | 0 {Kiwano => kiwano}/base/EventListener.h | 0 {Kiwano => kiwano}/base/Input.cpp | 0 {Kiwano => kiwano}/base/Input.h | 0 {Kiwano => kiwano}/base/Object.cpp | 0 {Kiwano => kiwano}/base/Object.h | 0 {Kiwano => kiwano}/base/RefCounter.hpp | 0 {Kiwano => kiwano}/base/Resource.cpp | 0 {Kiwano => kiwano}/base/Resource.h | 0 {Kiwano => kiwano}/base/SmartPtr.hpp | 0 {Kiwano => kiwano}/base/Timer.cpp | 0 {Kiwano => kiwano}/base/Timer.h | 0 {Kiwano => kiwano}/base/TimerManager.cpp | 0 {Kiwano => kiwano}/base/TimerManager.h | 0 {Kiwano => kiwano}/base/keys.hpp | 0 {Kiwano => kiwano}/base/logs.cpp | 0 {Kiwano => kiwano}/base/logs.h | 0 {Kiwano => kiwano}/base/time.cpp | 0 {Kiwano => kiwano}/base/time.h | 0 {Kiwano => kiwano}/base/window.cpp | 0 {Kiwano => kiwano}/base/window.h | 0 {Kiwano => kiwano}/common/Array.h | 0 {Kiwano => kiwano}/common/ComPtr.hpp | 0 {Kiwano => kiwano}/common/IntrusiveList.hpp | 0 {Kiwano => kiwano}/common/IntrusivePtr.hpp | 0 {Kiwano => kiwano}/common/Json.h | 0 {Kiwano => kiwano}/common/Singleton.hpp | 0 {Kiwano => kiwano}/common/String.h | 0 {Kiwano => kiwano}/common/closure.hpp | 0 {Kiwano => kiwano}/common/helper.h | 0 {Kiwano => kiwano}/common/noncopyable.hpp | 0 {Kiwano => kiwano}/config.h | 0 {Kiwano => kiwano}/kiwano.h | 0 {Kiwano => kiwano}/macros.h | 0 {Kiwano => kiwano}/math/Matrix.hpp | 0 {Kiwano => kiwano}/math/Rect.hpp | 0 {Kiwano => kiwano}/math/Vec2.hpp | 0 {Kiwano => kiwano}/math/constants.hpp | 0 {Kiwano => kiwano}/math/ease.hpp | 0 {Kiwano => kiwano}/math/helper.h | 0 {Kiwano => kiwano}/math/rand.h | 0 {Kiwano => kiwano}/math/scalar.hpp | 0 {Kiwano => kiwano}/platform/Application.cpp | 0 {Kiwano => kiwano}/platform/Application.h | 0 {Kiwano => kiwano}/platform/modules.cpp | 0 {Kiwano => kiwano}/platform/modules.h | 0 .../renderer/D2DDeviceResources.cpp | 0 .../renderer/D2DDeviceResources.h | 0 .../renderer/D3D10DeviceResources.cpp | 0 .../renderer/D3D10DeviceResources.h | 0 .../renderer/D3D11DeviceResources.cpp | 0 .../renderer/D3D11DeviceResources.h | 0 .../renderer/D3DDeviceResourcesBase.h | 0 {Kiwano => kiwano}/renderer/TextRenderer.cpp | 0 {Kiwano => kiwano}/renderer/TextRenderer.h | 0 {Kiwano => kiwano}/renderer/helper.hpp | 0 {Kiwano => kiwano}/renderer/render.cpp | 0 {Kiwano => kiwano}/renderer/render.h | 0 .../third-party/StackWalker/StackWalker.cpp | 0 .../third-party/StackWalker/StackWalker.h | 0 {Kiwano => kiwano}/ui/Button.cpp | 0 {Kiwano => kiwano}/ui/Button.h | 0 {Kiwano => kiwano}/ui/Menu.cpp | 0 {Kiwano => kiwano}/ui/Menu.h | 0 {Kiwano => kiwano}/utils/DataUtil.cpp | 0 {Kiwano => kiwano}/utils/DataUtil.h | 0 {Kiwano => kiwano}/utils/FileUtil.cpp | 0 {Kiwano => kiwano}/utils/FileUtil.h | 0 {Kiwano => kiwano}/utils/Path.cpp | 0 {Kiwano => kiwano}/utils/Path.h | 0 {Kiwano => kiwano}/utils/ResLoader.cpp | 0 {Kiwano => kiwano}/utils/ResLoader.h | 0 samples/Box2DSample/Box2DSample.vcxproj | 18 +- samples/Box2DSample/common.h | 2 +- samples/HelloWorld/HelloWorld.vcxproj | 18 +- samples/HelloWorld/main.cpp | 55 +- samples/ImGuiSample/ImGuiSample.vcxproj | 21 +- samples/ImGuiSample/MainScene.h | 5 +- samples/ImGuiSample/main.cpp | 2 - samples/Samples/Samples.vcxproj | 59 +- samples/Samples/Samples.vcxproj.filters | 3 - samples/Samples/common.h | 7 +- samples/Samples/readme.txt | 12 - 204 files changed, 2296 insertions(+), 7563 deletions(-) delete mode 100644 Kiwano/audio/Player.cpp delete mode 100644 Kiwano/audio/Sound.cpp delete mode 100644 Kiwano/audio/Transcoder.cpp delete mode 100644 Kiwano/audio/audio-modules.cpp delete mode 100644 Kiwano/audio/audio-modules.h delete mode 100644 Kiwano/imgui/ImGuiLayer.cpp delete mode 100644 Kiwano/imgui/ImGuiView.cpp delete mode 100644 Kiwano/third-party/dlls/websockets.dll delete mode 100644 Kiwano/third-party/libs/websockets.lib delete mode 100644 Kiwano/third-party/libwebsockets/libwebsockets.h delete mode 100644 Kiwano/third-party/libwebsockets/lws_config.h rename {Kiwano => kiwano-audio}/kiwano-audio.h (92%) create mode 100644 kiwano-audio/kiwano-audio.vcxproj create mode 100644 kiwano-audio/kiwano-audio.vcxproj.filters create mode 100644 kiwano-audio/src/Player.cpp rename {Kiwano/audio => kiwano-audio/src}/Player.h (52%) create mode 100644 kiwano-audio/src/Sound.cpp rename {Kiwano/audio => kiwano-audio/src}/Sound.h (59%) create mode 100644 kiwano-audio/src/Transcoder.cpp rename {Kiwano/audio => kiwano-audio/src}/Transcoder.h (70%) create mode 100644 kiwano-audio/src/audio-modules.cpp create mode 100644 kiwano-audio/src/audio-modules.h rename {Kiwano/audio => kiwano-audio/src}/audio.cpp (51%) rename {Kiwano/audio => kiwano-audio/src}/audio.h (69%) rename {Kiwano => kiwano-imgui}/kiwano-imgui.h (93%) create mode 100644 kiwano-imgui/kiwano-imgui.vcxproj create mode 100644 kiwano-imgui/kiwano-imgui.vcxproj.filters create mode 100644 kiwano-imgui/src/ImGuiLayer.cpp rename {Kiwano/imgui => kiwano-imgui/src}/ImGuiLayer.h (54%) create mode 100644 kiwano-imgui/src/ImGuiView.cpp rename {Kiwano/imgui => kiwano-imgui/src}/ImGuiView.h (68%) rename {Kiwano/imgui => kiwano-imgui/src}/imgui_impl.hpp (100%) rename {Kiwano/imgui => kiwano-imgui/src}/imgui_impl_dx10.cpp (100%) rename {Kiwano/imgui => kiwano-imgui/src}/imgui_impl_dx10.h (100%) rename {Kiwano/imgui => kiwano-imgui/src}/imgui_impl_dx11.cpp (100%) rename {Kiwano/imgui => kiwano-imgui/src}/imgui_impl_dx11.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/LICENSE.txt (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imconfig.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui.cpp (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui_demo.cpp (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui_draw.cpp (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui_internal.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imgui_widgets.cpp (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imstb_rectpack.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imstb_textedit.h (100%) rename {Kiwano => kiwano-imgui}/third-party/ImGui/imstb_truetype.h (100%) rename {Kiwano/third-party => kiwano-network}/dlls/libcrypto-1_1.dll (100%) rename {Kiwano/third-party => kiwano-network}/dlls/libcurl.dll (100%) rename {Kiwano/third-party => kiwano-network}/dlls/libssl-1_1.dll (100%) rename {Kiwano => kiwano-network}/kiwano-network.h (88%) create mode 100644 kiwano-network/kiwano-network.vcxproj create mode 100644 kiwano-network/kiwano-network.vcxproj.filters rename {Kiwano/network => kiwano-network/src}/HttpClient.cpp (99%) rename {Kiwano/network => kiwano-network/src}/HttpClient.h (100%) rename {Kiwano/network => kiwano-network/src}/HttpRequest.h (100%) rename {Kiwano/network => kiwano-network/src}/HttpResponse.h (100%) rename {Kiwano/network => kiwano-network/src}/helper.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/curl.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/curlbuild.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/curlrules.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/curlver.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/easy.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/mprintf.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/multi.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/stdcheaders.h (100%) rename {Kiwano => kiwano-network}/third-party/curl/typecheck-gcc.h (100%) rename {Kiwano => kiwano-network}/third-party/libs/libcurl.lib (100%) rename {Kiwano => kiwano}/2d/Action.cpp (100%) rename {Kiwano => kiwano}/2d/Action.h (100%) rename {Kiwano => kiwano}/2d/ActionGroup.cpp (100%) rename {Kiwano => kiwano}/2d/ActionGroup.h (100%) rename {Kiwano => kiwano}/2d/ActionHelper.h (100%) rename {Kiwano => kiwano}/2d/ActionManager.cpp (100%) rename {Kiwano => kiwano}/2d/ActionManager.h (100%) rename {Kiwano => kiwano}/2d/ActionTween.cpp (100%) rename {Kiwano => kiwano}/2d/ActionTween.h (100%) rename {Kiwano => kiwano}/2d/Animation.cpp (100%) rename {Kiwano => kiwano}/2d/Animation.h (100%) rename {Kiwano => kiwano}/2d/Canvas.cpp (100%) rename {Kiwano => kiwano}/2d/Canvas.h (100%) rename {Kiwano => kiwano}/2d/Color.cpp (100%) rename {Kiwano => kiwano}/2d/Color.h (100%) rename {Kiwano => kiwano}/2d/DebugNode.cpp (100%) rename {Kiwano => kiwano}/2d/DebugNode.h (100%) rename {Kiwano => kiwano}/2d/Font.hpp (100%) rename {Kiwano => kiwano}/2d/Frames.cpp (100%) rename {Kiwano => kiwano}/2d/Frames.h (100%) rename {Kiwano => kiwano}/2d/Geometry.cpp (100%) rename {Kiwano => kiwano}/2d/Geometry.h (100%) rename {Kiwano => kiwano}/2d/GeometryNode.cpp (100%) rename {Kiwano => kiwano}/2d/GeometryNode.h (100%) rename {Kiwano => kiwano}/2d/GifImage.cpp (100%) rename {Kiwano => kiwano}/2d/GifImage.h (100%) rename {Kiwano => kiwano}/2d/Image.cpp (100%) rename {Kiwano => kiwano}/2d/Image.h (100%) rename {Kiwano => kiwano}/2d/Layer.cpp (100%) rename {Kiwano => kiwano}/2d/Layer.h (100%) rename {Kiwano => kiwano}/2d/Node.cpp (99%) rename {Kiwano => kiwano}/2d/Node.h (100%) rename {Kiwano => kiwano}/2d/Scene.cpp (100%) rename {Kiwano => kiwano}/2d/Scene.h (100%) rename {Kiwano => kiwano}/2d/Sprite.cpp (100%) rename {Kiwano => kiwano}/2d/Sprite.h (100%) rename {Kiwano => kiwano}/2d/Text.cpp (100%) rename {Kiwano => kiwano}/2d/Text.h (100%) rename {Kiwano => kiwano}/2d/TextStyle.hpp (100%) rename {Kiwano => kiwano}/2d/Transform.hpp (100%) rename {Kiwano => kiwano}/2d/Transition.cpp (100%) rename {Kiwano => kiwano}/2d/Transition.h (100%) rename {Kiwano => kiwano}/2d/include-forwards.h (100%) rename {Kiwano => kiwano}/Kiwano.vcxproj (84%) rename {Kiwano => kiwano}/Kiwano.vcxproj.filters (75%) rename {Kiwano => kiwano}/base/AsyncTask.cpp (100%) rename {Kiwano => kiwano}/base/AsyncTask.h (100%) rename {Kiwano => kiwano}/base/Component.h (100%) rename {Kiwano => kiwano}/base/Event.hpp (100%) rename {Kiwano => kiwano}/base/EventDispatcher.cpp (100%) rename {Kiwano => kiwano}/base/EventDispatcher.h (100%) rename {Kiwano => kiwano}/base/EventListener.cpp (100%) rename {Kiwano => kiwano}/base/EventListener.h (100%) rename {Kiwano => kiwano}/base/Input.cpp (100%) rename {Kiwano => kiwano}/base/Input.h (100%) rename {Kiwano => kiwano}/base/Object.cpp (100%) rename {Kiwano => kiwano}/base/Object.h (100%) rename {Kiwano => kiwano}/base/RefCounter.hpp (100%) rename {Kiwano => kiwano}/base/Resource.cpp (100%) rename {Kiwano => kiwano}/base/Resource.h (100%) rename {Kiwano => kiwano}/base/SmartPtr.hpp (100%) rename {Kiwano => kiwano}/base/Timer.cpp (100%) rename {Kiwano => kiwano}/base/Timer.h (100%) rename {Kiwano => kiwano}/base/TimerManager.cpp (100%) rename {Kiwano => kiwano}/base/TimerManager.h (100%) rename {Kiwano => kiwano}/base/keys.hpp (100%) rename {Kiwano => kiwano}/base/logs.cpp (100%) rename {Kiwano => kiwano}/base/logs.h (100%) rename {Kiwano => kiwano}/base/time.cpp (100%) rename {Kiwano => kiwano}/base/time.h (100%) rename {Kiwano => kiwano}/base/window.cpp (100%) rename {Kiwano => kiwano}/base/window.h (100%) rename {Kiwano => kiwano}/common/Array.h (100%) rename {Kiwano => kiwano}/common/ComPtr.hpp (100%) rename {Kiwano => kiwano}/common/IntrusiveList.hpp (100%) rename {Kiwano => kiwano}/common/IntrusivePtr.hpp (100%) rename {Kiwano => kiwano}/common/Json.h (100%) rename {Kiwano => kiwano}/common/Singleton.hpp (100%) rename {Kiwano => kiwano}/common/String.h (100%) rename {Kiwano => kiwano}/common/closure.hpp (100%) rename {Kiwano => kiwano}/common/helper.h (100%) rename {Kiwano => kiwano}/common/noncopyable.hpp (100%) rename {Kiwano => kiwano}/config.h (100%) rename {Kiwano => kiwano}/kiwano.h (100%) rename {Kiwano => kiwano}/macros.h (100%) rename {Kiwano => kiwano}/math/Matrix.hpp (100%) rename {Kiwano => kiwano}/math/Rect.hpp (100%) rename {Kiwano => kiwano}/math/Vec2.hpp (100%) rename {Kiwano => kiwano}/math/constants.hpp (100%) rename {Kiwano => kiwano}/math/ease.hpp (100%) rename {Kiwano => kiwano}/math/helper.h (100%) rename {Kiwano => kiwano}/math/rand.h (100%) rename {Kiwano => kiwano}/math/scalar.hpp (100%) rename {Kiwano => kiwano}/platform/Application.cpp (100%) rename {Kiwano => kiwano}/platform/Application.h (100%) rename {Kiwano => kiwano}/platform/modules.cpp (100%) rename {Kiwano => kiwano}/platform/modules.h (100%) rename {Kiwano => kiwano}/renderer/D2DDeviceResources.cpp (100%) rename {Kiwano => kiwano}/renderer/D2DDeviceResources.h (100%) rename {Kiwano => kiwano}/renderer/D3D10DeviceResources.cpp (100%) rename {Kiwano => kiwano}/renderer/D3D10DeviceResources.h (100%) rename {Kiwano => kiwano}/renderer/D3D11DeviceResources.cpp (100%) rename {Kiwano => kiwano}/renderer/D3D11DeviceResources.h (100%) rename {Kiwano => kiwano}/renderer/D3DDeviceResourcesBase.h (100%) rename {Kiwano => kiwano}/renderer/TextRenderer.cpp (100%) rename {Kiwano => kiwano}/renderer/TextRenderer.h (100%) rename {Kiwano => kiwano}/renderer/helper.hpp (100%) rename {Kiwano => kiwano}/renderer/render.cpp (100%) rename {Kiwano => kiwano}/renderer/render.h (100%) rename {Kiwano => kiwano}/third-party/StackWalker/StackWalker.cpp (100%) rename {Kiwano => kiwano}/third-party/StackWalker/StackWalker.h (100%) rename {Kiwano => kiwano}/ui/Button.cpp (100%) rename {Kiwano => kiwano}/ui/Button.h (100%) rename {Kiwano => kiwano}/ui/Menu.cpp (100%) rename {Kiwano => kiwano}/ui/Menu.h (100%) rename {Kiwano => kiwano}/utils/DataUtil.cpp (100%) rename {Kiwano => kiwano}/utils/DataUtil.h (100%) rename {Kiwano => kiwano}/utils/FileUtil.cpp (100%) rename {Kiwano => kiwano}/utils/FileUtil.h (100%) rename {Kiwano => kiwano}/utils/Path.cpp (100%) rename {Kiwano => kiwano}/utils/Path.h (100%) rename {Kiwano => kiwano}/utils/ResLoader.cpp (100%) rename {Kiwano => kiwano}/utils/ResLoader.h (100%) delete mode 100644 samples/Samples/readme.txt diff --git a/.gitignore b/.gitignore index fba46d8b..b674a7fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +build/ +output/ + x64/ Debug/ Release/ diff --git a/Kiwano.sln b/Kiwano.sln index 2059c672..385e15c0 100644 --- a/Kiwano.sln +++ b/Kiwano.sln @@ -4,7 +4,7 @@ VisualStudioVersion = 16.0.28729.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HelloWorld", "samples\HelloWorld\HelloWorld.vcxproj", "{3561A359-F9FD-48AB-A977-34E7E568BC8E}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Kiwano", "Kiwano\Kiwano.vcxproj", "{FF7F943D-A89C-4E6C-97CF-84F7D8FF8EDF}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiwano", "kiwano\kiwano.vcxproj", "{FF7F943D-A89C-4E6C-97CF-84F7D8FF8EDF}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Samples", "samples\Samples\Samples.vcxproj", "{45F5738D-CDF2-4024-974D-25B64F9043DE}" EndProject @@ -12,6 +12,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ImGuiSample", "samples\ImGu EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Box2DSample", "samples\Box2DSample\Box2DSample.vcxproj", "{324CFF47-4EB2-499A-BE5F-53A82E3BA14B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiwano-audio", "kiwano-audio\kiwano-audio.vcxproj", "{1B97937D-8184-426C-BE71-29A163DC76C9}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiwano-network", "kiwano-network\kiwano-network.vcxproj", "{69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kiwano-imgui", "kiwano-imgui\kiwano-imgui.vcxproj", "{A7062ED8-8910-48A5-A3BC-C1612672571F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -62,6 +68,30 @@ Global {324CFF47-4EB2-499A-BE5F-53A82E3BA14B}.Release|Win32.Build.0 = Release|Win32 {324CFF47-4EB2-499A-BE5F-53A82E3BA14B}.Release|x64.ActiveCfg = Release|x64 {324CFF47-4EB2-499A-BE5F-53A82E3BA14B}.Release|x64.Build.0 = Release|x64 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Debug|Win32.ActiveCfg = Debug|Win32 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Debug|Win32.Build.0 = Debug|Win32 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Debug|x64.ActiveCfg = Debug|x64 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Debug|x64.Build.0 = Debug|x64 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Release|Win32.ActiveCfg = Release|Win32 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Release|Win32.Build.0 = Release|Win32 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Release|x64.ActiveCfg = Release|x64 + {1B97937D-8184-426C-BE71-29A163DC76C9}.Release|x64.Build.0 = Release|x64 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Debug|Win32.ActiveCfg = Debug|Win32 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Debug|Win32.Build.0 = Debug|Win32 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Debug|x64.ActiveCfg = Debug|x64 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Debug|x64.Build.0 = Debug|x64 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Release|Win32.ActiveCfg = Release|Win32 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Release|Win32.Build.0 = Release|Win32 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Release|x64.ActiveCfg = Release|x64 + {69DFBE92-C06F-4CF8-9495-CA9BF2E3BAF2}.Release|x64.Build.0 = Release|x64 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Debug|Win32.ActiveCfg = Debug|Win32 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Debug|Win32.Build.0 = Debug|Win32 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Debug|x64.ActiveCfg = Debug|x64 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Debug|x64.Build.0 = Debug|x64 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Release|Win32.ActiveCfg = Release|Win32 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Release|Win32.Build.0 = Release|Win32 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Release|x64.ActiveCfg = Release|x64 + {A7062ED8-8910-48A5-A3BC-C1612672571F}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Kiwano/audio/Player.cpp b/Kiwano/audio/Player.cpp deleted file mode 100644 index 096f603c..00000000 --- a/Kiwano/audio/Player.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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 "../kiwano-audio.h" -#include "Player.h" - -namespace kiwano -{ - Player::Player() - : volume_(1.f) - { - } - - Player::~Player() - { - ClearCache(); - } - - bool Player::Load(Resource const& res) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - return true; - - SoundPtr sound = new (std::nothrow) Sound(); - - if (sound) - { - if (sound->Load(res)) - { - sound->SetVolume(volume_); - sound_cache_.insert(std::make_pair(hash_code, sound)); - return true; - } - } - return false; - } - - void Player::Play(Resource const& res, int loop_count) - { - if (Load(res)) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Play(loop_count); - } - } - - void Player::Pause(Resource const& res) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Pause(); - } - - void Player::Resume(Resource const& res) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Resume(); - } - - void Player::Stop(Resource const& res) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - sound_cache_[hash_code]->Stop(); - } - - bool Player::IsPlaying(Resource const& res) - { - size_t hash_code = res.GetHashCode(); - if (sound_cache_.end() != sound_cache_.find(hash_code)) - return sound_cache_[hash_code]->IsPlaying(); - return false; - } - - float Player::GetVolume() const - { - return volume_; - } - - void Player::SetVolume(float volume) - { - volume_ = std::min(std::max(volume, -224.f), 224.f); - for (const auto& pair : sound_cache_) - { - pair.second->SetVolume(volume_); - } - } - - void Player::PauseAll() - { - for (const auto& pair : sound_cache_) - { - pair.second->Pause(); - } - } - - void Player::ResumeAll() - { - for (const auto& pair : sound_cache_) - { - pair.second->Resume(); - } - } - - void Player::StopAll() - { - for (const auto& pair : sound_cache_) - { - pair.second->Stop(); - } - } - - void Player::ClearCache() - { - sound_cache_.clear(); - } -} \ No newline at end of file diff --git a/Kiwano/audio/Sound.cpp b/Kiwano/audio/Sound.cpp deleted file mode 100644 index 70c0f676..00000000 --- a/Kiwano/audio/Sound.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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 "../kiwano-audio.h" -#include "Sound.h" -#include "Transcoder.h" - -namespace kiwano -{ - Sound::Sound() - : opened_(false) - , playing_(false) - , size_(0) - , wave_data_(nullptr) - , voice_(nullptr) - { - } - - Sound::Sound(Resource const& res) - : Sound() - { - Load(res); - } - - Sound::~Sound() - { - Close(); - } - - bool Sound::Load(Resource const& res) - { - if (opened_) - { - Close(); - } - - HRESULT hr = S_OK; - Transcoder transcoder; - - if (res.IsFileType()) - { -#if defined(KGE_DEBUG) - if (!FileUtil::ExistsFile(res.GetFileName())) - { - KGE_WARNING_LOG(L"Media file '%s' not found", res.GetFileName().c_str()); - return false; - } -#endif - hr = transcoder.LoadMediaFile(res.GetFileName(), &wave_data_, &size_); - } - else - { - hr = transcoder.LoadMediaResource(res, &wave_data_, &size_); - } - - if (FAILED(hr)) - { - KGE_ERROR_LOG(L"Load media file failed with HRESULT of %08X", hr); - return false; - } - - hr = Audio::Instance().CreateVoice(&voice_, transcoder.GetWaveFormatEx()); - if (FAILED(hr)) - { - if (wave_data_) - { - delete[] wave_data_; - wave_data_ = nullptr; - } - KGE_ERROR_LOG(L"Create source voice failed with HRESULT of %08X", hr); - return false; - } - - opened_ = true; - return true; - } - - void Sound::Play(int loop_count) - { - if (!opened_) - { - KGE_ERROR_LOG(L"Sound must be opened first!"); - return; - } - - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - // if sound stream is not empty, stop() will clear it - XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); - if (state.BuffersQueued) - Stop(); - - // clamp loop count - loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); - - XAUDIO2_BUFFER buffer = { 0 }; - buffer.pAudioData = wave_data_; - buffer.Flags = XAUDIO2_END_OF_STREAM; - buffer.AudioBytes = size_; - buffer.LoopCount = static_cast(loop_count); - - HRESULT hr = voice_->SubmitSourceBuffer(&buffer); - if (SUCCEEDED(hr)) - { - hr = voice_->Start(); - } - - if (FAILED(hr)) - { - KGE_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr); - } - - playing_ = SUCCEEDED(hr); - } - - void Sound::Pause() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - if (SUCCEEDED(voice_->Stop())) - playing_ = false; - } - - void Sound::Resume() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - if (SUCCEEDED(voice_->Start())) - playing_ = true; - } - - void Sound::Stop() - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - HRESULT hr = voice_->Stop(); - - if (SUCCEEDED(hr)) - hr = voice_->ExitLoop(); - - if (SUCCEEDED(hr)) - hr = voice_->FlushSourceBuffers(); - - if (SUCCEEDED(hr)) - playing_ = false; - } - - void Sound::Close() - { - if (voice_) - { - voice_->Stop(); - voice_->FlushSourceBuffers(); - voice_->DestroyVoice(); - voice_ = nullptr; - } - - if (wave_data_) - { - delete[] wave_data_; - wave_data_ = nullptr; - } - - opened_ = false; - playing_ = false; - } - - bool Sound::IsPlaying() const - { - if (opened_) - { - if (!voice_) - return false; - - XAUDIO2_VOICE_STATE state; - voice_->GetState(&state); - UINT32 buffers_queued = state.BuffersQueued; - - if (buffers_queued && playing_) - return true; - } - return false; - } - - float Sound::GetVolume() const - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - float volume = 0.0f; - voice_->GetVolume(&volume); - return volume; - } - - void Sound::SetVolume(float volume) - { - KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); - - volume = std::min(std::max(volume, -224.f), 224.f); - voice_->SetVolume(volume); - } -} \ No newline at end of file diff --git a/Kiwano/audio/Transcoder.cpp b/Kiwano/audio/Transcoder.cpp deleted file mode 100644 index d32658ba..00000000 --- a/Kiwano/audio/Transcoder.cpp +++ /dev/null @@ -1,267 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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 INITGUID -# define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio -#endif - -#include "../kiwano-audio.h" -#include "Transcoder.h" -#include "audio-modules.h" - -namespace kiwano -{ - Transcoder::Transcoder() - : wave_format_(nullptr) - { - } - - Transcoder::~Transcoder() - { - if (wave_format_) - { - ::CoTaskMemFree(wave_format_); - wave_format_ = nullptr; - } - } - - const WAVEFORMATEX* Transcoder::GetWaveFormatEx() const - { - return wave_format_; - } - - HRESULT Transcoder::LoadMediaFile(String const& file_path, BYTE** wave_data, UINT32* wave_data_size) - { - HRESULT hr = S_OK; - - ComPtr reader; - - hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromURL( - file_path.c_str(), - nullptr, - &reader - ); - - if (SUCCEEDED(hr)) - { - hr = ReadSource(reader.Get(), wave_data, wave_data_size); - } - - return hr; - } - - HRESULT Transcoder::LoadMediaResource(Resource const& res, BYTE** wave_data, UINT32* wave_data_size) - { - HRESULT hr = S_OK; - - ComPtr stream; - ComPtr byte_stream; - ComPtr reader; - - LPVOID buffer; - DWORD buffer_size; - if (!res.Load(buffer, buffer_size)) { return false; } - - stream = modules::Shlwapi::Get().SHCreateMemStream( - static_cast(buffer), - static_cast(buffer_size) - ); - - if (stream == nullptr) - { - KGE_ERROR_LOG(L"SHCreateMemStream failed"); - return E_OUTOFMEMORY; - } - - if (SUCCEEDED(hr)) - { - hr = modules::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.Get(), &byte_stream); - } - - if (SUCCEEDED(hr)) - { - hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromByteStream( - byte_stream.Get(), - nullptr, - &reader - ); - } - - if (SUCCEEDED(hr)) - { - hr = ReadSource(reader.Get(), wave_data, wave_data_size); - } - - return hr; - } - - HRESULT Transcoder::ReadSource(IMFSourceReader* reader, BYTE** wave_data, UINT32* wave_data_size) - { - HRESULT hr = S_OK; - DWORD max_stream_size = 0; - - ComPtr partial_type; - ComPtr uncompressed_type; - - hr = modules::MediaFoundation::Get().MFCreateMediaType(&partial_type); - - if (SUCCEEDED(hr)) - { - hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); - } - - if (SUCCEEDED(hr)) - { - hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); - } - - // ÉèÖà source reader µÄýÌåÀàÐÍ£¬Ëü½«Ê¹ÓúÏÊʵĽâÂëÆ÷È¥½âÂëÕâ¸öÒôƵ - if (SUCCEEDED(hr)) - { - hr = reader->SetCurrentMediaType( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - 0, - partial_type.Get() - ); - } - - // ´Ó IMFMediaType ÖлñÈ¡ WAVEFORMAT ½á¹¹ - if (SUCCEEDED(hr)) - { - hr = reader->GetCurrentMediaType( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - &uncompressed_type - ); - } - - // Ö¸¶¨ÒôƵÁ÷ - if (SUCCEEDED(hr)) - { - hr = reader->SetStreamSelection( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - true - ); - } - - // »ñÈ¡ WAVEFORMAT Êý¾Ý - if (SUCCEEDED(hr)) - { - UINT32 size = 0; - hr = modules::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType( - uncompressed_type.Get(), - &wave_format_, - &size, - (DWORD)MFWaveFormatExConvertFlag_Normal - ); - } - - // ¹ÀËãÒôƵÁ÷´óС - if (SUCCEEDED(hr)) - { - PROPVARIANT prop; - PropVariantInit(&prop); - - hr = reader->GetPresentationAttribute( - (DWORD)MF_SOURCE_READER_MEDIASOURCE, - MF_PD_DURATION, - &prop - ); - - LONGLONG duration = prop.uhVal.QuadPart; - max_stream_size = static_cast( - (duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1 - ); - PropVariantClear(&prop); - } - - // ¶ÁÈ¡ÒôƵÊý¾Ý - if (SUCCEEDED(hr)) - { - DWORD flags = 0; - DWORD position = 0; - BYTE* data = new (std::nothrow) BYTE[max_stream_size]; - - ComPtr sample; - ComPtr buffer; - - if (data == nullptr) - { - KGE_ERROR_LOG(L"Low memory"); - hr = E_OUTOFMEMORY; - } - else - { - while (true) - { - hr = reader->ReadSample( - (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, - 0, - nullptr, - &flags, - nullptr, - &sample - ); - - if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } - - if (sample == nullptr) { continue; } - - if (SUCCEEDED(hr)) - { - hr = sample->ConvertToContiguousBuffer(&buffer); - - if (SUCCEEDED(hr)) - { - BYTE *audio_data = nullptr; - DWORD sample_buffer_length = 0; - - hr = buffer->Lock( - &audio_data, - nullptr, - &sample_buffer_length - ); - - if (SUCCEEDED(hr)) - { - for (DWORD i = 0; i < sample_buffer_length; i++) - { - data[position++] = audio_data[i]; - } - hr = buffer->Unlock(); - } - } - buffer = nullptr; - } - sample = nullptr; - - if (FAILED(hr)) { break; } - } - - if (SUCCEEDED(hr)) - { - *wave_data = data; - *wave_data_size = position; - } - } - } - - return hr; - } -} \ No newline at end of file diff --git a/Kiwano/audio/audio-modules.cpp b/Kiwano/audio/audio-modules.cpp deleted file mode 100644 index bce321f1..00000000 --- a/Kiwano/audio/audio-modules.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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 "../kiwano-audio.h" -#include "audio-modules.h" - -namespace kiwano -{ - namespace modules - { - XAudio2::XAudio2() - { - const auto xaudio2_dll_names = - { - L"xaudio2_9.dll", // for Windows 10 - L"xaudio2_8.dll", // for Windows 8 - L"xaudio2_7.dll" // for DirectX SDK - }; - - for (const auto& name : xaudio2_dll_names) - { - xaudio2 = LoadLibraryW(name); - if (xaudio2) - { - XAudio2Create = (PFN_XAudio2Create) - GetProcAddress(xaudio2, "XAudio2Create"); - break; - } - } - - if (!xaudio2) - { - KGE_ERROR_LOG(L"Load xaudio2.dll failed"); - throw std::runtime_error("Load xaudio2.dll failed"); - } - } - - MediaFoundation::MediaFoundation() - { - mfplat = LoadLibraryW(L"Mfplat.dll"); - if (mfplat) - { - MFStartup = (PFN_MFStartup) - GetProcAddress(mfplat, "MFStartup"); - - MFShutdown = (PFN_MFShutdown) - GetProcAddress(mfplat, "MFShutdown"); - - MFCreateMediaType = (PFN_MFCreateMediaType) - GetProcAddress(mfplat, "MFCreateMediaType"); - - MFCreateWaveFormatExFromMFMediaType = (PFN_MFCreateWaveFormatExFromMFMediaType) - GetProcAddress(mfplat, "MFCreateWaveFormatExFromMFMediaType"); - - MFCreateMFByteStreamOnStream = (PFN_MFCreateMFByteStreamOnStream) - GetProcAddress(mfplat, "MFCreateMFByteStreamOnStream"); - } - else - { - KGE_LOG(L"Load Mfplat.dll failed"); - throw std::runtime_error("Load Mfplat.dll failed"); - } - - mfreadwrite = LoadLibraryW(L"Mfreadwrite.dll"); - if (mfreadwrite) - { - MFCreateSourceReaderFromURL = (PFN_MFCreateSourceReaderFromURL) - GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromURL"); - - MFCreateSourceReaderFromByteStream = (PFN_MFCreateSourceReaderFromByteStream) - GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromByteStream"); - } - else - { - KGE_LOG(L"Load Mfreadwrite.dll failed"); - throw std::runtime_error("Load Mfreadwrite.dll failed"); - } - } - } -} \ No newline at end of file diff --git a/Kiwano/audio/audio-modules.h b/Kiwano/audio/audio-modules.h deleted file mode 100644 index aad77d57..00000000 --- a/Kiwano/audio/audio-modules.h +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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. - -#pragma once -#include -#include -#include -#include - -namespace kiwano -{ - namespace modules - { - class KGE_API XAudio2 - { - XAudio2(); - - HMODULE xaudio2; - - // XAudio2 functions - typedef HRESULT(WINAPI *PFN_XAudio2Create)(IXAudio2**, UINT32, XAUDIO2_PROCESSOR); - - public: - static inline XAudio2& Get() - { - static XAudio2 instance; - return instance; - } - - PFN_XAudio2Create XAudio2Create; - }; - - - class KGE_API MediaFoundation - { - MediaFoundation(); - - HMODULE mfplat; - HMODULE mfreadwrite; - - // MediaFoundation functions - typedef HRESULT(WINAPI *PFN_MFStartup)(ULONG, DWORD); - typedef HRESULT(WINAPI *PFN_MFShutdown)(); - typedef HRESULT(WINAPI *PFN_MFCreateMediaType)(IMFMediaType**); - typedef HRESULT(WINAPI *PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32); - typedef HRESULT(WINAPI *PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**); - typedef HRESULT(WINAPI *PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**); - typedef HRESULT(WINAPI *PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**); - - public: - static inline MediaFoundation& Get() - { - static MediaFoundation instance; - return instance; - } - - PFN_MFStartup MFStartup; - PFN_MFShutdown MFShutdown; - PFN_MFCreateMediaType MFCreateMediaType; - PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType; - PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL; - PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream; - PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream; - }; - } -} diff --git a/Kiwano/imgui/ImGuiLayer.cpp b/Kiwano/imgui/ImGuiLayer.cpp deleted file mode 100644 index b0d4c7c9..00000000 --- a/Kiwano/imgui/ImGuiLayer.cpp +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) 2016-2018 Kiwano - Nomango -// -// 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 "../kiwano-imgui.h" - -namespace kiwano -{ - namespace - { - Map mouse_buttons = - { - { MouseButton::Left, 0 }, - { MouseButton::Right, 1 }, - { MouseButton::Middle, 2 } - }; - } - - ImGuiLayer::ImGuiLayer() - { - target_window_ = Renderer::Instance().GetTargetWindow(); - SetSwallowEvents(true); - } - - ImGuiLayer::~ImGuiLayer() - { - ImGuiView::Instance().RemoveLayer(this); - } - - void ImGuiLayer::OnMouseButtonDown(int btn, Point const & p) - { - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == nullptr) - ::SetCapture(target_window_); - - KGE_ASSERT(mouse_buttons.find(btn) != mouse_buttons.end()); - ImGui::GetIO().MouseDown[mouse_buttons[btn]] = true; - } - - void ImGuiLayer::OnMouseButtonUp(int btn, Point const & p) - { - KGE_ASSERT(mouse_buttons.find(btn) != mouse_buttons.end()); - ImGui::GetIO().MouseDown[mouse_buttons[btn]] = false; - - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == target_window_) - ::ReleaseCapture(); - } - - void ImGuiLayer::OnMouseWheel(float wheel) - { - ImGui::GetIO().MouseWheel += wheel; - } - - void ImGuiLayer::OnKeyDown(int key) - { - KGE_ASSERT(key < 256); - ImGui::GetIO().KeysDown[key] = 1; - } - - void ImGuiLayer::OnKeyUp(int key) - { - KGE_ASSERT(key < 256); - ImGui::GetIO().KeysDown[key] = 0; - } - - void ImGuiLayer::OnChar(char c) - { - ImGui::GetIO().AddInputCharacter(c); - } - - void ImGuiLayer::OnUpdate(Duration dt) - { - ImGuiIO& io = ImGui::GetIO(); - - // Setup time step - io.DeltaTime = dt.Seconds(); - - // Read keyboard modifiers inputs - io.KeyCtrl = Input::Instance().IsDown(KeyCode::Ctrl); - io.KeyShift = Input::Instance().IsDown(KeyCode::Shift); - io.KeyAlt = Input::Instance().IsDown(KeyCode::Alt); - io.KeySuper = false; - // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. - - // Update OS mouse position - UpdateMousePos(); - - // Update OS mouse cursor with the cursor requested by imgui - UpdateMouseCursor(); - } - - void ImGuiLayer::OnRender() - { - ImGuiView::Instance().NewFrame(); - - for (const auto& pipeline : pipelines_) - { - pipeline.second(); - } - - ImGuiView::Instance().Render(); - } - - void ImGuiLayer::UpdateMousePos() - { - ImGuiIO& io = ImGui::GetIO(); - - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - HWND hwnd = target_window_; - ::ClientToScreen(hwnd, &pos); - ::SetCursorPos(pos.x, pos.y); - } - - Point pos = Input::Instance().GetMousePos(); - io.MousePos = ImVec2(pos.x, pos.y); - } - - void ImGuiLayer::UpdateMouseCursor() - { - static ImGuiMouseCursor last_mouse_cursor = ImGuiMouseCursor_COUNT; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - - if (last_mouse_cursor != imgui_cursor) - { - last_mouse_cursor = imgui_cursor; - - if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) - return; - - MouseCursor cursor = MouseCursor::Arrow; - switch (imgui_cursor) - { - case ImGuiMouseCursor_Arrow: cursor = MouseCursor::Arrow; break; - case ImGuiMouseCursor_TextInput: cursor = MouseCursor::TextInput; break; - case ImGuiMouseCursor_ResizeAll: cursor = MouseCursor::SizeAll; break; - case ImGuiMouseCursor_ResizeEW: cursor = MouseCursor::SizeWE; break; - case ImGuiMouseCursor_ResizeNS: cursor = MouseCursor::SizeNS; break; - case ImGuiMouseCursor_ResizeNESW: cursor = MouseCursor::SizeNESW; break; - case ImGuiMouseCursor_ResizeNWSE: cursor = MouseCursor::SizeNWSE; break; - case ImGuiMouseCursor_Hand: cursor = MouseCursor::Hand; break; - } - - GetScene()->SetMouseCursor(cursor); - } - } - - void ImGuiLayer::AddItem(ImGuiPipeline const & item, String const & name) - { - pipelines_.insert(std::make_pair(name, item)); - } - - void ImGuiLayer::RemoveItem(String const & name) - { - auto iter = pipelines_.find(name); - if (iter != pipelines_.end()) - { - pipelines_.erase(iter); - } - } - - void ImGuiLayer::RemoveAllItems() - { - pipelines_.clear(); - } - -} \ No newline at end of file diff --git a/Kiwano/imgui/ImGuiView.cpp b/Kiwano/imgui/ImGuiView.cpp deleted file mode 100644 index c7f1662b..00000000 --- a/Kiwano/imgui/ImGuiView.cpp +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (C) 2019 Nomango - -#include "../kiwano-imgui.h" -#include "imgui_impl.hpp" - -namespace kiwano -{ - void ImGuiView::SetupComponent(Application* app) - { - // Setup Dear ImGui context - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; - //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls - - // Setup Dear ImGui style - ImGui::StyleColorsDark(); - //ImGui::StyleColorsClassic(); - - // Setup Platform/Renderer bindings - Init(app->GetWindow()->GetHandle()); - } - - void ImGuiView::DestroyComponent() - { - ImGui_Impl_Shutdown(); - ImGui::DestroyContext(); - } - - void ImGuiView::Init(HWND hwnd) - { - ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.BackendPlatformName = "imgui_impl_win32"; - io.ImeWindowHandle = hwnd; - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_Tab] = KeyCode::Tab; - io.KeyMap[ImGuiKey_LeftArrow] = KeyCode::Left; - io.KeyMap[ImGuiKey_RightArrow] = KeyCode::Right; - io.KeyMap[ImGuiKey_UpArrow] = KeyCode::Up; - io.KeyMap[ImGuiKey_DownArrow] = KeyCode::Down; - io.KeyMap[ImGuiKey_Delete] = KeyCode::Delete; - io.KeyMap[ImGuiKey_Backspace] = KeyCode::Back; - io.KeyMap[ImGuiKey_Space] = KeyCode::Space; - io.KeyMap[ImGuiKey_Enter] = KeyCode::Enter; - io.KeyMap[ImGuiKey_Escape] = KeyCode::Esc; - io.KeyMap[ImGuiKey_A] = KeyCode::A; - io.KeyMap[ImGuiKey_C] = KeyCode::C; - io.KeyMap[ImGuiKey_V] = KeyCode::V; - io.KeyMap[ImGuiKey_X] = KeyCode::X; - io.KeyMap[ImGuiKey_Y] = KeyCode::Y; - io.KeyMap[ImGuiKey_Z] = KeyCode::Z; - - ImGui_Impl_Init( - Renderer::Instance() - ); - } - - void ImGuiView::NewFrame() - { - ImGui_Impl_NewFrame(); - - ImGuiIO& io = ImGui::GetIO(); - KGE_ASSERT(io.Fonts->IsBuilt() && "Font atlas not built!"); - - // Setup display size (every frame to accommodate for window resizing) - Size display_size = Renderer::Instance().GetOutputSize(); - io.DisplaySize = ImVec2(display_size.x, display_size.y); - - ImGui::NewFrame(); - } - - void ImGuiView::Render() - { - ImGui::Render(); - - ImGui_Impl_RenderDrawData(ImGui::GetDrawData()); - } - - ImGuiLayerPtr ImGuiView::CreateLayer(Scene* scene) - { - auto iter = layers_.find(scene); - if (iter == layers_.end()) - { - ImGuiLayerPtr layer = new (std::nothrow) ImGuiLayer; - if (layer) - { - layers_.insert(std::make_pair(scene, layer.Get())); - } - return layer; - } - else - { - return iter->second; - } - } - - void ImGuiView::RemoveLayer(ImGuiLayer* layer) - { - using value_type = Map::value_type; - - auto iter = std::find_if(layers_.begin(), layers_.end(), [=](value_type const& value) - { - return value.second == layer; - }); - if (iter != layers_.end()) - { - layers_.erase(iter); - } - } - -} \ No newline at end of file diff --git a/Kiwano/third-party/dlls/websockets.dll b/Kiwano/third-party/dlls/websockets.dll deleted file mode 100644 index 18e35d37690547907219f058e708e680d874b1f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175616 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

wxJt?}ZIf()R{{pX$kc#@2x$ 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 diff --git a/Kiwano/third-party/libs/websockets.lib b/Kiwano/third-party/libs/websockets.lib deleted file mode 100644 index 4b94bbcc0880813a976ea248d9bcee5616662f55..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66876 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 - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation: - * version 2.1 of the License. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA - */ - -/** @file */ - -#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C -#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C - -#ifdef __cplusplus -#include -#include -# -extern "C" { -#else -#include -#endif - -#include "lws_config.h" - -/* - * CARE: everything using cmake defines needs to be below here - */ - -#if defined(LWS_WITH_ESP8266) -struct sockaddr_in; -#define LWS_POSIX 0 -#else -#define LWS_POSIX 1 -#endif - -#if defined(LWS_HAS_INTPTR_T) -#include -#define lws_intptr_t intptr_t -#else -typedef unsigned long long lws_intptr_t; -#endif - -#if defined(WIN32) || defined(_WIN32) -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif - -#include -#include -#include -#include -#ifndef _WIN32_WCE -#include -#else -#define _O_RDONLY 0x0000 -#define O_RDONLY _O_RDONLY -#endif - -// Visual studio older than 2015 and WIN_CE has only _stricmp -#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_WIN32_WCE) -#define strcasecmp _stricmp -#elif !defined(__MINGW32__) -#define strcasecmp stricmp -#endif -#define getdtablesize() 30000 - -#define LWS_INLINE __inline -#define LWS_VISIBLE -#define LWS_WARN_UNUSED_RESULT -#define LWS_WARN_DEPRECATED -#define LWS_FORMAT(string_index) - -#ifdef LWS_DLL -#ifdef LWS_INTERNAL -#define LWS_EXTERN extern __declspec(dllexport) -#else -#define LWS_EXTERN extern __declspec(dllimport) -#endif -#else -#define LWS_EXTERN -#endif - -#define LWS_INVALID_FILE INVALID_HANDLE_VALUE -#define LWS_O_RDONLY _O_RDONLY -#define LWS_O_WRONLY _O_WRONLY -#define LWS_O_CREAT _O_CREAT -#define LWS_O_TRUNC _O_TRUNC - -#if !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER < 1900) /* Visual Studio 2015 already defines this in */ -#define lws_snprintf _snprintf -#endif - -#ifndef __func__ -#define __func__ __FUNCTION__ -#endif - -#if !defined(__MINGW32__) &&(!defined(_MSC_VER) || _MSC_VER < 1900) && !defined(snprintf) -#define snprintf(buf,len, format,...) _snprintf_s(buf, len,len, format, __VA_ARGS__) -#endif - -#else /* NOT WIN32 */ -#include -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) -#include -#endif - -#if defined(__NetBSD__) || defined(__FreeBSD__) -#include -#endif - -#define LWS_INLINE inline -#define LWS_O_RDONLY O_RDONLY -#define LWS_O_WRONLY O_WRONLY -#define LWS_O_CREAT O_CREAT -#define LWS_O_TRUNC O_TRUNC - -#if !defined(LWS_WITH_ESP8266) && !defined(OPTEE_TA) && !defined(LWS_WITH_ESP32) -#include -#include -#define LWS_INVALID_FILE -1 -#else -#define getdtablesize() (30) -#if defined(LWS_WITH_ESP32) -#define LWS_INVALID_FILE NULL -#else -#define LWS_INVALID_FILE NULL -#endif -#endif - -#if defined(__GNUC__) - -/* warn_unused_result attribute only supported by GCC 3.4 or later */ -#if __GNUC__ >= 4 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) -#define LWS_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) -#else -#define LWS_WARN_UNUSED_RESULT -#endif - -#define LWS_VISIBLE __attribute__((visibility("default"))) -#define LWS_WARN_DEPRECATED __attribute__ ((deprecated)) -#define LWS_FORMAT(string_index) __attribute__ ((format(printf, string_index, string_index+1))) -#else -#define LWS_VISIBLE -#define LWS_WARN_UNUSED_RESULT -#define LWS_WARN_DEPRECATED -#define LWS_FORMAT(string_index) -#endif - -#if defined(__ANDROID__) -#include -#define getdtablesize() sysconf(_SC_OPEN_MAX) -#endif - -#endif - -#ifdef LWS_WITH_LIBEV -#include -#endif /* LWS_WITH_LIBEV */ -#ifdef LWS_WITH_LIBUV -#include -#ifdef LWS_HAVE_UV_VERSION_H -#include -#endif -#endif /* LWS_WITH_LIBUV */ -#ifdef LWS_WITH_LIBEVENT -#include -#endif /* LWS_WITH_LIBEVENT */ - -#ifndef LWS_EXTERN -#define LWS_EXTERN extern -#endif - -#ifdef _WIN32 -#define random rand -#else -#if !defined(OPTEE_TA) -#include -#include -#endif -#endif - -#ifdef LWS_OPENSSL_SUPPORT - -#ifdef USE_WOLFSSL -#ifdef USE_OLD_CYASSL -#include -#include -#else -#include -#include -#endif /* not USE_OLD_CYASSL */ -#else -#if defined(LWS_WITH_MBEDTLS) -#if defined(LWS_WITH_ESP32) -/* this filepath is passed to us but without quotes or <> */ -#undef MBEDTLS_CONFIG_FILE -#define MBEDTLS_CONFIG_FILE -#endif -#include -#endif -#include -#if !defined(LWS_WITH_MBEDTLS) -#include -#endif -#endif /* not USE_WOLFSSL */ -#endif - - -#define CONTEXT_PORT_NO_LISTEN -1 -#define CONTEXT_PORT_NO_LISTEN_SERVER -2 - -/** \defgroup log Logging - * - * ##Logging - * - * Lws provides flexible and filterable logging facilities, which can be - * used inside lws and in user code. - * - * Log categories may be individually filtered bitwise, and directed to built-in - * sinks for syslog-compatible logging, or a user-defined function. - */ -///@{ - -enum lws_log_levels { - LLL_ERR = 1 << 0, - LLL_WARN = 1 << 1, - LLL_NOTICE = 1 << 2, - LLL_INFO = 1 << 3, - LLL_DEBUG = 1 << 4, - LLL_PARSER = 1 << 5, - LLL_HEADER = 1 << 6, - LLL_EXT = 1 << 7, - LLL_CLIENT = 1 << 8, - LLL_LATENCY = 1 << 9, - LLL_USER = 1 << 10, - - LLL_COUNT = 11 /* set to count of valid flags */ -}; - -LWS_VISIBLE LWS_EXTERN void _lws_log(int filter, const char *format, ...) LWS_FORMAT(2); -LWS_VISIBLE LWS_EXTERN void _lws_logv(int filter, const char *format, va_list vl); -/** - * lwsl_timestamp: generate logging timestamp string - * - * \param level: logging level - * \param p: char * buffer to take timestamp - * \param len: length of p - * - * returns length written in p - */ -LWS_VISIBLE LWS_EXTERN int -lwsl_timestamp(int level, char *p, int len); - -/* these guys are unconditionally included */ - -#define lwsl_err(...) _lws_log(LLL_ERR, __VA_ARGS__) -#define lwsl_user(...) _lws_log(LLL_USER, __VA_ARGS__) - -#if !defined(LWS_WITH_NO_LOGS) -/* notice and warn are usually included by being compiled in */ -#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) -#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) -#endif -/* - * weaker logging can be deselected by telling CMake to build in RELEASE mode - * that gets rid of the overhead of checking while keeping _warn and _err - * active - */ - -#if defined(LWS_WITH_ESP8266) -#undef _DEBUG -#endif - -#ifdef _DEBUG -#if defined(LWS_WITH_NO_LOGS) -/* notice, warn and log are always compiled in */ -#define lwsl_warn(...) _lws_log(LLL_WARN, __VA_ARGS__) -#define lwsl_notice(...) _lws_log(LLL_NOTICE, __VA_ARGS__) -#endif -#define lwsl_info(...) _lws_log(LLL_INFO, __VA_ARGS__) -#define lwsl_debug(...) _lws_log(LLL_DEBUG, __VA_ARGS__) -#define lwsl_parser(...) _lws_log(LLL_PARSER, __VA_ARGS__) -#define lwsl_header(...) _lws_log(LLL_HEADER, __VA_ARGS__) -#define lwsl_ext(...) _lws_log(LLL_EXT, __VA_ARGS__) -#define lwsl_client(...) _lws_log(LLL_CLIENT, __VA_ARGS__) -#define lwsl_latency(...) _lws_log(LLL_LATENCY, __VA_ARGS__) - -#else /* no debug */ -#if defined(LWS_WITH_NO_LOGS) -#define lwsl_warn(...) do {} while(0) -#define lwsl_notice(...) do {} while(0) -#endif -#define lwsl_info(...) do {} while(0) -#define lwsl_debug(...) do {} while(0) -#define lwsl_parser(...) do {} while(0) -#define lwsl_header(...) do {} while(0) -#define lwsl_ext(...) do {} while(0) -#define lwsl_client(...) do {} while(0) -#define lwsl_latency(...) do {} while(0) - -#endif - -/** - * lwsl_hexdump() - helper to hexdump a buffer - * - * \param level: one of LLL_ constants - * \param buf: buffer start to dump - * \param len: length of buffer to dump - * - * If \p level is visible, does a nice hexdump -C style dump of \p buf for - * \p len bytes. This can be extremely convenient while debugging. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_hexdump_level(int level, const void *vbuf, size_t len); - -/** - * lwsl_hexdump() - helper to hexdump a buffer (DEBUG builds only) - * - * \param buf: buffer start to dump - * \param len: length of buffer to dump - * - * Calls through to lwsl_hexdump_level(LLL_DEBUG, ... for compatability. - * It's better to use lwsl_hexdump_level(level, ... directly so you can control - * the visibility. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_hexdump(const void *buf, size_t len); - -/** - * lws_is_be() - returns nonzero if the platform is Big Endian - */ -static LWS_INLINE int lws_is_be(void) { - const int probe = ~0xff; - - return *(const char *)&probe; -} - -/** - * lws_set_log_level() - Set the logging bitfield - * \param level: OR together the LLL_ debug contexts you want output from - * \param log_emit_function: NULL to leave it as it is, or a user-supplied - * function to perform log string emission instead of - * the default stderr one. - * - * log level defaults to "err", "warn" and "notice" contexts enabled and - * emission on stderr. If stderr is a tty (according to isatty()) then - * the output is coloured according to the log level using ANSI escapes. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_log_level(int level, - void (*log_emit_function)(int level, const char *line)); - -/** - * lwsl_emit_syslog() - helper log emit function writes to system log - * - * \param level: one of LLL_ log level indexes - * \param line: log string - * - * You use this by passing the function pointer to lws_set_log_level(), to set - * it as the log emit function, it is not called directly. - */ -LWS_VISIBLE LWS_EXTERN void -lwsl_emit_syslog(int level, const char *line); - -/** - * lwsl_visible() - returns true if the log level should be printed - * - * \param level: one of LLL_ log level indexes - * - * This is useful if you have to do work to generate the log content, you - * can skip the work if the log level used to print it is not actually - * enabled at runtime. - */ -LWS_VISIBLE LWS_EXTERN int -lwsl_visible(int level); - -///@} - - -#include - -#ifndef lws_container_of -#define lws_container_of(P,T,M) ((T *)((char *)(P) - offsetof(T, M))) -#endif - - -struct lws; -#ifndef ARRAY_SIZE -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) -#endif - -/* api change list for user code to test against */ - -#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG - -/* the struct lws_protocols has the id field present */ -#define LWS_FEATURE_PROTOCOLS_HAS_ID_FIELD - -/* you can call lws_get_peer_write_allowance */ -#define LWS_FEATURE_PROTOCOLS_HAS_PEER_WRITE_ALLOWANCE - -/* extra parameter introduced in 917f43ab821 */ -#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_LEN - -/* File operations stuff exists */ -#define LWS_FEATURE_FOPS - - -#if defined(_WIN32) -typedef SOCKET lws_sockfd_type; -typedef HANDLE lws_filefd_type; -#define lws_sockfd_valid(sfd) (!!sfd) -struct lws_pollfd { - lws_sockfd_type fd; /**< file descriptor */ - SHORT events; /**< which events to respond to */ - SHORT revents; /**< which events happened */ -}; -#define LWS_POLLHUP (FD_CLOSE) -#define LWS_POLLIN (FD_READ | FD_ACCEPT) -#define LWS_POLLOUT (FD_WRITE) -#else - - -#if defined(LWS_WITH_ESP8266) - -#include -#include - -typedef struct espconn * lws_sockfd_type; -typedef void * lws_filefd_type; -#define lws_sockfd_valid(sfd) (!!sfd) -struct pollfd { - lws_sockfd_type fd; /**< fd related to */ - short events; /**< which POLL... events to respond to */ - short revents; /**< which POLL... events occurred */ -}; -#define POLLIN 0x0001 -#define POLLPRI 0x0002 -#define POLLOUT 0x0004 -#define POLLERR 0x0008 -#define POLLHUP 0x0010 -#define POLLNVAL 0x0020 - -struct lws_vhost; - -lws_sockfd_type esp8266_create_tcp_listen_socket(struct lws_vhost *vh); -void esp8266_tcp_stream_accept(lws_sockfd_type fd, struct lws *wsi); - -#include -#include -#include "ets_sys.h" - -int ets_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); -#define snprintf ets_snprintf - -typedef os_timer_t uv_timer_t; -typedef void uv_cb_t(uv_timer_t *); - -void os_timer_disarm(void *); -void os_timer_setfn(os_timer_t *, os_timer_func_t *, void *); - -void ets_timer_arm_new(os_timer_t *, int, int, int); - -//void os_timer_arm(os_timer_t *, int, int); - -#define UV_VERSION_MAJOR 1 - -#define lws_uv_getloop(a, b) (NULL) - -static inline void uv_timer_init(void *l, uv_timer_t *t) -{ - (void)l; - memset(t, 0, sizeof(*t)); - os_timer_disarm(t); -} - -static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) -{ - os_timer_setfn(t, (os_timer_func_t *)cb, t); - /* ms, repeat */ - os_timer_arm(t, first, !!rep); -} - -static inline void uv_timer_stop(uv_timer_t *t) -{ - os_timer_disarm(t); -} - -#else -#if defined(LWS_WITH_ESP32) - -typedef int lws_sockfd_type; -typedef int lws_filefd_type; -#define lws_sockfd_valid(sfd) (sfd >= 0) -struct pollfd { - lws_sockfd_type fd; /**< fd related to */ - short events; /**< which POLL... events to respond to */ - short revents; /**< which POLL... events occurred */ -}; -#define POLLIN 0x0001 -#define POLLPRI 0x0002 -#define POLLOUT 0x0004 -#define POLLERR 0x0008 -#define POLLHUP 0x0010 -#define POLLNVAL 0x0020 - -#include -#include -#include -#include "esp_wifi.h" -#include "esp_system.h" -#include "esp_event.h" -#include "esp_event_loop.h" -#include "nvs.h" -#include "driver/gpio.h" -#include "esp_spi_flash.h" -#include "freertos/timers.h" - -#if !defined(CONFIG_FREERTOS_HZ) -#define CONFIG_FREERTOS_HZ 100 -#endif - -typedef TimerHandle_t uv_timer_t; -typedef void uv_cb_t(uv_timer_t *); -typedef void * uv_handle_t; - -struct timer_mapping { - uv_cb_t *cb; - uv_timer_t *t; -}; - -#define UV_VERSION_MAJOR 1 - -#define lws_uv_getloop(a, b) (NULL) - -static inline void uv_timer_init(void *l, uv_timer_t *t) -{ - (void)l; - *t = NULL; -} - -extern void esp32_uvtimer_cb(TimerHandle_t t); - -static inline void uv_timer_start(uv_timer_t *t, uv_cb_t *cb, int first, int rep) -{ - struct timer_mapping *tm = (struct timer_mapping *)malloc(sizeof(*tm)); - - if (!tm) - return; - - tm->t = t; - tm->cb = cb; - - *t = xTimerCreate("x", pdMS_TO_TICKS(first), !!rep, tm, - (TimerCallbackFunction_t)esp32_uvtimer_cb); - xTimerStart(*t, 0); -} - -static inline void uv_timer_stop(uv_timer_t *t) -{ - xTimerStop(*t, 0); -} - -static inline void uv_close(uv_handle_t *h, void *v) -{ - free(pvTimerGetTimerID((uv_timer_t)h)); - xTimerDelete(*(uv_timer_t *)h, 0); -} - -/* ESP32 helper declarations */ - -#include -#include - -#define LWS_PLUGIN_STATIC -#define LWS_MAGIC_REBOOT_TYPE_ADS 0x50001ffc -#define LWS_MAGIC_REBOOT_TYPE_REQ_FACTORY 0xb00bcafe -#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY 0xfaceb00b -#define LWS_MAGIC_REBOOT_TYPE_FORCED_FACTORY_BUTTON 0xf0cedfac - - -/* user code provides these */ - -extern void -lws_esp32_identify_physical_device(void); - -/* lws-plat-esp32 provides these */ - -typedef void (*lws_cb_scan_done)(uint16_t count, wifi_ap_record_t *recs, void *arg); - -enum genled_state { - LWSESP32_GENLED__INIT, - LWSESP32_GENLED__LOST_NETWORK, - LWSESP32_GENLED__NO_NETWORK, - LWSESP32_GENLED__CONN_AP, - LWSESP32_GENLED__GOT_IP, - LWSESP32_GENLED__OK, -}; - -struct lws_group_member { - struct lws_group_member *next; - uint64_t last_seen; - char model[16]; - char role[16]; - char host[32]; - char mac[20]; - int width, height; - struct ip4_addr addr; - struct ip6_addr addrv6; - uint8_t flags; -}; - -#define LWS_SYSTEM_GROUP_MEMBER_ADD 1 -#define LWS_SYSTEM_GROUP_MEMBER_CHANGE 2 -#define LWS_SYSTEM_GROUP_MEMBER_REMOVE 3 - -#define LWS_GROUP_FLAG_SELF 1 - -struct lws_esp32 { - char sta_ip[16]; - char sta_mask[16]; - char sta_gw[16]; - char serial[16]; - char opts[16]; - char model[16]; - char group[16]; - char role[16]; - char ssid[4][16]; - char password[4][32]; - char active_ssid[32]; - char access_pw[16]; - char hostname[32]; - char mac[20]; - mdns_server_t *mdns; - char region; - char inet; - char conn_ap; - - enum genled_state genled; - uint64_t genled_t; - - lws_cb_scan_done scan_consumer; - void *scan_consumer_arg; - struct lws_group_member *first; - int extant_group_members; -}; - -struct lws_esp32_image { - uint32_t romfs; - uint32_t romfs_len; - uint32_t json; - uint32_t json_len; -}; - -extern struct lws_esp32 lws_esp32; -struct lws_vhost; - -extern esp_err_t -lws_esp32_event_passthru(void *ctx, system_event_t *event); -extern void -lws_esp32_wlan_config(void); -extern void -lws_esp32_wlan_start_ap(void); -extern void -lws_esp32_wlan_start_station(void); -struct lws_context_creation_info; -extern void -lws_esp32_set_creation_defaults(struct lws_context_creation_info *info); -extern struct lws_context * -lws_esp32_init(struct lws_context_creation_info *, struct lws_vhost **pvh); -extern int -lws_esp32_wlan_nvs_get(int retry); -extern esp_err_t -lws_nvs_set_str(nvs_handle handle, const char* key, const char* value); -extern void -lws_esp32_restart_guided(uint32_t type); -extern const esp_partition_t * -lws_esp_ota_get_boot_partition(void); -extern int -lws_esp32_get_image_info(const esp_partition_t *part, struct lws_esp32_image *i, char *json, int json_len); -extern int -lws_esp32_leds_network_indication(void); - -extern uint32_t lws_esp32_get_reboot_type(void); -extern uint16_t lws_esp32_sine_interp(int n); - -/* required in external code by esp32 plat (may just return if no leds) */ -extern void lws_esp32_leds_timer_cb(TimerHandle_t th); -#else -typedef int lws_sockfd_type; -typedef int lws_filefd_type; -#define lws_sockfd_valid(sfd) (sfd >= 0) -#endif -#endif - -#define lws_pollfd pollfd -#define LWS_POLLHUP (POLLHUP|POLLERR) -#define LWS_POLLIN (POLLIN) -#define LWS_POLLOUT (POLLOUT) -#endif - - -#if (defined(WIN32) || defined(_WIN32)) && !defined(__MINGW32__) -/* ... */ -#define ssize_t SSIZE_T -#endif - -#if defined(WIN32) && defined(LWS_HAVE__STAT32I64) -#include -#include -#endif - -#if defined(LWS_HAVE_STDINT_H) -#include -#else -#if defined(WIN32) || defined(_WIN32) -/* !!! >:-[ */ -typedef unsigned __int32 uint32_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int8 uint8_t; -#else -typedef unsigned int uint32_t; -typedef unsigned short uint16_t; -typedef unsigned char uint8_t; -#endif -#endif - -typedef unsigned long long lws_filepos_t; -typedef long long lws_fileofs_t; -typedef uint32_t lws_fop_flags_t; - -/** struct lws_pollargs - argument structure for all external poll related calls - * passed in via 'in' */ -struct lws_pollargs { - lws_sockfd_type fd; /**< applicable socket descriptor */ - int events; /**< the new event mask */ - int prev_events; /**< the previous event mask */ -}; - -struct lws_tokens; -struct lws_token_limits; - -/*! \defgroup wsclose Websocket Close - * - * ##Websocket close frame control - * - * When we close a ws connection, we can send a reason code and a short - * UTF-8 description back with the close packet. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -/** enum lws_close_status - RFC6455 close status codes */ -enum lws_close_status { - LWS_CLOSE_STATUS_NOSTATUS = 0, - LWS_CLOSE_STATUS_NORMAL = 1000, - /**< 1000 indicates a normal closure, meaning that the purpose for - which the connection was established has been fulfilled. */ - LWS_CLOSE_STATUS_GOINGAWAY = 1001, - /**< 1001 indicates that an endpoint is "going away", such as a server - going down or a browser having navigated away from a page. */ - LWS_CLOSE_STATUS_PROTOCOL_ERR = 1002, - /**< 1002 indicates that an endpoint is terminating the connection due - to a protocol error. */ - LWS_CLOSE_STATUS_UNACCEPTABLE_OPCODE = 1003, - /**< 1003 indicates that an endpoint is terminating the connection - because it has received a type of data it cannot accept (e.g., an - endpoint that understands only text data MAY send this if it - receives a binary message). */ - LWS_CLOSE_STATUS_RESERVED = 1004, - /**< Reserved. The specific meaning might be defined in the future. */ - LWS_CLOSE_STATUS_NO_STATUS = 1005, - /**< 1005 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that no status - code was actually present. */ - LWS_CLOSE_STATUS_ABNORMAL_CLOSE = 1006, - /**< 1006 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that the - connection was closed abnormally, e.g., without sending or - receiving a Close control frame. */ - LWS_CLOSE_STATUS_INVALID_PAYLOAD = 1007, - /**< 1007 indicates that an endpoint is terminating the connection - because it has received data within a message that was not - consistent with the type of the message (e.g., non-UTF-8 [RFC3629] - data within a text message). */ - LWS_CLOSE_STATUS_POLICY_VIOLATION = 1008, - /**< 1008 indicates that an endpoint is terminating the connection - because it has received a message that violates its policy. This - is a generic status code that can be returned when there is no - other more suitable status code (e.g., 1003 or 1009) or if there - is a need to hide specific details about the policy. */ - LWS_CLOSE_STATUS_MESSAGE_TOO_LARGE = 1009, - /**< 1009 indicates that an endpoint is terminating the connection - because it has received a message that is too big for it to - process. */ - LWS_CLOSE_STATUS_EXTENSION_REQUIRED = 1010, - /**< 1010 indicates that an endpoint (client) is terminating the - connection because it has expected the server to negotiate one or - more extension, but the server didn't return them in the response - message of the WebSocket handshake. The list of extensions that - are needed SHOULD appear in the /reason/ part of the Close frame. - Note that this status code is not used by the server, because it - can fail the WebSocket handshake instead */ - LWS_CLOSE_STATUS_UNEXPECTED_CONDITION = 1011, - /**< 1011 indicates that a server is terminating the connection because - it encountered an unexpected condition that prevented it from - fulfilling the request. */ - LWS_CLOSE_STATUS_TLS_FAILURE = 1015, - /**< 1015 is a reserved value and MUST NOT be set as a status code in a - Close control frame by an endpoint. It is designated for use in - applications expecting a status code to indicate that the - connection was closed due to a failure to perform a TLS handshake - (e.g., the server certificate can't be verified). */ - - /****** add new things just above ---^ ******/ - - LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY = 9999, -}; - -/** - * lws_close_reason - Set reason and aux data to send with Close packet - * If you are going to return nonzero from the callback - * requesting the connection to close, you can optionally - * call this to set the reason the peer will be told if - * possible. - * - * \param wsi: The websocket connection to set the close reason on - * \param status: A valid close status from websocket standard - * \param buf: NULL or buffer containing up to 124 bytes of auxiliary data - * \param len: Length of data in \param buf to send - */ -LWS_VISIBLE LWS_EXTERN void -lws_close_reason(struct lws *wsi, enum lws_close_status status, - unsigned char *buf, size_t len); - -///@} - -struct lws; -struct lws_context; -/* needed even with extensions disabled for create context */ -struct lws_extension; - -/*! \defgroup lwsmeta lws-meta - * - * ##lws-meta protocol - * - * The protocol wraps other muxed connections inside one tcp connection. - * - * Commands are assigned from 0x41 up (so they are valid unicode) - */ -///@{ - -enum lws_meta_commands { - LWS_META_CMD_OPEN_SUBCHANNEL = 'A', - /**< Client requests to open new subchannel - */ - LWS_META_CMD_OPEN_RESULT, - /**< Result of client request to open new subchannel */ - LWS_META_CMD_CLOSE_NOTIFY, - /**< Notification of subchannel closure */ - LWS_META_CMD_CLOSE_RQ, - /**< client requests to close a subchannel */ - LWS_META_CMD_WRITE, - /**< connection writes something to specific channel index */ - - /****** add new things just above ---^ ******/ -}; - -/* channel numbers are transported offset by 0x20 so they are valid unicode */ - -#define LWS_META_TRANSPORT_OFFSET 0x20 - -///@} - -/*! \defgroup usercb User Callback - * - * ##User protocol callback - * - * The protocol callback is the primary way lws interacts with - * user code. For one of a list of a few dozen reasons the callback gets - * called at some event to be handled. - * - * All of the events can be ignored, returning 0 is taken as "OK" and returning - * nonzero in most cases indicates that the connection should be closed. - */ -///@{ - -struct lws_ssl_info { - int where; - int ret; -}; - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -/** enum lws_callback_reasons - reason you're getting a protocol callback */ -enum lws_callback_reasons { - LWS_CALLBACK_ESTABLISHED = 0, - /**< (VH) after the server completes a handshake with an incoming - * client. If you built the library with ssl support, in is a - * pointer to the ssl struct associated with the connection or NULL.*/ - LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 1, - /**< the request client connection has been unable to complete a - * handshake with the remote server. If in is non-NULL, you can - * find an error string of length len where it points to - * - * Diagnostic strings that may be returned include - * - * "getaddrinfo (ipv6) failed" - * "unknown address family" - * "getaddrinfo (ipv4) failed" - * "set socket opts failed" - * "insert wsi failed" - * "lws_ssl_client_connect1 failed" - * "lws_ssl_client_connect2 failed" - * "Peer hung up" - * "read failed" - * "HS: URI missing" - * "HS: Redirect code but no Location" - * "HS: URI did not parse" - * "HS: Redirect failed" - * "HS: Server did not return 200" - * "HS: OOM" - * "HS: disallowed by client filter" - * "HS: disallowed at ESTABLISHED" - * "HS: ACCEPT missing" - * "HS: ws upgrade response not 101" - * "HS: UPGRADE missing" - * "HS: Upgrade to something other than websocket" - * "HS: CONNECTION missing" - * "HS: UPGRADE malformed" - * "HS: PROTOCOL malformed" - * "HS: Cannot match protocol" - * "HS: EXT: list too big" - * "HS: EXT: failed setting defaults" - * "HS: EXT: failed parsing defaults" - * "HS: EXT: failed parsing options" - * "HS: EXT: Rejects server options" - * "HS: EXT: unknown ext" - * "HS: Accept hash wrong" - * "HS: Rejected by filter cb" - * "HS: OOM" - * "HS: SO_SNDBUF failed" - * "HS: Rejected at CLIENT_ESTABLISHED" - */ - LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH = 2, - /**< this is the last chance for the client user code to examine the - * http headers and decide to reject the connection. If the - * content in the headers is interesting to the - * client (url, etc) it needs to copy it out at - * this point since it will be destroyed before - * the CLIENT_ESTABLISHED call */ - LWS_CALLBACK_CLIENT_ESTABLISHED = 3, - /**< after your client connection completed - * a handshake with the remote server */ - LWS_CALLBACK_CLOSED = 4, - /**< when the websocket session ends */ - LWS_CALLBACK_CLOSED_HTTP = 5, - /**< when a HTTP (non-websocket) session ends */ - LWS_CALLBACK_RECEIVE = 6, - /**< data has appeared for this server endpoint from a - * remote client, it can be found at *in and is - * len bytes long */ - LWS_CALLBACK_RECEIVE_PONG = 7, - /**< servers receive PONG packets with this callback reason */ - LWS_CALLBACK_CLIENT_RECEIVE = 8, - /**< data has appeared from the server for the client connection, it - * can be found at *in and is len bytes long */ - LWS_CALLBACK_CLIENT_RECEIVE_PONG = 9, - /**< clients receive PONG packets with this callback reason */ - LWS_CALLBACK_CLIENT_WRITEABLE = 10, - /**< If you call lws_callback_on_writable() on a connection, you will - * get one of these callbacks coming when the connection socket - * is able to accept another write packet without blocking. - * If it already was able to take another packet without blocking, - * you'll get this callback at the next call to the service loop - * function. Notice that CLIENTs get LWS_CALLBACK_CLIENT_WRITEABLE - * and servers get LWS_CALLBACK_SERVER_WRITEABLE. */ - LWS_CALLBACK_SERVER_WRITEABLE = 11, - /**< See LWS_CALLBACK_CLIENT_WRITEABLE */ - LWS_CALLBACK_HTTP = 12, - /**< an http request has come from a client that is not - * asking to upgrade the connection to a websocket - * one. This is a chance to serve http content, - * for example, to send a script to the client - * which will then open the websockets connection. - * in points to the URI path requested and - * lws_serve_http_file() makes it very - * simple to send back a file to the client. - * Normally after sending the file you are done - * with the http connection, since the rest of the - * activity will come by websockets from the script - * that was delivered by http, so you will want to - * return 1; to close and free up the connection. */ - LWS_CALLBACK_HTTP_BODY = 13, - /**< the next len bytes data from the http - * request body HTTP connection is now available in in. */ - LWS_CALLBACK_HTTP_BODY_COMPLETION = 14, - /**< the expected amount of http request body has been delivered */ - LWS_CALLBACK_HTTP_FILE_COMPLETION = 15, - /**< a file requested to be sent down http link has completed. */ - LWS_CALLBACK_HTTP_WRITEABLE = 16, - /**< you can write more down the http protocol link now. */ - LWS_CALLBACK_FILTER_NETWORK_CONNECTION = 17, - /**< called when a client connects to - * the server at network level; the connection is accepted but then - * passed to this callback to decide whether to hang up immediately - * or not, based on the client IP. in contains the connection - * socket's descriptor. Since the client connection information is - * not available yet, wsi still pointing to the main server socket. - * Return non-zero to terminate the connection before sending or - * receiving anything. Because this happens immediately after the - * network connection from the client, there's no websocket protocol - * selected yet so this callback is issued only to protocol 0. */ - LWS_CALLBACK_FILTER_HTTP_CONNECTION = 18, - /**< called when the request has - * been received and parsed from the client, but the response is - * not sent yet. Return non-zero to disallow the connection. - * user is a pointer to the connection user space allocation, - * in is the URI, eg, "/" - * In your handler you can use the public APIs - * lws_hdr_total_length() / lws_hdr_copy() to access all of the - * headers using the header enums lws_token_indexes from - * libwebsockets.h to check for and read the supported header - * presence and content before deciding to allow the http - * connection to proceed or to kill the connection. */ - LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED = 19, - /**< A new client just had - * been connected, accepted, and instantiated into the pool. This - * callback allows setting any relevant property to it. Because this - * happens immediately after the instantiation of a new client, - * there's no websocket protocol selected yet so this callback is - * issued only to protocol 0. Only wsi is defined, pointing to the - * new client, and the return value is ignored. */ - LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION = 20, - /**< called when the handshake has - * been received and parsed from the client, but the response is - * not sent yet. Return non-zero to disallow the connection. - * user is a pointer to the connection user space allocation, - * in is the requested protocol name - * In your handler you can use the public APIs - * lws_hdr_total_length() / lws_hdr_copy() to access all of the - * headers using the header enums lws_token_indexes from - * libwebsockets.h to check for and read the supported header - * presence and content before deciding to allow the handshake - * to proceed or to kill the connection. */ - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS = 21, - /**< if configured for - * including OpenSSL support, this callback allows your user code - * to perform extra SSL_CTX_load_verify_locations() or similar - * calls to direct OpenSSL where to find certificates the client - * can use to confirm the remote server identity. user is the - * OpenSSL SSL_CTX* */ - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS = 22, - /**< if configured for - * including OpenSSL support, this callback allows your user code - * to load extra certificates into the server which allow it to - * verify the validity of certificates returned by clients. user - * is the server's OpenSSL SSL_CTX* */ - LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION = 23, - /**< if the libwebsockets vhost was created with the option - * LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT, then this - * callback is generated during OpenSSL verification of the cert - * sent from the client. It is sent to protocol[0] callback as - * no protocol has been negotiated on the connection yet. - * Notice that the libwebsockets context and wsi are both NULL - * during this callback. See - * http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html - * to understand more detail about the OpenSSL callback that - * generates this libwebsockets callback and the meanings of the - * arguments passed. In this callback, user is the x509_ctx, - * in is the ssl pointer and len is preverify_ok - * Notice that this callback maintains libwebsocket return - * conventions, return 0 to mean the cert is OK or 1 to fail it. - * This also means that if you don't handle this callback then - * the default callback action of returning 0 allows the client - * certificates. */ - LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER = 24, - /**< this callback happens - * when a client handshake is being compiled. user is NULL, - * in is a char **, it's pointing to a char * which holds the - * next location in the header buffer where you can add - * headers, and len is the remaining space in the header buffer, - * which is typically some hundreds of bytes. So, to add a canned - * cookie, your handler code might look similar to: - * - * char **p = (char **)in; - * - * if (len < 100) - * return 1; - * - * *p += sprintf(*p, "Cookie: a=b\x0d\x0a"); - * - * return 0; - * - * Notice if you add anything, you just have to take care about - * the CRLF on the line you added. Obviously this callback is - * optional, if you don't handle it everything is fine. - * - * Notice the callback is coming to protocols[0] all the time, - * because there is no specific protocol negotiated yet. */ - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY = 25, - /**< When the server handshake code - * sees that it does support a requested extension, before - * accepting the extension by additing to the list sent back to - * the client it gives this callback just to check that it's okay - * to use that extension. It calls back to the requested protocol - * and with in being the extension name, len is 0 and user is - * valid. Note though at this time the ESTABLISHED callback hasn't - * happened yet so if you initialize user content there, user - * content during this callback might not be useful for anything. */ - LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED = 26, - /**< When a client - * connection is being prepared to start a handshake to a server, - * each supported extension is checked with protocols[0] callback - * with this reason, giving the user code a chance to suppress the - * claim to support that extension by returning non-zero. If - * unhandled, by default 0 will be returned and the extension - * support included in the header to the server. Notice this - * callback comes to protocols[0]. */ - LWS_CALLBACK_PROTOCOL_INIT = 27, - /**< One-time call per protocol, per-vhost using it, so it can - * do initial setup / allocations etc */ - LWS_CALLBACK_PROTOCOL_DESTROY = 28, - /**< One-time call per protocol, per-vhost using it, indicating - * this protocol won't get used at all after this callback, the - * vhost is getting destroyed. Take the opportunity to - * deallocate everything that was allocated by the protocol. */ - LWS_CALLBACK_WSI_CREATE = 29, - /**< outermost (earliest) wsi create notification to protocols[0] */ - LWS_CALLBACK_WSI_DESTROY = 30, - /**< outermost (latest) wsi destroy notification to protocols[0] */ - LWS_CALLBACK_GET_THREAD_ID = 31, - /**< lws can accept callback when writable requests from other - * threads, if you implement this callback and return an opaque - * current thread ID integer. */ - - /* external poll() management support */ - LWS_CALLBACK_ADD_POLL_FD = 32, - /**< lws normally deals with its poll() or other event loop - * internally, but in the case you are integrating with another - * server you will need to have lws sockets share a - * polling array with the other server. This and the other - * POLL_FD related callbacks let you put your specialized - * poll array interface code in the callback for protocol 0, the - * first protocol you support, usually the HTTP protocol in the - * serving case. - * This callback happens when a socket needs to be - * added to the polling loop: in points to a struct - * lws_pollargs; the fd member of the struct is the file - * descriptor, and events contains the active events - * - * If you are using the internal lws polling / event loop - * you can just ignore these callbacks. */ - LWS_CALLBACK_DEL_POLL_FD = 33, - /**< This callback happens when a socket descriptor - * needs to be removed from an external polling array. in is - * again the struct lws_pollargs containing the fd member - * to be removed. If you are using the internal polling - * loop, you can just ignore it. */ - LWS_CALLBACK_CHANGE_MODE_POLL_FD = 34, - /**< This callback happens when lws wants to modify the events for - * a connection. - * in is the struct lws_pollargs with the fd to change. - * The new event mask is in events member and the old mask is in - * the prev_events member. - * If you are using the internal polling loop, you can just ignore - * it. */ - LWS_CALLBACK_LOCK_POLL = 35, - /**< These allow the external poll changes driven - * by lws to participate in an external thread locking - * scheme around the changes, so the whole thing is threadsafe. - * These are called around three activities in the library, - * - inserting a new wsi in the wsi / fd table (len=1) - * - deleting a wsi from the wsi / fd table (len=1) - * - changing a wsi's POLLIN/OUT state (len=0) - * Locking and unlocking external synchronization objects when - * len == 1 allows external threads to be synchronized against - * wsi lifecycle changes if it acquires the same lock for the - * duration of wsi dereference from the other thread context. */ - LWS_CALLBACK_UNLOCK_POLL = 36, - /**< See LWS_CALLBACK_LOCK_POLL, ignore if using lws internal poll */ - - LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY = 37, - /**< if configured for including OpenSSL support but no private key - * file has been specified (ssl_private_key_filepath is NULL), this is - * called to allow the user to set the private key directly via - * libopenssl and perform further operations if required; this might be - * useful in situations where the private key is not directly accessible - * by the OS, for example if it is stored on a smartcard. - * user is the server's OpenSSL SSL_CTX* */ - LWS_CALLBACK_WS_PEER_INITIATED_CLOSE = 38, - /**< The peer has sent an unsolicited Close WS packet. in and - * len are the optional close code (first 2 bytes, network - * order) and the optional additional information which is not - * defined in the standard, and may be a string or non-human- readable data. - * If you return 0 lws will echo the close and then close the - * connection. If you return nonzero lws will just close the - * connection. */ - - LWS_CALLBACK_WS_EXT_DEFAULTS = 39, - /**< Gives client connections an opportunity to adjust negotiated - * extension defaults. `user` is the extension name that was - * negotiated (eg, "permessage-deflate"). `in` points to a - * buffer and `len` is the buffer size. The user callback can - * set the buffer to a string describing options the extension - * should parse. Or just ignore for defaults. */ - - LWS_CALLBACK_CGI = 40, - /**< CGI: CGI IO events on stdin / out / err are sent here on - * protocols[0]. The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. */ - LWS_CALLBACK_CGI_TERMINATED = 41, - /**< CGI: The related CGI process ended, this is called before - * the wsi is closed. Used to, eg, terminate chunking. - * The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. The child PID that terminated is in len. */ - LWS_CALLBACK_CGI_STDIN_DATA = 42, - /**< CGI: Data is, to be sent to the CGI process stdin, eg from - * a POST body. The provided `lws_callback_http_dummy()` - * handles this and the callback should be directed there if - * you use CGI. */ - LWS_CALLBACK_CGI_STDIN_COMPLETED = 43, - /**< CGI: no more stdin is coming. The provided - * `lws_callback_http_dummy()` handles this and the callback - * should be directed there if you use CGI. */ - LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP = 44, - /**< The HTTP client connection has succeeded, and is now - * connected to the server */ - LWS_CALLBACK_CLOSED_CLIENT_HTTP = 45, - /**< The HTTP client connection is closing */ - LWS_CALLBACK_RECEIVE_CLIENT_HTTP = 46, - /**< This simply indicates data was received on the HTTP client - * connection. It does NOT drain or provide the data. - * This exists to neatly allow a proxying type situation, - * where this incoming data will go out on another connection. - * If the outgoing connection stalls, we should stall processing - * the incoming data. So a handler for this in that case should - * simply set a flag to indicate there is incoming data ready - * and ask for a writeable callback on the outgoing connection. - * In the writable callback he can check the flag and then get - * and drain the waiting incoming data using lws_http_client_read(). - * This will use callbacks to LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ - * to get and drain the incoming data, where it should be sent - * back out on the outgoing connection. */ - LWS_CALLBACK_COMPLETED_CLIENT_HTTP = 47, - /**< The client transaction completed... at the moment this - * is the same as closing since transaction pipelining on - * client side is not yet supported. */ - LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ = 48, - /**< This is generated by lws_http_client_read() used to drain - * incoming data. In the case the incoming data was chunked, - * it will be split into multiple smaller callbacks for each - * chunk block, removing the chunk headers. If not chunked, - * it will appear all in one callback. */ - LWS_CALLBACK_HTTP_BIND_PROTOCOL = 49, - /**< By default, all HTTP handling is done in protocols[0]. - * However you can bind different protocols (by name) to - * different parts of the URL space using callback mounts. This - * callback occurs in the new protocol when a wsi is bound - * to that protocol. Any protocol allocation related to the - * http transaction processing should be created then. - * These specific callbacks are necessary because with HTTP/1.1, - * a single connection may perform at series of different - * transactions at different URLs, thus the lifetime of the - * protocol bind is just for one transaction, not connection. */ - LWS_CALLBACK_HTTP_DROP_PROTOCOL = 50, - /**< This is called when a transaction is unbound from a protocol. - * It indicates the connection completed its transaction and may - * do something different now. Any protocol allocation related - * to the http transaction processing should be destroyed. */ - LWS_CALLBACK_CHECK_ACCESS_RIGHTS = 51, - /**< This gives the user code a chance to forbid an http access. - * `in` points to a `struct lws_process_html_args`, which - * describes the URL, and a bit mask describing the type of - * authentication required. If the callback returns nonzero, - * the transaction ends with HTTP_STATUS_UNAUTHORIZED. */ - LWS_CALLBACK_PROCESS_HTML = 52, - /**< This gives your user code a chance to mangle outgoing - * HTML. `in` points to a `struct lws_process_html_args` - * which describes the buffer containing outgoing HTML. - * The buffer may grow up to `.max_len` (currently +128 - * bytes per buffer). - * */ - LWS_CALLBACK_ADD_HEADERS = 53, - /**< This gives your user code a chance to add headers to a - * transaction bound to your protocol. `in` points to a - * `struct lws_process_html_args` describing a buffer and length - * you can add headers into using the normal lws apis. - * - * Only `args->p` and `args->len` are valid, and `args->p` should - * be moved on by the amount of bytes written, if any. Eg - * - * case LWS_CALLBACK_ADD_HEADERS: - * - * struct lws_process_html_args *args = - * (struct lws_process_html_args *)in; - * - * if (lws_add_http_header_by_name(wsi, - * (unsigned char *)"set-cookie:", - * (unsigned char *)cookie, cookie_len, - * (unsigned char **)&args->p, - * (unsigned char *)args->p + args->max_len)) - * return 1; - * - * break; - */ - LWS_CALLBACK_SESSION_INFO = 54, - /**< This is only generated by user code using generic sessions. - * It's used to get a `struct lws_session_info` filled in by - * generic sessions with information about the logged-in user. - * See the messageboard sample for an example of how to use. */ - - LWS_CALLBACK_GS_EVENT = 55, - /**< Indicates an event happened to the Generic Sessions session. - * `in` contains a `struct lws_gs_event_args` describing the event. */ - LWS_CALLBACK_HTTP_PMO = 56, - /**< per-mount options for this connection, called before - * the normal LWS_CALLBACK_HTTP when the mount has per-mount - * options. - */ - LWS_CALLBACK_CLIENT_HTTP_WRITEABLE = 57, - /**< when doing an HTTP type client connection, you can call - * lws_client_http_body_pending(wsi, 1) from - * LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER to get these callbacks - * sending the HTTP headers. - * - * From this callback, when you have sent everything, you should let - * lws know by calling lws_client_http_body_pending(wsi, 0) - */ - LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION = 58, - /**< Similar to LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION - * this callback is called during OpenSSL verification of the cert - * sent from the server to the client. It is sent to protocol[0] - * callback as no protocol has been negotiated on the connection yet. - * Notice that the wsi is set because lws_client_connect_via_info was - * successful. - * - * See http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html - * to understand more detail about the OpenSSL callback that - * generates this libwebsockets callback and the meanings of the - * arguments passed. In this callback, user is the x509_ctx, - * in is the ssl pointer and len is preverify_ok. - * - * THIS IS NOT RECOMMENDED BUT if a cert validation error shall be - * overruled and cert shall be accepted as ok, - * X509_STORE_CTX_set_error((X509_STORE_CTX*)user, X509_V_OK); must be - * called and return value must be 0 to mean the cert is OK; - * returning 1 will fail the cert in any case. - * - * This also means that if you don't handle this callback then - * the default callback action of returning 0 will not accept the - * certificate in case of a validation error decided by the SSL lib. - * - * This is expected and secure behaviour when validating certificates. - * - * Note: LCCSCF_ALLOW_SELFSIGNED and - * LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK still work without this - * callback being implemented. - */ - LWS_CALLBACK_RAW_RX = 59, - /**< RAW mode connection RX */ - LWS_CALLBACK_RAW_CLOSE = 60, - /**< RAW mode connection is closing */ - LWS_CALLBACK_RAW_WRITEABLE = 61, - /**< RAW mode connection may be written */ - LWS_CALLBACK_RAW_ADOPT = 62, - /**< RAW mode connection was adopted (equivalent to 'wsi created') */ - LWS_CALLBACK_RAW_ADOPT_FILE = 63, - /**< RAW mode file was adopted (equivalent to 'wsi created') */ - LWS_CALLBACK_RAW_RX_FILE = 64, - /**< RAW mode file has something to read */ - LWS_CALLBACK_RAW_WRITEABLE_FILE = 65, - /**< RAW mode file is writeable */ - LWS_CALLBACK_RAW_CLOSE_FILE = 66, - /**< RAW mode wsi that adopted a file is closing */ - LWS_CALLBACK_SSL_INFO = 67, - /**< SSL connections only. An event you registered an - * interest in at the vhost has occurred on a connection - * using the vhost. in is a pointer to a - * struct lws_ssl_info containing information about the - * event*/ - LWS_CALLBACK_CHILD_WRITE_VIA_PARENT = 68, - /**< Child has been marked with parent_carries_io attribute, so - * lws_write directs the to this callback at the parent, - * in is a struct lws_write_passthru containing the args - * the lws_write() was called with. - */ - LWS_CALLBACK_CHILD_CLOSING = 69, - /**< Sent to parent to notify them a child is closing / being - * destroyed. in is the child wsi. - */ - LWS_CALLBACK_CGI_PROCESS_ATTACH = 70, - /**< CGI: Sent when the CGI process is spawned for the wsi. The - * len parameter is the PID of the child process */ - - /****** add new things just above ---^ ******/ - - LWS_CALLBACK_USER = 1000, - /**< user code can use any including above without fear of clashes */ -}; - - - -/** - * typedef lws_callback_function() - User server actions - * \param wsi: Opaque websocket instance pointer - * \param reason: The reason for the call - * \param user: Pointer to per-session user data allocated by library - * \param in: Pointer used for some callback reasons - * \param len: Length set for some callback reasons - * - * This callback is the way the user controls what is served. All the - * protocol detail is hidden and handled by the library. - * - * For each connection / session there is user data allocated that is - * pointed to by "user". You set the size of this user data area when - * the library is initialized with lws_create_server. - */ -typedef int -lws_callback_function(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len); - -#define LWS_CB_REASON_AUX_BF__CGI 1 -#define LWS_CB_REASON_AUX_BF__PROXY 2 -#define LWS_CB_REASON_AUX_BF__CGI_CHUNK_END 4 -#define LWS_CB_REASON_AUX_BF__CGI_HEADERS 8 -///@} - -/*! \defgroup generic hash - * ## Generic Hash related functions - * - * Lws provides generic hash / digest accessors that abstract the ones - * provided by whatever OpenSSL library you are linking against. - * - * It lets you use the same code if you build against mbedtls or OpenSSL - * for example. - */ -///@{ - -#ifdef LWS_OPENSSL_SUPPORT - -#if defined(LWS_WITH_MBEDTLS) -#include -#include -#include -#endif - -#define LWS_GENHASH_TYPE_SHA1 0 -#define LWS_GENHASH_TYPE_SHA256 1 -#define LWS_GENHASH_TYPE_SHA512 2 - -struct lws_genhash_ctx { - uint8_t type; -#if defined(LWS_WITH_MBEDTLS) - union { - mbedtls_sha1_context sha1; - mbedtls_sha256_context sha256; - mbedtls_sha512_context sha512; - } u; -#else - const EVP_MD *evp_type; - EVP_MD_CTX *mdctx; -#endif -}; - -/** lws_genhash_size() - get hash size in bytes - * - * \param type: one of LWS_GENHASH_TYPE_... - * - * Returns number of bytes in this type of hash - */ -LWS_VISIBLE LWS_EXTERN size_t LWS_WARN_UNUSED_RESULT -lws_genhash_size(int type); - -/** lws_genhash_init() - prepare your struct lws_genhash_ctx for use - * - * \param ctx: your struct lws_genhash_ctx - * \param type: one of LWS_GENHASH_TYPE_... - * - * Initializes the hash context for the type you requested - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhash_init(struct lws_genhash_ctx *ctx, int type); - -/** lws_genhash_update() - digest len bytes of the buffer starting at in - * - * \param ctx: your struct lws_genhash_ctx - * \param in: start of the bytes to digest - * \param len: count of bytes to digest - * - * Updates the state of your hash context to reflect digesting len bytes from in - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_genhash_update(struct lws_genhash_ctx *ctx, const void *in, size_t len); - -/** lws_genhash_destroy() - copy out the result digest and destroy the ctx - * - * \param ctx: your struct lws_genhash_ctx - * \param result: NULL, or where to copy the result hash - * - * Finalizes the hash and copies out the digest. Destroys any allocations such - * that ctx can safely go out of scope after calling this. - * - * NULL result is supported so that you can destroy the ctx cleanly on error - * conditions, where there is no valid result. - */ -LWS_VISIBLE LWS_EXTERN int -lws_genhash_destroy(struct lws_genhash_ctx *ctx, void *result); - -#endif - -///@} - -/*! \defgroup extensions Extension related functions - * ##Extension releated functions - * - * Ws defines optional extensions, lws provides the ability to implement these - * in user code if so desired. - * - * We provide one extensions permessage-deflate. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_extension_callback_reasons { - LWS_EXT_CB_SERVER_CONTEXT_CONSTRUCT = 0, - LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT = 1, - LWS_EXT_CB_SERVER_CONTEXT_DESTRUCT = 2, - LWS_EXT_CB_CLIENT_CONTEXT_DESTRUCT = 3, - LWS_EXT_CB_CONSTRUCT = 4, - LWS_EXT_CB_CLIENT_CONSTRUCT = 5, - LWS_EXT_CB_CHECK_OK_TO_REALLY_CLOSE = 6, - LWS_EXT_CB_CHECK_OK_TO_PROPOSE_EXTENSION = 7, - LWS_EXT_CB_DESTROY = 8, - LWS_EXT_CB_DESTROY_ANY_WSI_CLOSING = 9, - LWS_EXT_CB_ANY_WSI_ESTABLISHED = 10, - LWS_EXT_CB_PACKET_RX_PREPARSE = 11, - LWS_EXT_CB_PACKET_TX_PRESEND = 12, - LWS_EXT_CB_PACKET_TX_DO_SEND = 13, - LWS_EXT_CB_HANDSHAKE_REPLY_TX = 14, - LWS_EXT_CB_FLUSH_PENDING_TX = 15, - LWS_EXT_CB_EXTENDED_PAYLOAD_RX = 16, - LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION = 17, - LWS_EXT_CB_1HZ = 18, - LWS_EXT_CB_REQUEST_ON_WRITEABLE = 19, - LWS_EXT_CB_IS_WRITEABLE = 20, - LWS_EXT_CB_PAYLOAD_TX = 21, - LWS_EXT_CB_PAYLOAD_RX = 22, - LWS_EXT_CB_OPTION_DEFAULT = 23, - LWS_EXT_CB_OPTION_SET = 24, - LWS_EXT_CB_OPTION_CONFIRM = 25, - LWS_EXT_CB_NAMED_OPTION_SET = 26, - - /****** add new things just above ---^ ******/ -}; - -/** enum lws_ext_options_types */ -enum lws_ext_options_types { - EXTARG_NONE, /**< does not take an argument */ - EXTARG_DEC, /**< requires a decimal argument */ - EXTARG_OPT_DEC /**< may have an optional decimal argument */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** struct lws_ext_options - Option arguments to the extension. These are - * used in the negotiation at ws upgrade time. - * The helper function lws_ext_parse_options() - * uses these to generate callbacks */ -struct lws_ext_options { - const char *name; /**< Option name, eg, "server_no_context_takeover" */ - enum lws_ext_options_types type; /**< What kind of args the option can take */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** struct lws_ext_option_arg */ -struct lws_ext_option_arg { - const char *option_name; /**< may be NULL, option_index used then */ - int option_index; /**< argument ordinal to use if option_name missing */ - const char *start; /**< value */ - int len; /**< length of value */ -}; - -/** - * typedef lws_extension_callback_function() - Hooks to allow extensions to operate - * \param context: Websockets context - * \param ext: This extension - * \param wsi: Opaque websocket instance pointer - * \param reason: The reason for the call - * \param user: Pointer to ptr to per-session user data allocated by library - * \param in: Pointer used for some callback reasons - * \param len: Length set for some callback reasons - * - * Each extension that is active on a particular connection receives - * callbacks during the connection lifetime to allow the extension to - * operate on websocket data and manage itself. - * - * Libwebsockets takes care of allocating and freeing "user" memory for - * each active extension on each connection. That is what is pointed to - * by the user parameter. - * - * LWS_EXT_CB_CONSTRUCT: called when the server has decided to - * select this extension from the list provided by the client, - * just before the server will send back the handshake accepting - * the connection with this extension active. This gives the - * extension a chance to initialize its connection context found - * in user. - * - * LWS_EXT_CB_CLIENT_CONSTRUCT: same as LWS_EXT_CB_CONSTRUCT - * but called when client is instantiating this extension. Some - * extensions will work the same on client and server side and then - * you can just merge handlers for both CONSTRUCTS. - * - * LWS_EXT_CB_DESTROY: called when the connection the extension was - * being used on is about to be closed and deallocated. It's the - * last chance for the extension to deallocate anything it has - * allocated in the user data (pointed to by user) before the - * user data is deleted. This same callback is used whether you - * are in client or server instantiation context. - * - * LWS_EXT_CB_PACKET_RX_PREPARSE: when this extension was active on - * a connection, and a packet of data arrived at the connection, - * it is passed to this callback to give the extension a chance to - * change the data, eg, decompress it. user is pointing to the - * extension's private connection context data, in is pointing - * to an lws_tokens struct, it consists of a char * pointer called - * token, and an int called token_len. At entry, these are - * set to point to the received buffer and set to the content - * length. If the extension will grow the content, it should use - * a new buffer allocated in its private user context data and - * set the pointed-to lws_tokens members to point to its buffer. - * - * LWS_EXT_CB_PACKET_TX_PRESEND: this works the same way as - * LWS_EXT_CB_PACKET_RX_PREPARSE above, except it gives the - * extension a chance to change websocket data just before it will - * be sent out. Using the same lws_token pointer scheme in in, - * the extension can change the buffer and the length to be - * transmitted how it likes. Again if it wants to grow the - * buffer safely, it should copy the data into its own buffer and - * set the lws_tokens token pointer to it. - * - * LWS_EXT_CB_ARGS_VALIDATE: - */ -typedef int -lws_extension_callback_function(struct lws_context *context, - const struct lws_extension *ext, struct lws *wsi, - enum lws_extension_callback_reasons reason, - void *user, void *in, size_t len); - -/** struct lws_extension - An extension we support */ -struct lws_extension { - const char *name; /**< Formal extension name, eg, "permessage-deflate" */ - lws_extension_callback_function *callback; /**< Service callback */ - const char *client_offer; /**< String containing exts and options client offers */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** - * lws_set_extension_option(): set extension option if possible - * - * \param wsi: websocket connection - * \param ext_name: name of ext, like "permessage-deflate" - * \param opt_name: name of option, like "rx_buf_size" - * \param opt_val: value to set option to - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_extension_option(struct lws *wsi, const char *ext_name, - const char *opt_name, const char *opt_val); - -#ifndef LWS_NO_EXTENSIONS -/* lws_get_internal_extensions() - DEPRECATED - * - * \Deprecated There is no longer a set internal extensions table. The table is provided - * by user code along with application-specific settings. See the test - * client and server for how to do. - */ -static LWS_INLINE LWS_WARN_DEPRECATED const struct lws_extension * -lws_get_internal_extensions(void) { return NULL; } - -/** - * lws_ext_parse_options() - deal with parsing negotiated extension options - * - * \param ext: related extension struct - * \param wsi: websocket connection - * \param ext_user: per-connection extension private data - * \param opts: list of supported options - * \param o: option string to parse - * \param len: length - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_ext_parse_options(const struct lws_extension *ext, struct lws *wsi, - void *ext_user, const struct lws_ext_options *opts, - const char *o, int len); -#endif - -/** lws_extension_callback_pm_deflate() - extension for RFC7692 - * - * \param context: lws context - * \param ext: related lws_extension struct - * \param wsi: websocket connection - * \param reason: incoming callback reason - * \param user: per-connection extension private data - * \param in: pointer parameter - * \param len: length parameter - * - * Built-in callback implementing RFC7692 permessage-deflate - */ -LWS_EXTERN -int lws_extension_callback_pm_deflate( - struct lws_context *context, const struct lws_extension *ext, - struct lws *wsi, enum lws_extension_callback_reasons reason, - void *user, void *in, size_t len); - -/* - * The internal exts are part of the public abi - * If we add more extensions, publish the callback here ------v - */ -///@} - -/*! \defgroup Protocols-and-Plugins Protocols and Plugins - * \ingroup lwsapi - * - * ##Protocol and protocol plugin -related apis - * - * Protocols bind ws protocol names to a custom callback specific to that - * protocol implementaion. - * - * A list of protocols can be passed in at context creation time, but it is - * also legal to leave that NULL and add the protocols and their callback code - * using plugins. - * - * Plugins are much preferable compared to cut and pasting code into an - * application each time, since they can be used standalone. - */ -///@{ -/** struct lws_protocols - List of protocols and handlers client or server - * supports. */ - -struct lws_protocols { - const char *name; - /**< Protocol name that must match the one given in the client - * Javascript new WebSocket(url, 'protocol') name. */ - lws_callback_function *callback; - /**< The service callback used for this protocol. It allows the - * service action for an entire protocol to be encapsulated in - * the protocol-specific callback */ - size_t per_session_data_size; - /**< Each new connection using this protocol gets - * this much memory allocated on connection establishment and - * freed on connection takedown. A pointer to this per-connection - * allocation is passed into the callback in the 'user' parameter */ - size_t rx_buffer_size; - /**< lws allocates this much space for rx data and informs callback - * when something came. Due to rx flow control, the callback may not - * be able to consume it all without having to return to the event - * loop. That is supported in lws. - * - * If .tx_packet_size is 0, this also controls how much may be sent at once - * for backwards compatibility. - */ - unsigned int id; - /**< ignored by lws, but useful to contain user information bound - * to the selected protocol. For example if this protocol was - * called "myprotocol-v2", you might set id to 2, and the user - * code that acts differently according to the version can do so by - * switch (wsi->protocol->id), user code might use some bits as - * capability flags based on selected protocol version, etc. */ - void *user; /**< ignored by lws, but user code can pass a pointer - here it can later access from the protocol callback */ - size_t tx_packet_size; - /**< 0 indicates restrict send() size to .rx_buffer_size for backwards- - * compatibility. - * If greater than zero, a single send() is restricted to this amount - * and any remainder is buffered by lws and sent afterwards also in - * these size chunks. Since that is expensive, it's preferable - * to restrict one fragment you are trying to send to match this - * size. - */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -struct lws_vhost; - -/** - * lws_vhost_name_to_protocol() - get vhost's protocol object from its name - * - * \param vh: vhost to search - * \param name: protocol name - * - * Returns NULL or a pointer to the vhost's protocol of the requested name - */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name); - -/** - * lws_get_protocol() - Returns a protocol pointer from a websocket - * connection. - * \param wsi: pointer to struct websocket you want to know the protocol of - * - * - * Some apis can act on all live connections of a given protocol, - * this is how you can get a pointer to the active protocol if needed. - */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_get_protocol(struct lws *wsi); - -/** lws_protocol_get() - deprecated: use lws_get_protocol */ -LWS_VISIBLE LWS_EXTERN const struct lws_protocols * -lws_protocol_get(struct lws *wsi) LWS_WARN_DEPRECATED; - -/** - * lws_protocol_vh_priv_zalloc() - Allocate and zero down a protocol's per-vhost - * storage - * \param vhost: vhost the instance is related to - * \param prot: protocol the instance is related to - * \param size: bytes to allocate - * - * Protocols often find it useful to allocate a per-vhost struct, this is a - * helper to be called in the per-vhost init LWS_CALLBACK_PROTOCOL_INIT - */ -LWS_VISIBLE LWS_EXTERN void * -lws_protocol_vh_priv_zalloc(struct lws_vhost *vhost, const struct lws_protocols *prot, - int size); - -/** - * lws_protocol_vh_priv_get() - retreive a protocol's per-vhost storage - * - * \param vhost: vhost the instance is related to - * \param prot: protocol the instance is related to - * - * Recover a pointer to the allocated per-vhost storage for the protocol created - * by lws_protocol_vh_priv_zalloc() earlier - */ -LWS_VISIBLE LWS_EXTERN void * -lws_protocol_vh_priv_get(struct lws_vhost *vhost, const struct lws_protocols *prot); - -/** - * lws_adjust_protocol_psds - change a vhost protocol's per session data size - * - * \param wsi: a connection with the protocol to change - * \param new_size: the new size of the per session data size for the protocol - * - * Returns user_space for the wsi, after allocating - * - * This should not be used except to initalize a vhost protocol's per session - * data size one time, before any connections are accepted. - * - * Sometimes the protocol wraps another protocol and needs to discover and set - * its per session data size at runtime. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_adjust_protocol_psds(struct lws *wsi, size_t new_size); - -/** - * lws_finalize_startup() - drop initial process privileges - * - * \param context: lws context - * - * This is called after the end of the vhost protocol initializations, but - * you may choose to call it earlier - */ -LWS_VISIBLE LWS_EXTERN int -lws_finalize_startup(struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN int -lws_protocol_init(struct lws_context *context); - -#ifdef LWS_WITH_PLUGINS - -/* PLUGINS implies LIBUV */ - -#define LWS_PLUGIN_API_MAGIC 180 - -/** struct lws_plugin_capability - how a plugin introduces itself to lws */ -struct lws_plugin_capability { - unsigned int api_magic; /**< caller fills this in, plugin fills rest */ - const struct lws_protocols *protocols; /**< array of supported protocols provided by plugin */ - int count_protocols; /**< how many protocols */ - const struct lws_extension *extensions; /**< array of extensions provided by plugin */ - int count_extensions; /**< how many extensions */ -}; - -typedef int (*lws_plugin_init_func)(struct lws_context *, - struct lws_plugin_capability *); -typedef int (*lws_plugin_destroy_func)(struct lws_context *); - -/** struct lws_plugin */ -struct lws_plugin { - struct lws_plugin *list; /**< linked list */ -#if (UV_VERSION_MAJOR > 0) - uv_lib_t lib; /**< shared library pointer */ -#else - void *l; /**< so we can compile on ancient libuv */ -#endif - char name[64]; /**< name of the plugin */ - struct lws_plugin_capability caps; /**< plugin capabilities */ -}; - -#endif - -///@} - - -/*! \defgroup generic-sessions plugin: generic-sessions - * \ingroup Protocols-and-Plugins - * - * ##Plugin Generic-sessions related - * - * generic-sessions plugin provides a reusable, generic session and login / - * register / forgot password framework including email verification. - */ -///@{ - -#define LWSGS_EMAIL_CONTENT_SIZE 16384 -/**< Maximum size of email we might send */ - -/* SHA-1 binary and hexified versions */ -/** typedef struct lwsgw_hash_bin */ -typedef struct { unsigned char bin[20]; /**< binary representation of hash */} lwsgw_hash_bin; -/** typedef struct lwsgw_hash */ -typedef struct { char id[41]; /**< ascii hex representation of hash */ } lwsgw_hash; - -/** enum lwsgs_auth_bits */ -enum lwsgs_auth_bits { - LWSGS_AUTH_LOGGED_IN = 1, /**< user is logged in as somebody */ - LWSGS_AUTH_ADMIN = 2, /**< logged in as the admin user */ - LWSGS_AUTH_VERIFIED = 4, /**< user has verified his email */ - LWSGS_AUTH_FORGOT_FLOW = 8, /**< he just completed "forgot password" flow */ -}; - -/** struct lws_session_info - information about user session status */ -struct lws_session_info { - char username[32]; /**< username logged in as, or empty string */ - char email[100]; /**< email address associated with login, or empty string */ - char ip[72]; /**< ip address session was started from */ - unsigned int mask; /**< access rights mask associated with session - * see enum lwsgs_auth_bits */ - char session[42]; /**< session id string, usable as opaque uid when not logged in */ -}; - -/** enum lws_gs_event */ -enum lws_gs_event { - LWSGSE_CREATED, /**< a new user was created */ - LWSGSE_DELETED /**< an existing user was deleted */ -}; - -/** struct lws_gs_event_args */ -struct lws_gs_event_args { - enum lws_gs_event event; /**< which event happened */ - const char *username; /**< which username the event happened to */ - const char *email; /**< the email address of that user */ -}; - -///@} - - -/*! \defgroup context-and-vhost context and vhost related functions - * ##Context and Vhost releated functions - * \ingroup lwsapi - * - * - * LWS requires that there is one context, in which you may define multiple - * vhosts. Each vhost is a virtual host, with either its own listen port - * or sharing an existing one. Each vhost has its own SSL context that can - * be set up individually or left disabled. - * - * If you don't care about multiple "site" support, you can ignore it and - * lws will create a single default vhost at context creation time. - */ -///@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ - -/** enum lws_context_options - context and vhost options */ -enum lws_context_options { - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = (1 << 1) | - (1 << 12), - /**< (VH) Don't allow the connection unless the client has a - * client cert that we recognize; provides - * LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ - LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = (1 << 2), - /**< (CTX) Don't try to get the server's hostname */ - LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = (1 << 3) | - (1 << 12), - /**< (VH) Allow non-SSL (plaintext) connections on the same - * port as SSL is listening... undermines the security of SSL; - * provides LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT */ - LWS_SERVER_OPTION_LIBEV = (1 << 4), - /**< (CTX) Use libev event loop */ - LWS_SERVER_OPTION_DISABLE_IPV6 = (1 << 5), - /**< (VH) Disable IPV6 support */ - LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS = (1 << 6), - /**< (VH) Don't load OS CA certs, you will need to load your - * own CA cert(s) */ - LWS_SERVER_OPTION_PEER_CERT_NOT_REQUIRED = (1 << 7), - /**< (VH) Accept connections with no valid Cert (eg, selfsigned) */ - LWS_SERVER_OPTION_VALIDATE_UTF8 = (1 << 8), - /**< (VH) Check UT-8 correctness */ - LWS_SERVER_OPTION_SSL_ECDH = (1 << 9) | - (1 << 12), - /**< (VH) initialize ECDH ciphers */ - LWS_SERVER_OPTION_LIBUV = (1 << 10), - /**< (CTX) Use libuv event loop */ - LWS_SERVER_OPTION_REDIRECT_HTTP_TO_HTTPS = (1 << 11) | - (1 << 12), - /**< (VH) Use http redirect to force http to https - * (deprecated: use mount redirection) */ - LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT = (1 << 12), - /**< (CTX) Initialize the SSL library at all */ - LWS_SERVER_OPTION_EXPLICIT_VHOSTS = (1 << 13), - /**< (CTX) Only create the context when calling context - * create api, implies user code will create its own vhosts */ - LWS_SERVER_OPTION_UNIX_SOCK = (1 << 14), - /**< (VH) Use Unix socket */ - LWS_SERVER_OPTION_STS = (1 << 15), - /**< (VH) Send Strict Transport Security header, making - * clients subsequently go to https even if user asked for http */ - LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY = (1 << 16), - /**< (VH) Enable LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE to take effect */ - LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE = (1 << 17), - /**< (VH) if set, only ipv6 allowed on the vhost */ - LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN = (1 << 18), - /**< (CTX) Libuv only: Do not spin on SIGSEGV / SIGFPE. A segfault - * normally makes the lib spin so you can attach a debugger to it - * even if it happened without a debugger in place. You can disable - * that by giving this option. - */ - LWS_SERVER_OPTION_JUST_USE_RAW_ORIGIN = (1 << 19), - /**< For backwards-compatibility reasons, by default - * lws prepends "http://" to the origin you give in the client - * connection info struct. If you give this flag when you create - * the context, only the string you give in the client connect - * info for .origin (if any) will be used directly. - */ - LWS_SERVER_OPTION_FALLBACK_TO_RAW = (1 << 20), - /**< (VH) if invalid http is coming in the first line, */ - LWS_SERVER_OPTION_LIBEVENT = (1 << 21), - /**< (CTX) Use libevent event loop */ - LWS_SERVER_OPTION_ONLY_RAW = (1 << 22), - /**< (VH) All connections to this vhost / port are RAW as soon as - * the connection is accepted, no HTTP is going to be coming. - */ - LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE = (1 << 23), - /**< (VH) Set to allow multiple listen sockets on one interface + - * address + port. The default is to strictly allow only one - * listen socket at a time. This is automatically selected if you - * have multiple service threads. - */ - LWS_SERVER_OPTION_CREATE_VHOST_SSL_CTX = (1 << 24), - /**< (VH) Force setting up the vhost SSL_CTX, even though the user - * code doesn't explicitly provide a cert in the info struct. It - * implies the user code is going to provide a cert at the - * LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which - * provides the vhost SSL_CTX * in the user parameter. - */ - - /****** add new things just above ---^ ******/ -}; - -#define lws_check_opt(c, f) (((c) & (f)) == (f)) - -struct lws_plat_file_ops; - -/** struct lws_context_creation_info - parameters to create context and /or vhost with - * - * This is also used to create vhosts.... if LWS_SERVER_OPTION_EXPLICIT_VHOSTS - * is not given, then for backwards compatibility one vhost is created at - * context-creation time using the info from this struct. - * - * If LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, then no vhosts are created - * at the same time as the context, they are expected to be created afterwards. - */ -struct lws_context_creation_info { - int port; - /**< VHOST: Port to listen on. Use CONTEXT_PORT_NO_LISTEN to suppress - * listening for a client. Use CONTEXT_PORT_NO_LISTEN_SERVER if you are - * writing a server but you are using \ref sock-adopt instead of the - * built-in listener */ - const char *iface; - /**< VHOST: NULL to bind the listen socket to all interfaces, or the - * interface name, eg, "eth2" - * If options specifies LWS_SERVER_OPTION_UNIX_SOCK, this member is - * the pathname of a UNIX domain socket. you can use the UNIX domain - * sockets in abstract namespace, by prepending an at symbol to the - * socket name. */ - const struct lws_protocols *protocols; - /**< VHOST: Array of structures listing supported protocols and a protocol- - * specific callback for each one. The list is ended with an - * entry that has a NULL callback pointer. */ - const struct lws_extension *extensions; - /**< VHOST: NULL or array of lws_extension structs listing the - * extensions this context supports. */ - const struct lws_token_limits *token_limits; - /**< CONTEXT: NULL or struct lws_token_limits pointer which is initialized - * with a token length limit for each possible WSI_TOKEN_ */ - const char *ssl_private_key_password; - /**< VHOST: NULL or the passphrase needed for the private key. (For - * backwards compatibility, this can also be used to pass the client - * cert passphrase when setting up a vhost client SSL context, but it is - * preferred to use .client_ssl_private_key_password for that.) */ - const char *ssl_cert_filepath; - /**< VHOST: If libwebsockets was compiled to use ssl, and you want - * to listen using SSL, set to the filepath to fetch the - * server cert from, otherwise NULL for unencrypted. (For backwards - * compatibility, this can also be used to pass the client certificate - * when setting up a vhost client SSL context, but it is preferred to - * use .client_ssl_cert_filepath for that.) */ - const char *ssl_private_key_filepath; - /**< VHOST: filepath to private key if wanting SSL mode; - * if this is set to NULL but ssl_cert_filepath is set, the - * OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY callback is called - * to allow setting of the private key directly via openSSL - * library calls. (For backwards compatibility, this can also be used - * to pass the client cert private key filepath when setting up a - * vhost client SSL context, but it is preferred to use - * .client_ssl_private_key_filepath for that.) */ - const char *ssl_ca_filepath; - /**< VHOST: CA certificate filepath or NULL. (For backwards - * compatibility, this can also be used to pass the client CA - * filepath when setting up a vhost client SSL context, - * but it is preferred to use .client_ssl_ca_filepath for that.) */ - const char *ssl_cipher_list; - /**< VHOST: List of valid ciphers to use (eg, - * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" - * or you can leave it as NULL to get "DEFAULT" (For backwards - * compatibility, this can also be used to pass the client cipher - * list when setting up a vhost client SSL context, - * but it is preferred to use .client_ssl_cipher_list for that.)*/ - const char *http_proxy_address; - /**< VHOST: If non-NULL, attempts to proxy via the given address. - * If proxy auth is required, use format "username:password\@server:port" */ - unsigned int http_proxy_port; - /**< VHOST: If http_proxy_address was non-NULL, uses this port */ - int gid; - /**< CONTEXT: group id to change to after setting listen socket, or -1. */ - int uid; - /**< CONTEXT: user id to change to after setting listen socket, or -1. */ - unsigned int options; - /**< VHOST + CONTEXT: 0, or LWS_SERVER_OPTION_... bitfields */ - void *user; - /**< VHOST + CONTEXT: optional user pointer that will be associated - * with the context when creating the context (and can be retrieved by - * lws_context_user(context), or with the vhost when creating the vhost - * (and can be retrieved by lws_vhost_user(vhost)). You will need to - * use LWS_SERVER_OPTION_EXPLICIT_VHOSTS and create the vhost separately - * if you care about giving the context and vhost different user pointer - * values. - */ - int ka_time; - /**< CONTEXT: 0 for no TCP keepalive, otherwise apply this keepalive - * timeout to all libwebsocket sockets, client or server */ - int ka_probes; - /**< CONTEXT: if ka_time was nonzero, after the timeout expires how many - * times to try to get a response from the peer before giving up - * and killing the connection */ - int ka_interval; - /**< CONTEXT: if ka_time was nonzero, how long to wait before each ka_probes - * attempt */ -#ifdef LWS_OPENSSL_SUPPORT - SSL_CTX *provided_client_ssl_ctx; - /**< CONTEXT: If non-null, swap out libwebsockets ssl - * implementation for the one provided by provided_ssl_ctx. - * Libwebsockets no longer is responsible for freeing the context - * if this option is selected. */ -#else /* maintain structure layout either way */ - void *provided_client_ssl_ctx; /**< dummy if ssl disabled */ -#endif - - short max_http_header_data; - /**< CONTEXT: The max amount of header payload that can be handled - * in an http request (unrecognized header payload is dropped) */ - short max_http_header_pool; - /**< CONTEXT: The max number of connections with http headers that - * can be processed simultaneously (the corresponding memory is - * allocated for the lifetime of the context). If the pool is - * busy new incoming connections must wait for accept until one - * becomes free. */ - - unsigned int count_threads; - /**< CONTEXT: how many contexts to create in an array, 0 = 1 */ - unsigned int fd_limit_per_thread; - /**< CONTEXT: nonzero means restrict each service thread to this - * many fds, 0 means the default which is divide the process fd - * limit by the number of threads. */ - unsigned int timeout_secs; - /**< VHOST: various processes involving network roundtrips in the - * library are protected from hanging forever by timeouts. If - * nonzero, this member lets you set the timeout used in seconds. - * Otherwise a default timeout is used. */ - const char *ecdh_curve; - /**< VHOST: if NULL, defaults to initializing server with "prime256v1" */ - const char *vhost_name; - /**< VHOST: name of vhost, must match external DNS name used to - * access the site, like "warmcat.com" as it's used to match - * Host: header and / or SNI name for SSL. */ - const char * const *plugin_dirs; - /**< CONTEXT: NULL, or NULL-terminated array of directories to - * scan for lws protocol plugins at context creation time */ - const struct lws_protocol_vhost_options *pvo; - /**< VHOST: pointer to optional linked list of per-vhost - * options made accessible to protocols */ - int keepalive_timeout; - /**< VHOST: (default = 0 = 60s) seconds to allow remote - * client to hold on to an idle HTTP/1.1 connection */ - const char *log_filepath; - /**< VHOST: filepath to append logs to... this is opened before - * any dropping of initial privileges */ - const struct lws_http_mount *mounts; - /**< VHOST: optional linked list of mounts for this vhost */ - const char *server_string; - /**< CONTEXT: string used in HTTP headers to identify server - * software, if NULL, "libwebsockets". */ - unsigned int pt_serv_buf_size; - /**< CONTEXT: 0 = default of 4096. This buffer is used by - * various service related features including file serving, it - * defines the max chunk of file that can be sent at once. - * At the risk of lws having to buffer failed large sends, it - * can be increased to, eg, 128KiB to improve throughput. */ - unsigned int max_http_header_data2; - /**< CONTEXT: if max_http_header_data is 0 and this - * is nonzero, this will be used in place of the default. It's - * like this for compatibility with the original short version, - * this is unsigned int length. */ - long ssl_options_set; - /**< VHOST: Any bits set here will be set as SSL options */ - long ssl_options_clear; - /**< VHOST: Any bits set here will be cleared as SSL options */ - unsigned short ws_ping_pong_interval; - /**< CONTEXT: 0 for none, else interval in seconds between sending - * PINGs on idle websocket connections. When the PING is sent, - * the PONG must come within the normal timeout_secs timeout period - * or the connection will be dropped. - * Any RX or TX traffic on the connection restarts the interval timer, - * so a connection which always sends or receives something at intervals - * less than the interval given here will never send PINGs / expect - * PONGs. Conversely as soon as the ws connection is established, an - * idle connection will do the PING / PONG roundtrip as soon as - * ws_ping_pong_interval seconds has passed without traffic - */ - const struct lws_protocol_vhost_options *headers; - /**< VHOST: pointer to optional linked list of per-vhost - * canned headers that are added to server responses */ - - const struct lws_protocol_vhost_options *reject_service_keywords; - /**< CONTEXT: Optional list of keywords and rejection codes + text. - * - * The keywords are checked for existing in the user agent string. - * - * Eg, "badrobot" "404 Not Found" - */ - void *external_baggage_free_on_destroy; - /**< CONTEXT: NULL, or pointer to something externally malloc'd, that - * should be freed when the context is destroyed. This allows you to - * automatically sync the freeing action to the context destruction - * action, so there is no need for an external free() if the context - * succeeded to create. - */ - - const char *client_ssl_private_key_password; - /**< VHOST: Client SSL context init: NULL or the passphrase needed - * for the private key */ - const char *client_ssl_cert_filepath; - /**< VHOST: Client SSL context init:T he certificate the client - * should present to the peer on connection */ - const char *client_ssl_private_key_filepath; - /**< VHOST: Client SSL context init: filepath to client private key - * if this is set to NULL but client_ssl_cert_filepath is set, you - * can handle the LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS - * callback of protocols[0] to allow setting of the private key directly - * via openSSL library calls */ - const char *client_ssl_ca_filepath; - /**< VHOST: Client SSL context init: CA certificate filepath or NULL */ - const char *client_ssl_cipher_list; - /**< VHOST: Client SSL context init: List of valid ciphers to use (eg, - * "RC4-MD5:RC4-SHA:AES128-SHA:AES256-SHA:HIGH:!DSS:!aNULL" - * or you can leave it as NULL to get "DEFAULT" */ - - const struct lws_plat_file_ops *fops; - /**< CONTEXT: NULL, or pointer to an array of fops structs, terminated - * by a sentinel with NULL .open. - * - * If NULL, lws provides just the platform file operations struct for - * backwards compatibility. - */ - int simultaneous_ssl_restriction; - /**< CONTEXT: 0 (no limit) or limit of simultaneous SSL sessions possible.*/ - const char *socks_proxy_address; - /**< VHOST: If non-NULL, attempts to proxy via the given address. - * If proxy auth is required, use format "username:password\@server:port" */ - unsigned int socks_proxy_port; - /**< VHOST: If socks_proxy_address was non-NULL, uses this port */ -#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP) - cap_value_t caps[4]; - /**< CONTEXT: array holding Linux capabilities you want to - * continue to be available to the server after it transitions - * to a noprivileged user. Usually none are needed but for, eg, - * .bind_iface, CAP_NET_RAW is required. This gives you a way - * to still have the capability but drop root. - */ - char count_caps; - /**< CONTEXT: count of Linux capabilities in .caps[]. 0 means - * no capabilities will be inherited from root (the default) */ -#endif - int bind_iface; - /**< VHOST: nonzero to strictly bind sockets to the interface name in - * .iface (eg, "eth2"), using SO_BIND_TO_DEVICE. - * - * Requires SO_BINDTODEVICE support from your OS and CAP_NET_RAW - * capability. - * - * Notice that common things like access network interface IP from - * your local machine use your lo / loopback interface and will be - * disallowed by this. - */ - int ssl_info_event_mask; - /**< VHOST: mask of ssl events to be reported on LWS_CALLBACK_SSL_INFO - * callback for connections on this vhost. The mask values are of - * the form SSL_CB_ALERT, defined in openssl/ssl.h. The default of - * 0 means no info events will be reported. - */ - unsigned int timeout_secs_ah_idle; - /**< VHOST: seconds to allow a client to hold an ah without using it. - * 0 defaults to 10s. */ - unsigned short ip_limit_ah; - /**< CONTEXT: max number of ah a single IP may use simultaneously - * 0 is no limit. This is a soft limit: if the limit is - * reached, connections from that IP will wait in the ah - * waiting list and not be able to acquire an ah until - * a connection belonging to the IP relinquishes one it - * already has. - */ - unsigned short ip_limit_wsi; - /**< CONTEXT: max number of wsi a single IP may use simultaneously. - * 0 is no limit. This is a hard limit, connections from - * the same IP will simply be dropped once it acquires the - * amount of simultaneous wsi / accepted connections - * given here. - */ - uint32_t http2_settings[7]; - /**< CONTEXT: after context creation http2_settings[1] thru [6] have - * been set to the lws platform default values. - * VHOST: if http2_settings[0] is nonzero, the values given in - * http2_settings[1]..[6] are used instead of the lws - * platform default values. - * Just leave all at 0 if you don't care. - */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility - * - * The below is to ensure later library versions with new - * members added above will see 0 (default) even if the app - * was not built against the newer headers. - */ - - void *_unused[8]; /**< dummy */ -}; - -/** - * lws_create_context() - Create the websocket handler - * \param info: pointer to struct with parameters - * - * This function creates the listening socket (if serving) and takes care - * of all initialization in one step. - * - * If option LWS_SERVER_OPTION_EXPLICIT_VHOSTS is given, no vhost is - * created; you're expected to create your own vhosts afterwards using - * lws_create_vhost(). Otherwise a vhost named "default" is also created - * using the information in the vhost-related members, for compatibility. - * - * After initialization, it returns a struct lws_context * that - * represents this server. After calling, user code needs to take care - * of calling lws_service() with the context pointer to get the - * server's sockets serviced. This must be done in the same process - * context as the initialization call. - * - * The protocol callback functions are called for a handful of events - * including http requests coming in, websocket connections becoming - * established, and data arriving; it's also called periodically to allow - * async transmission. - * - * HTTP requests are sent always to the FIRST protocol in protocol, since - * at that time websocket protocol has not been negotiated. Other - * protocols after the first one never see any HTTP callback activity. - * - * The server created is a simple http server by default; part of the - * websocket standard is upgrading this http connection to a websocket one. - * - * This allows the same server to provide files like scripts and favicon / - * images or whatever over http and dynamic data over websockets all in - * one place; they're all handled in the user callback. - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * -lws_create_context(struct lws_context_creation_info *info); - -/** - * lws_context_destroy() - Destroy the websocket context - * \param context: Websocket context - * - * This function closes any active connections and then frees the - * context. After calling this, any further use of the context is - * undefined. - */ -LWS_VISIBLE LWS_EXTERN void -lws_context_destroy(struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN void -lws_context_destroy2(struct lws_context *context); - -typedef int (*lws_reload_func)(void); - -/** - * lws_context_deprecate() - Deprecate the websocket context - * - * \param context: Websocket context - * \param cb: Callback notified when old context listen sockets are closed - * - * This function is used on an existing context before superceding it - * with a new context. - * - * It closes any listen sockets in the context, so new connections are - * not possible. - * - * And it marks the context to be deleted when the number of active - * connections into it falls to zero. - * - * Otherwise if you attach the deprecated context to the replacement - * context when it has been created using lws_context_attach_deprecated() - * both any deprecated and the new context will service their connections. - * - * This is aimed at allowing seamless configuration reloads. - * - * The callback cb will be called after the listen sockets are actually - * closed and may be reopened. In the callback the new context should be - * configured and created. (With libuv, socket close happens async after - * more loop events). - */ -LWS_VISIBLE LWS_EXTERN void -lws_context_deprecate(struct lws_context *context, lws_reload_func cb); - -LWS_VISIBLE LWS_EXTERN int -lws_context_is_deprecated(struct lws_context *context); - -/** - * lws_set_proxy() - Setups proxy to lws_context. - * \param vhost: pointer to struct lws_vhost you want set proxy for - * \param proxy: pointer to c string containing proxy in format address:port - * - * Returns 0 if proxy string was parsed and proxy was setup. - * Returns -1 if proxy is NULL or has incorrect format. - * - * This is only required if your OS does not provide the http_proxy - * environment variable (eg, OSX) - * - * IMPORTANT! You should call this function right after creation of the - * lws_context and before call to connect. If you call this - * function after connect behavior is undefined. - * This function will override proxy settings made on lws_context - * creation with genenv() call. - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_proxy(struct lws_vhost *vhost, const char *proxy); - -/** - * lws_set_socks() - Setup socks to lws_context. - * \param vhost: pointer to struct lws_vhost you want set socks for - * \param socks: pointer to c string containing socks in format address:port - * - * Returns 0 if socks string was parsed and socks was setup. - * Returns -1 if socks is NULL or has incorrect format. - * - * This is only required if your OS does not provide the socks_proxy - * environment variable (eg, OSX) - * - * IMPORTANT! You should call this function right after creation of the - * lws_context and before call to connect. If you call this - * function after connect behavior is undefined. - * This function will override proxy settings made on lws_context - * creation with genenv() call. - */ -LWS_VISIBLE LWS_EXTERN int -lws_set_socks(struct lws_vhost *vhost, const char *socks); - -struct lws_vhost; - -/** - * lws_create_vhost() - Create a vhost (virtual server context) - * \param context: pointer to result of lws_create_context() - * \param info: pointer to struct with parameters - * - * This function creates a virtual server (vhost) using the vhost-related - * members of the info struct. You can create many vhosts inside one context - * if you created the context with the option LWS_SERVER_OPTION_EXPLICIT_VHOSTS - */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_create_vhost(struct lws_context *context, - struct lws_context_creation_info *info); - -/** - * lws_vhost_destroy() - Destroy a vhost (virtual server context) - * - * \param vh: pointer to result of lws_create_vhost() - * - * This function destroys a vhost. Normally, if you just want to exit, - * then lws_destroy_context() will take care of everything. If you want - * to destroy an individual vhost and all connections and allocations, you - * can do it with this. - * - * If the vhost has a listen sockets shared by other vhosts, it will be given - * to one of the vhosts sharing it rather than closed. - */ -LWS_VISIBLE LWS_EXTERN void -lws_vhost_destroy(struct lws_vhost *vh); - -/** - * lwsws_get_config_globals() - Parse a JSON server config file - * \param info: pointer to struct with parameters - * \param d: filepath of the config file - * \param config_strings: storage for the config strings extracted from JSON, - * the pointer is incremented as strings are stored - * \param len: pointer to the remaining length left in config_strings - * the value is decremented as strings are stored - * - * This function prepares a n lws_context_creation_info struct with global - * settings from a file d. - * - * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled - */ -LWS_VISIBLE LWS_EXTERN int -lwsws_get_config_globals(struct lws_context_creation_info *info, const char *d, - char **config_strings, int *len); - -/** - * lwsws_get_config_vhosts() - Create vhosts from a JSON server config file - * \param context: pointer to result of lws_create_context() - * \param info: pointer to struct with parameters - * \param d: filepath of the config file - * \param config_strings: storage for the config strings extracted from JSON, - * the pointer is incremented as strings are stored - * \param len: pointer to the remaining length left in config_strings - * the value is decremented as strings are stored - * - * This function creates vhosts into a context according to the settings in - *JSON files found in directory d. - * - * Requires CMake option LWS_WITH_LEJP_CONF to have been enabled - */ -LWS_VISIBLE LWS_EXTERN int -lwsws_get_config_vhosts(struct lws_context *context, - struct lws_context_creation_info *info, const char *d, - char **config_strings, int *len); - -/** lws_vhost_get() - \deprecated deprecated: use lws_get_vhost() */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED; - -/** - * lws_get_vhost() - return the vhost a wsi belongs to - * - * \param wsi: which connection - */ -LWS_VISIBLE LWS_EXTERN struct lws_vhost * -lws_get_vhost(struct lws *wsi); - -/** - * lws_json_dump_vhost() - describe vhost state and stats in JSON - * - * \param vh: the vhost - * \param buf: buffer to fill with JSON - * \param len: max length of buf - */ -LWS_VISIBLE LWS_EXTERN int -lws_json_dump_vhost(const struct lws_vhost *vh, char *buf, int len); - -/** - * lws_json_dump_context() - describe context state and stats in JSON - * - * \param context: the context - * \param buf: buffer to fill with JSON - * \param len: max length of buf - * \param hide_vhosts: nonzero to not provide per-vhost mount etc information - * - * Generates a JSON description of vhost state into buf - */ -LWS_VISIBLE LWS_EXTERN int -lws_json_dump_context(const struct lws_context *context, char *buf, int len, - int hide_vhosts); - -/** - * lws_vhost_user() - get the user data associated with the vhost - * \param vhost: Websocket vhost - * - * This returns the optional user pointer that can be attached to - * a vhost when it was created. Lws never dereferences this pointer, it only - * sets it when the vhost is created, and returns it using this api. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_vhost_user(struct lws_vhost *vhost); - -/** - * lws_context_user() - get the user data associated with the context - * \param context: Websocket context - * - * This returns the optional user allocation that can be attached to - * the context the sockets live in at context_create time. It's a way - * to let all sockets serviced in the same context share data without - * using globals statics in the user code. - */ -LWS_VISIBLE LWS_EXTERN void * -lws_context_user(struct lws_context *context); - -/*! \defgroup vhost-mounts Vhost mounts and options - * \ingroup context-and-vhost-creation - * - * ##Vhost mounts and options - */ -///@{ -/** struct lws_protocol_vhost_options - linked list of per-vhost protocol - * name=value options - * - * This provides a general way to attach a linked-list of name=value pairs, - * which can also have an optional child link-list using the options member. - */ -struct lws_protocol_vhost_options { - const struct lws_protocol_vhost_options *next; /**< linked list */ - const struct lws_protocol_vhost_options *options; /**< child linked-list of more options for this node */ - const char *name; /**< name of name=value pair */ - const char *value; /**< value of name=value pair */ -}; - -/** enum lws_mount_protocols - * This specifies the mount protocol for a mountpoint, whether it is to be - * served from a filesystem, or it is a cgi etc. - */ -enum lws_mount_protocols { - LWSMPRO_HTTP = 0, /**< http reverse proxy */ - LWSMPRO_HTTPS = 1, /**< https reverse proxy */ - LWSMPRO_FILE = 2, /**< serve from filesystem directory */ - LWSMPRO_CGI = 3, /**< pass to CGI to handle */ - LWSMPRO_REDIR_HTTP = 4, /**< redirect to http:// url */ - LWSMPRO_REDIR_HTTPS = 5, /**< redirect to https:// url */ - LWSMPRO_CALLBACK = 6, /**< hand by named protocol's callback */ -}; - -/** struct lws_http_mount - * - * arguments for mounting something in a vhost's url namespace - */ -struct lws_http_mount { - const struct lws_http_mount *mount_next; - /**< pointer to next struct lws_http_mount */ - const char *mountpoint; - /**< mountpoint in http pathspace, eg, "/" */ - const char *origin; - /**< path to be mounted, eg, "/var/www/warmcat.com" */ - const char *def; - /**< default target, eg, "index.html" */ - const char *protocol; - /**<"protocol-name" to handle mount */ - - const struct lws_protocol_vhost_options *cgienv; - /**< optional linked-list of cgi options. These are created - * as environment variables for the cgi process - */ - const struct lws_protocol_vhost_options *extra_mimetypes; - /**< optional linked-list of mimetype mappings */ - const struct lws_protocol_vhost_options *interpret; - /**< optional linked-list of files to be interpreted */ - - int cgi_timeout; - /**< seconds cgi is allowed to live, if cgi://mount type */ - int cache_max_age; - /**< max-age for reuse of client cache of files, seconds */ - unsigned int auth_mask; - /**< bits set here must be set for authorized client session */ - - unsigned int cache_reusable:1; /**< set if client cache may reuse this */ - unsigned int cache_revalidate:1; /**< set if client cache should revalidate on use */ - unsigned int cache_intermediaries:1; /**< set if intermediaries are allowed to cache */ - - unsigned char origin_protocol; /**< one of enum lws_mount_protocols */ - unsigned char mountpoint_len; /**< length of mountpoint string */ - - const char *basic_auth_login_file; - /**revents will be zeroed now. - * - * If the socket is foreign to lws, it leaves revents alone. So you can - * see if you should service yourself by checking the pollfd revents - * after letting lws try to service it. - * - * You should also call this with pollfd = NULL to just allow the - * once-per-second global timeout checks; if less than a second since the last - * check it returns immediately then. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_fd(struct lws_context *context, struct lws_pollfd *pollfd); - -/** - * lws_service_fd_tsi() - Service polled socket in specific service thread - * \param context: Websocket context - * \param pollfd: The pollfd entry describing the socket fd and which events - * happened. - * \param tsi: thread service index - * - * Same as lws_service_fd() but used with multiple service threads - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd, - int tsi); - -/** - * lws_service_adjust_timeout() - Check for any connection needing forced service - * \param context: Websocket context - * \param timeout_ms: The original poll timeout value. You can just set this - * to 1 if you don't really have a poll timeout. - * \param tsi: thread service index - * - * Under some conditions connections may need service even though there is no - * pending network action on them, this is "forced service". For default - * poll() and libuv / libev, the library takes care of calling this and - * dealing with it for you. But for external poll() integration, you need - * access to the apis. - * - * If anybody needs "forced service", returned timeout is zero. In that case, - * you can call lws_service_tsi() with a timeout of -1 to only service - * guys who need forced service. - */ -LWS_VISIBLE LWS_EXTERN int -lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi); - -/* Backwards compatibility */ -#define lws_plat_service_tsi lws_service_tsi - -LWS_VISIBLE LWS_EXTERN int -lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); - -///@} - -/*! \defgroup http HTTP - - Modules related to handling HTTP -*/ -//@{ - -/*! \defgroup httpft HTTP File transfer - * \ingroup http - - APIs for sending local files in response to HTTP requests -*/ -//@{ - -/** - * lws_get_mimetype() - Determine mimetype to use from filename - * - * \param file: filename - * \param m: NULL, or mount context - * - * This uses a canned list of known filetypes first, if no match and m is - * non-NULL, then tries a list of per-mount file suffix to mimtype mappings. - * - * Returns either NULL or a pointer to the mimetype matching the file. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_mimetype(const char *file, const struct lws_http_mount *m); - -/** - * lws_serve_http_file() - Send a file back to the client using http - * \param wsi: Websocket instance (available from user callback) - * \param file: The file to issue over http - * \param content_type: The http content type, eg, text/html - * \param other_headers: NULL or pointer to header string - * \param other_headers_len: length of the other headers if non-NULL - * - * This function is intended to be called from the callback in response - * to http requests from the client. It allows the callback to issue - * local files down the http link in a single step. - * - * Returning <0 indicates error and the wsi should be closed. Returning - * >0 indicates the file was completely sent and - * lws_http_transaction_completed() called on the wsi (and close if != 0) - * ==0 indicates the file transfer is started and needs more service later, - * the wsi should be left alone. - */ -LWS_VISIBLE LWS_EXTERN int -lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, - const char *other_headers, int other_headers_len); - -LWS_VISIBLE LWS_EXTERN int -lws_serve_http_file_fragment(struct lws *wsi); -//@} - - -enum http_status { - HTTP_STATUS_CONTINUE = 100, - - HTTP_STATUS_OK = 200, - HTTP_STATUS_NO_CONTENT = 204, - HTTP_STATUS_PARTIAL_CONTENT = 206, - - HTTP_STATUS_MOVED_PERMANENTLY = 301, - HTTP_STATUS_FOUND = 302, - HTTP_STATUS_SEE_OTHER = 303, - HTTP_STATUS_NOT_MODIFIED = 304, - - HTTP_STATUS_BAD_REQUEST = 400, - HTTP_STATUS_UNAUTHORIZED, - HTTP_STATUS_PAYMENT_REQUIRED, - HTTP_STATUS_FORBIDDEN, - HTTP_STATUS_NOT_FOUND, - HTTP_STATUS_METHOD_NOT_ALLOWED, - HTTP_STATUS_NOT_ACCEPTABLE, - HTTP_STATUS_PROXY_AUTH_REQUIRED, - HTTP_STATUS_REQUEST_TIMEOUT, - HTTP_STATUS_CONFLICT, - HTTP_STATUS_GONE, - HTTP_STATUS_LENGTH_REQUIRED, - HTTP_STATUS_PRECONDITION_FAILED, - HTTP_STATUS_REQ_ENTITY_TOO_LARGE, - HTTP_STATUS_REQ_URI_TOO_LONG, - HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, - HTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, - HTTP_STATUS_EXPECTATION_FAILED, - - HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, - HTTP_STATUS_NOT_IMPLEMENTED, - HTTP_STATUS_BAD_GATEWAY, - HTTP_STATUS_SERVICE_UNAVAILABLE, - HTTP_STATUS_GATEWAY_TIMEOUT, - HTTP_STATUS_HTTP_VERSION_NOT_SUPPORTED, -}; -/*! \defgroup html-chunked-substitution HTML Chunked Substitution - * \ingroup http - * - * ##HTML chunked Substitution - * - * APIs for receiving chunks of text, replacing a set of variable names via - * a callback, and then prepending and appending HTML chunked encoding - * headers. - */ -//@{ - -struct lws_process_html_args { - char *p; /**< pointer to the buffer containing the data */ - int len; /**< length of the original data at p */ - int max_len; /**< maximum length we can grow the data to */ - int final; /**< set if this is the last chunk of the file */ -}; - -typedef const char *(*lws_process_html_state_cb)(void *data, int index); - -struct lws_process_html_state { - char *start; /**< pointer to start of match */ - char swallow[16]; /**< matched character buffer */ - int pos; /**< position in match */ - void *data; /**< opaque pointer */ - const char * const *vars; /**< list of variable names */ - int count_vars; /**< count of variable names */ - - lws_process_html_state_cb replace; /**< called on match to perform substitution */ -}; - -/*! lws_chunked_html_process() - generic chunked substitution - * \param args: buffer to process using chunked encoding - * \param s: current processing state - */ -LWS_VISIBLE LWS_EXTERN int -lws_chunked_html_process(struct lws_process_html_args *args, - struct lws_process_html_state *s); -//@} - -/** \defgroup HTTP-headers-read HTTP headers: read - * \ingroup http - * - * ##HTTP header releated functions - * - * In lws the client http headers are temporarily stored in a pool, only for the - * duration of the http part of the handshake. It's because in most cases, - * the header content is ignored for the whole rest of the connection lifetime - * and would then just be taking up space needlessly. - * - * During LWS_CALLBACK_HTTP when the URI path is delivered is the last time - * the http headers are still allocated, you can use these apis then to - * look at and copy out interesting header content (cookies, etc) - * - * Notice that the header total length reported does not include a terminating - * '\0', however you must allocate for it when using the _copy apis. So the - * length reported for a header containing "123" is 3, but you must provide - * a buffer of length 4 so that "123\0" may be copied into it, or the copy - * will fail with a nonzero return code. - * - * In the special case of URL arguments, like ?x=1&y=2, the arguments are - * stored in a token named for the method, eg, WSI_TOKEN_GET_URI if it - * was a GET or WSI_TOKEN_POST_URI if POST. You can check the total - * length to confirm the method. - * - * For URL arguments, each argument is stored urldecoded in a "fragment", so - * you can use the fragment-aware api lws_hdr_copy_fragment() to access each - * argument in turn: the fragments contain urldecoded strings like x=1 or y=2. - * - * As a convenience, lws has an api that will find the fragment with a - * given name= part, lws_get_urlarg_by_name(). - */ -///@{ - -/** struct lws_tokens - * you need these to look at headers that have been parsed if using the - * LWS_CALLBACK_FILTER_CONNECTION callback. If a header from the enum - * list below is absent, .token = NULL and token_len = 0. Otherwise .token - * points to .token_len chars containing that header content. - */ -struct lws_tokens { - char *token; /**< pointer to start of the token */ - int token_len; /**< length of the token's value */ -}; - -/* enum lws_token_indexes - * these have to be kept in sync with lextable.h / minilex.c - * - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_token_indexes { - WSI_TOKEN_GET_URI = 0, - WSI_TOKEN_POST_URI = 1, - WSI_TOKEN_OPTIONS_URI = 2, - WSI_TOKEN_HOST = 3, - WSI_TOKEN_CONNECTION = 4, - WSI_TOKEN_UPGRADE = 5, - WSI_TOKEN_ORIGIN = 6, - WSI_TOKEN_DRAFT = 7, - WSI_TOKEN_CHALLENGE = 8, - WSI_TOKEN_EXTENSIONS = 9, - WSI_TOKEN_KEY1 = 10, - WSI_TOKEN_KEY2 = 11, - WSI_TOKEN_PROTOCOL = 12, - WSI_TOKEN_ACCEPT = 13, - WSI_TOKEN_NONCE = 14, - WSI_TOKEN_HTTP = 15, - WSI_TOKEN_HTTP2_SETTINGS = 16, - WSI_TOKEN_HTTP_ACCEPT = 17, - WSI_TOKEN_HTTP_AC_REQUEST_HEADERS = 18, - WSI_TOKEN_HTTP_IF_MODIFIED_SINCE = 19, - WSI_TOKEN_HTTP_IF_NONE_MATCH = 20, - WSI_TOKEN_HTTP_ACCEPT_ENCODING = 21, - WSI_TOKEN_HTTP_ACCEPT_LANGUAGE = 22, - WSI_TOKEN_HTTP_PRAGMA = 23, - WSI_TOKEN_HTTP_CACHE_CONTROL = 24, - WSI_TOKEN_HTTP_AUTHORIZATION = 25, - WSI_TOKEN_HTTP_COOKIE = 26, - WSI_TOKEN_HTTP_CONTENT_LENGTH = 27, - WSI_TOKEN_HTTP_CONTENT_TYPE = 28, - WSI_TOKEN_HTTP_DATE = 29, - WSI_TOKEN_HTTP_RANGE = 30, - WSI_TOKEN_HTTP_REFERER = 31, - WSI_TOKEN_KEY = 32, - WSI_TOKEN_VERSION = 33, - WSI_TOKEN_SWORIGIN = 34, - - WSI_TOKEN_HTTP_COLON_AUTHORITY = 35, - WSI_TOKEN_HTTP_COLON_METHOD = 36, - WSI_TOKEN_HTTP_COLON_PATH = 37, - WSI_TOKEN_HTTP_COLON_SCHEME = 38, - WSI_TOKEN_HTTP_COLON_STATUS = 39, - - WSI_TOKEN_HTTP_ACCEPT_CHARSET = 40, - WSI_TOKEN_HTTP_ACCEPT_RANGES = 41, - WSI_TOKEN_HTTP_ACCESS_CONTROL_ALLOW_ORIGIN = 42, - WSI_TOKEN_HTTP_AGE = 43, - WSI_TOKEN_HTTP_ALLOW = 44, - WSI_TOKEN_HTTP_CONTENT_DISPOSITION = 45, - WSI_TOKEN_HTTP_CONTENT_ENCODING = 46, - WSI_TOKEN_HTTP_CONTENT_LANGUAGE = 47, - WSI_TOKEN_HTTP_CONTENT_LOCATION = 48, - WSI_TOKEN_HTTP_CONTENT_RANGE = 49, - WSI_TOKEN_HTTP_ETAG = 50, - WSI_TOKEN_HTTP_EXPECT = 51, - WSI_TOKEN_HTTP_EXPIRES = 52, - WSI_TOKEN_HTTP_FROM = 53, - WSI_TOKEN_HTTP_IF_MATCH = 54, - WSI_TOKEN_HTTP_IF_RANGE = 55, - WSI_TOKEN_HTTP_IF_UNMODIFIED_SINCE = 56, - WSI_TOKEN_HTTP_LAST_MODIFIED = 57, - WSI_TOKEN_HTTP_LINK = 58, - WSI_TOKEN_HTTP_LOCATION = 59, - WSI_TOKEN_HTTP_MAX_FORWARDS = 60, - WSI_TOKEN_HTTP_PROXY_AUTHENTICATE = 61, - WSI_TOKEN_HTTP_PROXY_AUTHORIZATION = 62, - WSI_TOKEN_HTTP_REFRESH = 63, - WSI_TOKEN_HTTP_RETRY_AFTER = 64, - WSI_TOKEN_HTTP_SERVER = 65, - WSI_TOKEN_HTTP_SET_COOKIE = 66, - WSI_TOKEN_HTTP_STRICT_TRANSPORT_SECURITY = 67, - WSI_TOKEN_HTTP_TRANSFER_ENCODING = 68, - WSI_TOKEN_HTTP_USER_AGENT = 69, - WSI_TOKEN_HTTP_VARY = 70, - WSI_TOKEN_HTTP_VIA = 71, - WSI_TOKEN_HTTP_WWW_AUTHENTICATE = 72, - - WSI_TOKEN_PATCH_URI = 73, - WSI_TOKEN_PUT_URI = 74, - WSI_TOKEN_DELETE_URI = 75, - - WSI_TOKEN_HTTP_URI_ARGS = 76, - WSI_TOKEN_PROXY = 77, - WSI_TOKEN_HTTP_X_REAL_IP = 78, - WSI_TOKEN_HTTP1_0 = 79, - WSI_TOKEN_X_FORWARDED_FOR = 80, - WSI_TOKEN_CONNECT = 81, - WSI_TOKEN_HEAD_URI = 82, - WSI_TOKEN_TE = 83, - /****** add new things just above ---^ ******/ - - /* use token storage to stash these internally, not for - * user use */ - - _WSI_TOKEN_CLIENT_SENT_PROTOCOLS, - _WSI_TOKEN_CLIENT_PEER_ADDRESS, - _WSI_TOKEN_CLIENT_URI, - _WSI_TOKEN_CLIENT_HOST, - _WSI_TOKEN_CLIENT_ORIGIN, - _WSI_TOKEN_CLIENT_METHOD, - _WSI_TOKEN_CLIENT_IFACE, - - /* always last real token index*/ - WSI_TOKEN_COUNT, - - /* parser state additions, no storage associated */ - WSI_TOKEN_NAME_PART, - WSI_TOKEN_SKIPPING, - WSI_TOKEN_SKIPPING_SAW_CR, - WSI_PARSING_COMPLETE, - WSI_INIT_TOKEN_MUXURL, -}; - -struct lws_token_limits { - unsigned short token_limit[WSI_TOKEN_COUNT]; /**< max chars for this token */ -}; - -/** - * lws_token_to_string() - returns a textual representation of a hdr token index - * - * \param token: token index - */ -LWS_VISIBLE LWS_EXTERN const unsigned char * -lws_token_to_string(enum lws_token_indexes token); - -/** - * lws_hdr_total_length: report length of all fragments of a header totalled up - * The returned length does not include the space for a - * terminating '\0' - * - * \param wsi: websocket connection - * \param h: which header index we are interested in - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_hdr_total_length(struct lws *wsi, enum lws_token_indexes h); - -/** - * lws_hdr_fragment_length: report length of a single fragment of a header - * The returned length does not include the space for a - * terminating '\0' - * - * \param wsi: websocket connection - * \param h: which header index we are interested in - * \param frag_idx: which fragment of h we want to get the length of - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_hdr_fragment_length(struct lws *wsi, enum lws_token_indexes h, int frag_idx); - -/** - * lws_hdr_copy() - copy a single fragment of the given header to a buffer - * The buffer length len must include space for an additional - * terminating '\0', or it will fail returning -1. - * - * \param wsi: websocket connection - * \param dest: destination buffer - * \param len: length of destination buffer - * \param h: which header index we are interested in - * - * copies the whole, aggregated header, even if it was delivered in - * several actual headers piece by piece - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_copy(struct lws *wsi, char *dest, int len, enum lws_token_indexes h); - -/** - * lws_hdr_copy_fragment() - copy a single fragment of the given header to a buffer - * The buffer length len must include space for an additional - * terminating '\0', or it will fail returning -1. - * If the requested fragment index is not present, it fails - * returning -1. - * - * \param wsi: websocket connection - * \param dest: destination buffer - * \param len: length of destination buffer - * \param h: which header index we are interested in - * \param frag_idx: which fragment of h we want to copy - * - * Normally this is only useful - * to parse URI arguments like ?x=1&y=2, token index WSI_TOKEN_HTTP_URI_ARGS - * fragment 0 will contain "x=1" and fragment 1 "y=2" - */ -LWS_VISIBLE LWS_EXTERN int -lws_hdr_copy_fragment(struct lws *wsi, char *dest, int len, - enum lws_token_indexes h, int frag_idx); - -/** - * lws_get_urlarg_by_name() - return pointer to arg value if present - * \param wsi: the connection to check - * \param name: the arg name, like "token=" - * \param buf: the buffer to receive the urlarg (including the name= part) - * \param len: the length of the buffer to receive the urlarg - * - * Returns NULL if not found or a pointer inside buf to just after the - * name= part. - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len); -///@} - -/*! \defgroup HTTP-headers-create HTTP headers: create - * - * ## HTTP headers: Create - * - * These apis allow you to create HTTP response headers in a way compatible with - * both HTTP/1.x and HTTP/2. - * - * They each append to a buffer taking care about the buffer end, which is - * passed in as a pointer. When data is written to the buffer, the current - * position p is updated accordingly. - * - * All of these apis are LWS_WARN_UNUSED_RESULT as they can run out of space - * and fail with nonzero return. - */ -///@{ - -#define LWSAHH_CODE_MASK ((1 << 16) - 1) -#define LWSAHH_FLAG_NO_SERVER_NAME (1 << 30) - -/** - * lws_add_http_header_status() - add the HTTP response status code - * - * \param wsi: the connection to check - * \param code: an HTTP code like 200, 404 etc (see enum http_status) - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Adds the initial response code, so should be called first. - * - * Code may additionally take OR'd flags: - * - * LWSAHH_FLAG_NO_SERVER_NAME: don't apply server name header this time - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_status(struct lws *wsi, - unsigned int code, unsigned char **p, - unsigned char *end); -/** - * lws_add_http_header_by_name() - append named header and value - * - * \param wsi: the connection to check - * \param name: the hdr name, like "my-header" - * \param value: the value after the = for this header - * \param length: the length of the value - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends name: value to the headers - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name, - const unsigned char *value, int length, - unsigned char **p, unsigned char *end); -/** - * lws_add_http_header_by_token() - append given header and value - * - * \param wsi: the connection to check - * \param token: the token index for the hdr - * \param value: the value after the = for this header - * \param length: the length of the value - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends name=value to the headers, but is able to take advantage of better - * HTTP/2 coding mechanisms where possible. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token, - const unsigned char *value, int length, - unsigned char **p, unsigned char *end); -/** - * lws_add_http_header_content_length() - append content-length helper - * - * \param wsi: the connection to check - * \param content_length: the content length to use - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Appends content-length: content_length to the headers - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_add_http_header_content_length(struct lws *wsi, - lws_filepos_t content_length, - unsigned char **p, unsigned char *end); -/** - * lws_finalize_http_header() - terminate header block - * - * \param wsi: the connection to check - * \param p: pointer to current position in buffer pointer - * \param end: pointer to end of buffer - * - * Indicates no more headers will be added - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_finalize_http_header(struct lws *wsi, unsigned char **p, - unsigned char *end); -///@} - -/** \defgroup form-parsing Form Parsing - * \ingroup http - * ##POSTed form parsing functions - * - * These lws_spa (stateful post arguments) apis let you parse and urldecode - * POSTed form arguments, both using simple urlencoded and multipart transfer - * encoding. - * - * It's capable of handling file uploads as well a named input parsing, - * and the apis are the same for both form upload styles. - * - * You feed it a list of parameter names and it creates pointers to the - * urldecoded arguments: file upload parameters pass the file data in chunks to - * a user-supplied callback as they come. - * - * Since it's stateful, it handles the incoming data needing more than one - * POST_BODY callback and has no limit on uploaded file size. - */ -///@{ - -/** enum lws_spa_fileupload_states */ -enum lws_spa_fileupload_states { - LWS_UFS_CONTENT, - /**< a chunk of file content has arrived */ - LWS_UFS_FINAL_CONTENT, - /**< the last chunk (possibly zero length) of file content has arrived */ - LWS_UFS_OPEN - /**< a new file is starting to arrive */ -}; - -/** - * lws_spa_fileupload_cb() - callback to receive file upload data - * - * \param data: opt_data pointer set in lws_spa_create - * \param name: name of the form field being uploaded - * \param filename: original filename from client - * \param buf: start of data to receive - * \param len: length of data to receive - * \param state: information about how this call relates to file - * - * Notice name and filename shouldn't be trusted, as they are passed from - * HTTP provided by the client. - */ -typedef int (*lws_spa_fileupload_cb)(void *data, const char *name, - const char *filename, char *buf, int len, - enum lws_spa_fileupload_states state); - -/** struct lws_spa - opaque urldecode parser capable of handling multipart - * and file uploads */ -struct lws_spa; - -/** - * lws_spa_create() - create urldecode parser - * - * \param wsi: lws connection (used to find Content Type) - * \param param_names: array of form parameter names, like "username" - * \param count_params: count of param_names - * \param max_storage: total amount of form parameter values we can store - * \param opt_cb: NULL, or callback to receive file upload data. - * \param opt_data: NULL, or user pointer provided to opt_cb. - * - * Creates a urldecode parser and initializes it. - * - * opt_cb can be NULL if you just want normal name=value parsing, however - * if one or more entries in your form are bulk data (file transfer), you - * can provide this callback and filter on the name callback parameter to - * treat that urldecoded data separately. The callback should return -1 - * in case of fatal error, and 0 if OK. - */ -LWS_VISIBLE LWS_EXTERN struct lws_spa * -lws_spa_create(struct lws *wsi, const char * const *param_names, - int count_params, int max_storage, lws_spa_fileupload_cb opt_cb, - void *opt_data); - -/** - * lws_spa_process() - parses a chunk of input data - * - * \param spa: the parser object previously created - * \param in: incoming, urlencoded data - * \param len: count of bytes valid at \param in - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_process(struct lws_spa *spa, const char *in, int len); - -/** - * lws_spa_finalize() - indicate incoming data completed - * - * \param spa: the parser object previously created - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_finalize(struct lws_spa *spa); - -/** - * lws_spa_get_length() - return length of parameter value - * - * \param spa: the parser object previously created - * \param n: parameter ordinal to return length of value for - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_get_length(struct lws_spa *spa, int n); - -/** - * lws_spa_get_string() - return pointer to parameter value - * \param spa: the parser object previously created - * \param n: parameter ordinal to return pointer to value for - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_spa_get_string(struct lws_spa *spa, int n); - -/** - * lws_spa_destroy() - destroy parser object - * - * \param spa: the parser object previously created - */ -LWS_VISIBLE LWS_EXTERN int -lws_spa_destroy(struct lws_spa *spa); -///@} - -/*! \defgroup urlendec Urlencode and Urldecode - * \ingroup http - * - * ##HTML chunked Substitution - * - * APIs for receiving chunks of text, replacing a set of variable names via - * a callback, and then prepending and appending HTML chunked encoding - * headers. - */ -//@{ - -/** - * lws_urlencode() - like strncpy but with urlencoding - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * - * Because urlencoding expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_urlencode(char *escaped, const char *string, int len); - -/* - * URLDECODE 1 / 2 - * - * This simple urldecode only operates until the first '\0' and requires the - * data to exist all at once - */ -/** - * lws_urldecode() - like strncpy but with urldecoding - * - * \param string: output buffer - * \param escaped: input buffer ('\0' terminated) - * \param len: output buffer max length - * - * This is only useful for '\0' terminated strings - * - * Since urldecoding only shrinks the output string, it is possible to - * do it in-place, ie, string == escaped - * - * Returns 0 if completed OK or nonzero for urldecode violation (non-hex chars - * where hex required, etc) - */ -LWS_VISIBLE LWS_EXTERN int -lws_urldecode(char *string, const char *escaped, int len); -///@} -/** - * lws_return_http_status() - Return simple http status - * \param wsi: Websocket instance (available from user callback) - * \param code: Status index, eg, 404 - * \param html_body: User-readable HTML description < 1KB, or NULL - * - * Helper to report HTTP errors back to the client cleanly and - * consistently - */ -LWS_VISIBLE LWS_EXTERN int -lws_return_http_status(struct lws *wsi, unsigned int code, - const char *html_body); - -/** - * lws_http_redirect() - write http redirect into buffer - * - * \param wsi: websocket connection - * \param code: HTTP response code (eg, 301) - * \param loc: where to redirect to - * \param len: length of loc - * \param p: pointer current position in buffer (updated as we write) - * \param end: pointer to end of buffer - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_redirect(struct lws *wsi, int code, const unsigned char *loc, int len, - unsigned char **p, unsigned char *end); - -/** - * lws_http_transaction_completed() - wait for new http transaction or close - * \param wsi: websocket connection - * - * Returns 1 if the HTTP connection must close now - * Returns 0 and resets connection to wait for new HTTP header / - * transaction if possible - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_http_transaction_completed(struct lws *wsi); -///@} - -/*! \defgroup pur Sanitize / purify SQL and JSON helpers - * - * ##Sanitize / purify SQL and JSON helpers - * - * APIs for escaping untrusted JSON and SQL safely before use - */ -//@{ - -/** - * lws_sql_purify() - like strncpy but with escaping for sql quotes - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * - * Because escaping expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_sql_purify(char *escaped, const char *string, int len); - -/** - * lws_json_purify() - like strncpy but with escaping for json chars - * - * \param escaped: output buffer - * \param string: input buffer ('/0' terminated) - * \param len: output buffer max length - * - * Because escaping expands the output string, it's not - * possible to do it in-place, ie, with escaped == string - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_json_purify(char *escaped, const char *string, int len); -///@} - -/*! \defgroup ev libev helpers - * - * ##libev helpers - * - * APIs specific to libev event loop itegration - */ -///@{ - -#ifdef LWS_WITH_LIBEV -typedef void (lws_ev_signal_cb_t)(EV_P_ struct ev_signal *w, int revents); - -LWS_VISIBLE LWS_EXTERN int -lws_ev_sigint_cfg(struct lws_context *context, int use_ev_sigint, - lws_ev_signal_cb_t *cb); - -LWS_VISIBLE LWS_EXTERN int -lws_ev_initloop(struct lws_context *context, struct ev_loop *loop, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_ev_sigint_cb(struct ev_loop *loop, struct ev_signal *watcher, int revents); -#endif /* LWS_WITH_LIBEV */ - -///@} - -/*! \defgroup uv libuv helpers - * - * ##libuv helpers - * - * APIs specific to libuv event loop itegration - */ -///@{ -#ifdef LWS_WITH_LIBUV -LWS_VISIBLE LWS_EXTERN int -lws_uv_sigint_cfg(struct lws_context *context, int use_uv_sigint, - uv_signal_cb cb); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_run(const struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_stop(struct lws_context *context); - -LWS_VISIBLE LWS_EXTERN void -lws_libuv_stop_without_kill(const struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN int -lws_uv_initloop(struct lws_context *context, uv_loop_t *loop, int tsi); - -LWS_VISIBLE LWS_EXTERN uv_loop_t * -lws_uv_getloop(struct lws_context *context, int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_uv_sigint_cb(uv_signal_t *watcher, int signum); - -LWS_VISIBLE LWS_EXTERN void -lws_close_all_handles_in_loop(uv_loop_t *loop); -#endif /* LWS_WITH_LIBUV */ -///@} - -/*! \defgroup event libevent helpers - * - * ##libevent helpers - * - * APIs specific to libevent event loop itegration - */ -///@{ - -#ifdef LWS_WITH_LIBEVENT -typedef void (lws_event_signal_cb_t) (evutil_socket_t sock_fd, short revents, - void *ctx); - -LWS_VISIBLE LWS_EXTERN int -lws_event_sigint_cfg(struct lws_context *context, int use_event_sigint, - lws_event_signal_cb_t cb); - -LWS_VISIBLE LWS_EXTERN int -lws_event_initloop(struct lws_context *context, struct event_base *loop, - int tsi); - -LWS_VISIBLE LWS_EXTERN void -lws_event_sigint_cb(evutil_socket_t sock_fd, short revents, - void *ctx); -#endif /* LWS_WITH_LIBEVENT */ - -///@} - -/*! \defgroup timeout Connection timeouts - - APIs related to setting connection timeouts -*/ -//@{ - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum pending_timeout { - NO_PENDING_TIMEOUT = 0, - PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE = 1, - PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE = 2, - PENDING_TIMEOUT_ESTABLISH_WITH_SERVER = 3, - PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE = 4, - PENDING_TIMEOUT_AWAITING_PING = 5, - PENDING_TIMEOUT_CLOSE_ACK = 6, - PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE = 7, - PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE = 8, - PENDING_TIMEOUT_SSL_ACCEPT = 9, - PENDING_TIMEOUT_HTTP_CONTENT = 10, - PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND = 11, - PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE = 12, - PENDING_TIMEOUT_SHUTDOWN_FLUSH = 13, - PENDING_TIMEOUT_CGI = 14, - PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE = 15, - PENDING_TIMEOUT_WS_PONG_CHECK_SEND_PING = 16, - PENDING_TIMEOUT_WS_PONG_CHECK_GET_PONG = 17, - PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD = 18, - PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY = 19, - PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY = 20, - PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY = 21, - PENDING_TIMEOUT_KILLED_BY_SSL_INFO = 22, - PENDING_TIMEOUT_KILLED_BY_PARENT = 23, - PENDING_TIMEOUT_CLOSE_SEND = 24, - PENDING_TIMEOUT_HOLDING_AH = 25, - - /****** add new things just above ---^ ******/ - - PENDING_TIMEOUT_USER_REASON_BASE = 1000 -}; - -#define LWS_TO_KILL_ASYNC -1 -/**< If LWS_TO_KILL_ASYNC is given as the timeout sec in a lws_set_timeout() - * call, then the connection is marked to be killed at the next timeout - * check. This is how you should force-close the wsi being serviced if - * you are doing it outside the callback (where you should close by nonzero - * return). - */ -#define LWS_TO_KILL_SYNC -2 -/**< If LWS_TO_KILL_SYNC is given as the timeout sec in a lws_set_timeout() - * call, then the connection is closed before returning (which may delete - * the wsi). This should only be used where the wsi being closed is not the - * wsi currently being serviced. - */ -/** - * lws_set_timeout() - marks the wsi as subject to a timeout - * - * You will not need this unless you are doing something special - * - * \param wsi: Websocket connection instance - * \param reason: timeout reason - * \param secs: how many seconds. You may set to LWS_TO_KILL_ASYNC to - * force the connection to timeout at the next opportunity, or - * LWS_TO_KILL_SYNC to close it synchronously if you know the - * wsi is not the one currently being serviced. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_timeout(struct lws *wsi, enum pending_timeout reason, int secs); -///@} - -/*! \defgroup sending-data Sending data - - APIs related to writing data on a connection -*/ -//@{ -#if !defined(LWS_SIZEOFPTR) -#define LWS_SIZEOFPTR (sizeof (void *)) -#endif - -#if defined(__x86_64__) -#define _LWS_PAD_SIZE 16 /* Intel recommended for best performance */ -#else -#define _LWS_PAD_SIZE LWS_SIZEOFPTR /* Size of a pointer on the target arch */ -#endif -#define _LWS_PAD(n) (((n) % _LWS_PAD_SIZE) ? \ - ((n) + (_LWS_PAD_SIZE - ((n) % _LWS_PAD_SIZE))) : (n)) -/* last 2 is for lws-meta */ -#define LWS_PRE _LWS_PAD(4 + 10 + 2) -/* used prior to 1.7 and retained for backward compatibility */ -#define LWS_SEND_BUFFER_PRE_PADDING LWS_PRE -#define LWS_SEND_BUFFER_POST_PADDING 0 - -/* - * NOTE: These public enums are part of the abi. If you want to add one, - * add it at where specified so existing users are unaffected. - */ -enum lws_write_protocol { - LWS_WRITE_TEXT = 0, - /**< Send a ws TEXT message,the pointer must have LWS_PRE valid - * memory behind it. The receiver expects only valid utf-8 in the - * payload */ - LWS_WRITE_BINARY = 1, - /**< Send a ws BINARY message, the pointer must have LWS_PRE valid - * memory behind it. Any sequence of bytes is valid */ - LWS_WRITE_CONTINUATION = 2, - /**< Continue a previous ws message, the pointer must have LWS_PRE valid - * memory behind it */ - LWS_WRITE_HTTP = 3, - /**< Send HTTP content */ - - /* LWS_WRITE_CLOSE is handled by lws_close_reason() */ - LWS_WRITE_PING = 5, - LWS_WRITE_PONG = 6, - - /* Same as write_http but we know this write ends the transaction */ - LWS_WRITE_HTTP_FINAL = 7, - - /* HTTP2 */ - - LWS_WRITE_HTTP_HEADERS = 8, - /**< Send http headers (http2 encodes this payload and LWS_WRITE_HTTP - * payload differently, http 1.x links also handle this correctly. so - * to be compatible with both in the future,header response part should - * be sent using this regardless of http version expected) - */ - LWS_WRITE_HTTP_HEADERS_CONTINUATION = 9, - /**< Continuation of http/2 headers - */ - - /****** add new things just above ---^ ******/ - - /* flags */ - - LWS_WRITE_NO_FIN = 0x40, - /**< This part of the message is not the end of the message */ - - LWS_WRITE_H2_STREAM_END = 0x80, - /**< Flag indicates this packet should go out with STREAM_END if h2 - * STREAM_END is allowed on DATA or HEADERS. - */ - - LWS_WRITE_CLIENT_IGNORE_XOR_MASK = 0x80 - /**< client packet payload goes out on wire unmunged - * only useful for security tests since normal servers cannot - * decode the content if used */ -}; - -/* used with LWS_CALLBACK_CHILD_WRITE_VIA_PARENT */ - -struct lws_write_passthru { - struct lws *wsi; - unsigned char *buf; - size_t len; - enum lws_write_protocol wp; -}; - - -/** - * lws_write() - Apply protocol then write data to client - * \param wsi: Websocket instance (available from user callback) - * \param buf: The data to send. For data being sent on a websocket - * connection (ie, not default http), this buffer MUST have - * LWS_PRE bytes valid BEFORE the pointer. - * This is so the protocol header data can be added in-situ. - * \param len: Count of the data bytes in the payload starting from buf - * \param protocol: Use LWS_WRITE_HTTP to reply to an http connection, and one - * of LWS_WRITE_BINARY or LWS_WRITE_TEXT to send appropriate - * data on a websockets connection. Remember to allow the extra - * bytes before and after buf if LWS_WRITE_BINARY or LWS_WRITE_TEXT - * are used. - * - * This function provides the way to issue data back to the client - * for both http and websocket protocols. - * - * IMPORTANT NOTICE! - * - * When sending with websocket protocol - * - * LWS_WRITE_TEXT, - * LWS_WRITE_BINARY, - * LWS_WRITE_CONTINUATION, - * LWS_WRITE_PING, - * LWS_WRITE_PONG - * - * the send buffer has to have LWS_PRE bytes valid BEFORE - * the buffer pointer you pass to lws_write(). - * - * This allows us to add protocol info before and after the data, and send as - * one packet on the network without payload copying, for maximum efficiency. - * - * So for example you need this kind of code to use lws_write with a - * 128-byte payload - * - * char buf[LWS_PRE + 128]; - * - * // fill your part of the buffer... for example here it's all zeros - * memset(&buf[LWS_PRE], 0, 128); - * - * lws_write(wsi, &buf[LWS_PRE], 128, LWS_WRITE_TEXT); - * - * When sending HTTP, with - * - * LWS_WRITE_HTTP, - * LWS_WRITE_HTTP_HEADERS - * LWS_WRITE_HTTP_FINAL - * - * there is no protocol data prepended, and don't need to take care about the - * LWS_PRE bytes valid before the buffer pointer. - * - * LWS_PRE is at least the frame nonce + 2 header + 8 length - * LWS_SEND_BUFFER_POST_PADDING is deprecated, it's now 0 and can be left off. - * The example apps no longer use it. - * - * Pad LWS_PRE to the CPU word size, so that word references - * to the address immediately after the padding won't cause an unaligned access - * error. Sometimes for performance reasons the recommended padding is even - * larger than sizeof(void *). - * - * In the case of sending using websocket protocol, be sure to allocate - * valid storage before and after buf as explained above. This scheme - * allows maximum efficiency of sending data and protocol in a single - * packet while not burdening the user code with any protocol knowledge. - * - * Return may be -1 for a fatal error needing connection close, or the - * number of bytes sent. - * - * Truncated Writes - * ================ - * - * The OS may not accept everything you asked to write on the connection. - * - * Posix defines POLLOUT indication from poll() to show that the connection - * will accept more write data, but it doesn't specifiy how much. It may just - * accept one byte of whatever you wanted to send. - * - * LWS will buffer the remainder automatically, and send it out autonomously. - * - * During that time, WRITABLE callbacks will be suppressed. - * - * This is to handle corner cases where unexpectedly the OS refuses what we - * usually expect it to accept. You should try to send in chunks that are - * almost always accepted in order to avoid the inefficiency of the buffering. - */ -LWS_VISIBLE LWS_EXTERN int -lws_write(struct lws *wsi, unsigned char *buf, size_t len, - enum lws_write_protocol protocol); - -/* helper for case where buffer may be const */ -#define lws_write_http(wsi, buf, len) \ - lws_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) -///@} - -/** \defgroup callback-when-writeable Callback when writeable - * - * ##Callback When Writeable - * - * lws can only write data on a connection when it is able to accept more - * data without blocking. - * - * So a basic requirement is we should only use the lws_write() apis when the - * connection we want to write on says that he can accept more data. - * - * When lws cannot complete your send at the time, it will buffer the data - * and send it in the background, suppressing any further WRITEABLE callbacks - * on that connection until it completes. So it is important to write new - * things in a new writeable callback. - * - * These apis reflect the various ways we can indicate we would like to be - * called back when one or more connections is writeable. - */ -///@{ - -/** - * lws_callback_on_writable() - Request a callback when this socket - * becomes able to be written to without - * blocking - * - * \param wsi: Websocket connection instance to get callback for - * - * - Which: only this wsi - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable(struct lws *wsi); - -/** - * lws_callback_on_writable_all_protocol() - Request a callback for all - * connections using the given protocol when it - * becomes possible to write to each socket without - * blocking in turn. - * - * \param context: lws_context - * \param protocol: Protocol whose connections will get callbacks - * - * - Which: connections using this protocol on ANY VHOST - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol); - -/** - * lws_callback_on_writable_all_protocol_vhost() - Request a callback for - * all connections on same vhost using the given protocol - * when it becomes possible to write to each socket without - * blocking in turn. - * - * \param vhost: Only consider connections on this lws_vhost - * \param protocol: Protocol whose connections will get callbacks - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: when the individual connection becomes writeable - * - What: LWS_CALLBACK_*_WRITEABLE - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_on_writable_all_protocol_vhost(const struct lws_vhost *vhost, - const struct lws_protocols *protocol); - -/** - * lws_callback_all_protocol() - Callback all connections using - * the given protocol with the given reason - * - * \param context: lws_context - * \param protocol: Protocol whose connections will get callbacks - * \param reason: Callback reason index - * - * - Which: connections using this protocol on ALL VHOSTS - * - When: before returning - * - What: reason - * - * This isn't normally what you want... normally any update of connection- - * specific information can wait until a network-related callback like rx, - * writable, or close. - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_all_protocol(struct lws_context *context, - const struct lws_protocols *protocol, int reason); - -/** - * lws_callback_all_protocol_vhost() - Callback all connections using - * the given protocol with the given reason. This is - * deprecated since v2.4: use lws_callback_all_protocol_vhost_args - * - * \param vh: Vhost whose connections will get callbacks - * \param protocol: Which protocol to match. NULL means all. - * \param reason: Callback reason index - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_all_protocol_vhost(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason) -LWS_WARN_DEPRECATED; - -/** - * lws_callback_all_protocol_vhost_args() - Callback all connections using - * the given protocol with the given reason and args - * - * \param vh: Vhost whose connections will get callbacks - * \param protocol: Which protocol to match. NULL means all. - * \param reason: Callback reason index - * \param argp: Callback "in" parameter - * \param len: Callback "len" parameter - * - * - Which: connections using this protocol on GIVEN VHOST ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE int -lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, - const struct lws_protocols *protocol, int reason, - void *argp, size_t len); - -/** - * lws_callback_vhost_protocols() - Callback all protocols enabled on a vhost - * with the given reason - * - * \param wsi: wsi whose vhost will get callbacks - * \param reason: Callback reason index - * \param in: in argument to callback - * \param len: len argument to callback - * - * - Which: connections using this protocol on same VHOST as wsi ONLY - * - When: now - * - What: reason - */ -LWS_VISIBLE LWS_EXTERN int -lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len); - -LWS_VISIBLE LWS_EXTERN int -lws_callback_http_dummy(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len); - -/** - * lws_get_socket_fd() - returns the socket file descriptor - * - * You will not need this unless you are doing something special - * - * \param wsi: Websocket connection instance - */ -LWS_VISIBLE LWS_EXTERN int -lws_get_socket_fd(struct lws *wsi); - -/** - * lws_get_peer_write_allowance() - get the amount of data writeable to peer - * if known - * - * \param wsi: Websocket connection instance - * - * if the protocol does not have any guidance, returns -1. Currently only - * http2 connections get send window information from this API. But your code - * should use it so it can work properly with any protocol. - * - * If nonzero return is the amount of payload data the peer or intermediary has - * reported it has buffer space for. That has NO relationship with the amount - * of buffer space your OS can accept on this connection for a write action. - * - * This number represents the maximum you could send to the peer or intermediary - * on this connection right now without the protocol complaining. - * - * lws manages accounting for send window updates and payload writes - * automatically, so this number reflects the situation at the peer or - * intermediary dynamically. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_get_peer_write_allowance(struct lws *wsi); -///@} - -enum { - /* - * Flags for enable and disable rxflow with reason bitmap and with - * backwards-compatible single bool - */ - LWS_RXFLOW_REASON_USER_BOOL = (1 << 0), - LWS_RXFLOW_REASON_HTTP_RXBUFFER = (1 << 6), - LWS_RXFLOW_REASON_H2_PPS_PENDING = (1 << 7), - - LWS_RXFLOW_REASON_APPLIES = (1 << 14), - LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT = (1 << 13), - LWS_RXFLOW_REASON_APPLIES_ENABLE = LWS_RXFLOW_REASON_APPLIES | - LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT, - LWS_RXFLOW_REASON_APPLIES_DISABLE = LWS_RXFLOW_REASON_APPLIES, - LWS_RXFLOW_REASON_FLAG_PROCESS_NOW = (1 << 12), - -}; - -/** - * lws_rx_flow_control() - Enable and disable socket servicing for - * received packets. - * - * If the output side of a server process becomes choked, this allows flow - * control for the input side. - * - * \param wsi: Websocket connection instance to get callback for - * \param enable: 0 = disable read servicing for this connection, 1 = enable - * - * If you need more than one additive reason for rxflow control, you can give - * iLWS_RXFLOW_REASON_APPLIES_ENABLE or _DISABLE together with one or more of - * b5..b0 set to idicate which bits to enable or disable. If any bits are - * enabled, rx on the connection is suppressed. - * - * LWS_RXFLOW_REASON_FLAG_PROCESS_NOW flag may also be given to force any change - * in rxflowbstatus to benapplied immediately, this should be used when you are - * changing a wsi flow control state from outside a callback on that wsi. - */ -LWS_VISIBLE LWS_EXTERN int -lws_rx_flow_control(struct lws *wsi, int enable); - -/** - * lws_rx_flow_allow_all_protocol() - Allow all connections with this protocol to receive - * - * When the user server code realizes it can accept more input, it can - * call this to have the RX flow restriction removed from all connections using - * the given protocol. - * \param context: lws_context - * \param protocol: all connections using this protocol will be allowed to receive - */ -LWS_VISIBLE LWS_EXTERN void -lws_rx_flow_allow_all_protocol(const struct lws_context *context, - const struct lws_protocols *protocol); - -/** - * lws_remaining_packet_payload() - Bytes to come before "overall" - * rx packet is complete - * \param wsi: Websocket instance (available from user callback) - * - * This function is intended to be called from the callback if the - * user code is interested in "complete packets" from the client. - * libwebsockets just passes through payload as it comes and issues a buffer - * additionally when it hits a built-in limit. The LWS_CALLBACK_RECEIVE - * callback handler can use this API to find out if the buffer it has just - * been given is the last piece of a "complete packet" from the client -- - * when that is the case lws_remaining_packet_payload() will return - * 0. - * - * Many protocols won't care becuse their packets are always small. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_remaining_packet_payload(struct lws *wsi); - - -/** \defgroup sock-adopt Socket adoption helpers - * ##Socket adoption helpers - * - * When integrating with an external app with its own event loop, these can - * be used to accept connections from someone else's listening socket. - * - * When using lws own event loop, these are not needed. - */ -///@{ - -/** - * lws_adopt_socket() - adopt foreign socket as if listen socket accepted it - * for the default vhost of context. - * - * \param context: lws context - * \param accept_fd: fd of already-accepted socket to adopt - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd); -/** - * lws_adopt_socket_vhost() - adopt foreign socket as if listen socket accepted it - * for vhost - * - * \param vh: lws vhost - * \param accept_fd: fd of already-accepted socket to adopt - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd); - -typedef enum { - LWS_ADOPT_RAW_FILE_DESC = 0, /* convenience constant */ - LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */ - LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ - LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ - LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO - * if given must be only flag - * wsi put directly into ws mode - */ -} lws_adoption_type; - -typedef union { - lws_sockfd_type sockfd; - lws_filefd_type filefd; -} lws_sock_file_fd_type; - -/* -* lws_adopt_descriptor_vhost() - adopt foreign socket or file descriptor -* if socket descriptor, should already have been accepted from listen socket -* -* \param vhost: lws vhost -* \param type: OR-ed combinations of lws_adoption_type flags -* \param fd: union with either .sockfd or .filefd set -* \param vh_prot_name: NULL or vh protocol name to bind raw connection to -* \param parent: NULL or struct lws to attach new_wsi to as a child -* -* Either returns new wsi bound to accept_fd, or closes accept_fd and -* returns NULL, having cleaned up any new wsi pieces. -* -* If LWS_ADOPT_SOCKET is set, LWS adopts the socket in http serving mode, it's -* ready to accept an upgrade to ws or just serve http. -* -* parent may be NULL, if given it should be an existing wsi that will become the -* parent of the new wsi created by this call. -*/ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, - lws_sock_file_fd_type fd, const char *vh_prot_name, - struct lws *parent); - -/** - * lws_adopt_socket_readbuf() - adopt foreign socket and first rx as if listen socket accepted it - * for the default vhost of context. - * \param context: lws context - * \param accept_fd: fd of already-accepted socket to adopt - * \param readbuf: NULL or pointer to data that must be drained before reading from - * accept_fd - * \param len: The length of the data held at \param readbuf - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - * - * If your external code did not already read from the socket, you can use - * lws_adopt_socket() instead. - * - * This api is guaranteed to use the data at \param readbuf first, before reading from - * the socket. - * - * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, - const char *readbuf, size_t len); -/** - * lws_adopt_socket_vhost_readbuf() - adopt foreign socket and first rx as if listen socket - * accepted it for vhost. - * \param vhost: lws vhost - * \param accept_fd: fd of already-accepted socket to adopt - * \param readbuf: NULL or pointer to data that must be drained before reading from - * accept_fd - * \param len: The length of the data held at \param readbuf - * - * Either returns new wsi bound to accept_fd, or closes accept_fd and - * returns NULL, having cleaned up any new wsi pieces. - * - * LWS adopts the socket in http serving mode, it's ready to accept an upgrade - * to ws or just serve http. - * - * If your external code did not already read from the socket, you can use - * lws_adopt_socket() instead. - * - * This api is guaranteed to use the data at \param readbuf first, before reading from - * the socket. - * - * readbuf is limited to the size of the ah rx buf, currently 2048 bytes. - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, lws_sockfd_type accept_fd, - const char *readbuf, size_t len); -///@} - -/** \defgroup net Network related helper APIs - * ##Network related helper APIs - * - * These wrap miscellaneous useful network-related functions - */ -///@{ - -/** - * lws_canonical_hostname() - returns this host's hostname - * - * This is typically used by client code to fill in the host parameter - * when making a client connection. You can only call it after the context - * has been created. - * - * \param context: Websocket context - */ -LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT -lws_canonical_hostname(struct lws_context *context); - -/** - * lws_get_peer_addresses() - Get client address information - * \param wsi: Local struct lws associated with - * \param fd: Connection socket descriptor - * \param name: Buffer to take client address name - * \param name_len: Length of client address name buffer - * \param rip: Buffer to take client address IP dotted quad - * \param rip_len: Length of client address IP buffer - * - * This function fills in name and rip with the name and IP of - * the client connected with socket descriptor fd. Names may be - * truncated if there is not enough room. If either cannot be - * determined, they will be returned as valid zero-length strings. - */ -LWS_VISIBLE LWS_EXTERN void -lws_get_peer_addresses(struct lws *wsi, lws_sockfd_type fd, char *name, - int name_len, char *rip, int rip_len); - -/** - * lws_get_peer_simple() - Get client address information without RDNS - * - * \param wsi: Local struct lws associated with - * \param name: Buffer to take client address name - * \param namelen: Length of client address name buffer - * - * This provides a 123.123.123.123 type IP address in name from the - * peer that has connected to wsi - */ -LWS_VISIBLE LWS_EXTERN const char * -lws_get_peer_simple(struct lws *wsi, char *name, int namelen); -#if !defined(LWS_WITH_ESP8266) && !defined(LWS_WITH_ESP32) -/** - * lws_interface_to_sa() - Convert interface name or IP to sockaddr struct - * - * \param ipv6: Allow IPV6 addresses - * \param ifname: Interface name or IP - * \param addr: struct sockaddr_in * to be written - * \param addrlen: Length of addr - * - * This converts a textual network interface name to a sockaddr usable by - * other network functions - */ -LWS_VISIBLE LWS_EXTERN int -lws_interface_to_sa(int ipv6, const char *ifname, struct sockaddr_in *addr, - size_t addrlen); -///@} -#endif - -/** \defgroup misc Miscellaneous APIs -* ##Miscellaneous APIs -* -* Various APIs outside of other categories -*/ -///@{ - -/** - * lws_start_foreach_ll(): linkedlist iterator helper start - * - * \param type: type of iteration, eg, struct xyz * - * \param it: iterator var name to create - * \param start: start of list - * - * This helper creates an iterator and starts a while (it) { - * loop. The iterator runs through the linked list starting at start and - * ends when it gets a NULL. - * The while loop should be terminated using lws_start_foreach_ll(). - */ -#define lws_start_foreach_ll(type, it, start)\ -{ \ - type it = start; \ - while (it) { - -/** - * lws_end_foreach_ll(): linkedlist iterator helper end - * - * \param it: same iterator var name given when starting - * \param nxt: member name in the iterator pointing to next list element - * - * This helper is the partner for lws_start_foreach_ll() that ends the - * while loop. - */ - -#define lws_end_foreach_ll(it, nxt) \ - it = it->nxt; \ - } \ -} - -/** - * lws_start_foreach_llp(): linkedlist pointer iterator helper start - * - * \param type: type of iteration, eg, struct xyz ** - * \param it: iterator var name to create - * \param start: start of list - * - * This helper creates an iterator and starts a while (it) { - * loop. The iterator runs through the linked list starting at the - * address of start and ends when it gets a NULL. - * The while loop should be terminated using lws_start_foreach_llp(). - * - * This helper variant iterates using a pointer to the previous linked-list - * element. That allows you to easily delete list members by rewriting the - * previous pointer to the element's next pointer. - */ -#define lws_start_foreach_llp(type, it, start)\ -{ \ - type it = &(start); \ - while (*(it)) { - -/** - * lws_end_foreach_llp(): linkedlist pointer iterator helper end - * - * \param it: same iterator var name given when starting - * \param nxt: member name in the iterator pointing to next list element - * - * This helper is the partner for lws_start_foreach_llp() that ends the - * while loop. - */ - -#define lws_end_foreach_llp(it, nxt) \ - it = &(*(it))->nxt; \ - } \ -} - -/** - * lws_snprintf(): snprintf that truncates the returned length too - * - * \param str: destination buffer - * \param size: bytes left in destination buffer - * \param format: format string - * \param ...: args for format - * - * This lets you correctly truncate buffers by concatenating lengths, if you - * reach the limit the reported length doesn't exceed the limit. - */ -LWS_VISIBLE LWS_EXTERN int -lws_snprintf(char *str, size_t size, const char *format, ...) LWS_FORMAT(3); - -/** - * lws_get_random(): fill a buffer with platform random data - * - * \param context: the lws context - * \param buf: buffer to fill - * \param len: how much to fill - * - * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if - * it's interested to see if the frame it's dealing with was sent in binary - * mode. - */ -LWS_VISIBLE LWS_EXTERN int -lws_get_random(struct lws_context *context, void *buf, int len); -/** - * lws_daemonize(): make current process run in the background - * - * \param _lock_path: the filepath to write the lock file - * - * Spawn lws as a background process, taking care of various things - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_daemonize(const char *_lock_path); -/** - * lws_get_library_version(): return string describing the version of lws - * - * On unix, also includes the git describe - */ -LWS_VISIBLE LWS_EXTERN const char * LWS_WARN_UNUSED_RESULT -lws_get_library_version(void); - -/** - * lws_wsi_user() - get the user data associated with the connection - * \param wsi: lws connection - * - * Not normally needed since it's passed into the callback - */ -LWS_VISIBLE LWS_EXTERN void * -lws_wsi_user(struct lws *wsi); - -/** - * lws_wsi_set_user() - set the user data associated with the client connection - * \param wsi: lws connection - * \param user: user data - * - * By default lws allocates this and it's not legal to externally set it - * yourself. However client connections may have it set externally when the - * connection is created... if so, this api can be used to modify it at - * runtime additionally. - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_wsi_user(struct lws *wsi, void *user); - -/** - * lws_parse_uri: cut up prot:/ads:port/path into pieces - * Notice it does so by dropping '\0' into input string - * and the leading / on the path is consequently lost - * - * \param p: incoming uri string.. will get written to - * \param prot: result pointer for protocol part (https://) - * \param ads: result pointer for address part - * \param port: result pointer for port part - * \param path: result pointer for path part - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_parse_uri(char *p, const char **prot, const char **ads, int *port, - const char **path); - -/** - * lws_now_secs(): return seconds since 1970-1-1 - */ -LWS_VISIBLE LWS_EXTERN unsigned long -lws_now_secs(void); - -/** - * lws_get_context - Allow getting lws_context from a Websocket connection - * instance - * - * With this function, users can access context in the callback function. - * Otherwise users may have to declare context as a global variable. - * - * \param wsi: Websocket connection instance - */ -LWS_VISIBLE LWS_EXTERN struct lws_context * LWS_WARN_UNUSED_RESULT -lws_get_context(const struct lws *wsi); - -/** - * lws_get_count_threads(): how many service threads the context uses - * - * \param context: the lws context - * - * By default this is always 1, if you asked for more than lws can handle it - * will clip the number of threads. So you can use this to find out how many - * threads are actually in use. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_get_count_threads(struct lws_context *context); - -/** - * lws_get_parent() - get parent wsi or NULL - * \param wsi: lws connection - * - * Specialized wsi like cgi stdin/out/err are associated to a parent wsi, - * this allows you to get their parent. - */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_get_parent(const struct lws *wsi); - -/** - * lws_get_child() - get child wsi or NULL - * \param wsi: lws connection - * - * Allows you to find a related wsi from the parent wsi. - */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_get_child(const struct lws *wsi); - -/** - * lws_parent_carries_io() - mark wsi as needing to send messages via parent - * - * \param wsi: child lws connection - */ - -LWS_VISIBLE LWS_EXTERN void -lws_set_parent_carries_io(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void * -lws_get_opaque_parent_data(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void -lws_set_opaque_parent_data(struct lws *wsi, void *data); - -LWS_VISIBLE LWS_EXTERN int -lws_get_child_pending_on_writable(const struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN void -lws_clear_child_pending_on_writable(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN int -lws_get_close_length(struct lws *wsi); - -LWS_VISIBLE LWS_EXTERN unsigned char * -lws_get_close_payload(struct lws *wsi); - -/** - * lws_get_network_wsi() - Returns wsi that has the tcp connection for this wsi - * - * \param wsi: wsi you have - * - * Returns wsi that has the tcp connection (which may be the incoming wsi) - * - * HTTP/1 connections will always return the incoming wsi - * HTTP/2 connections may return a different wsi that has the tcp connection - */ -LWS_VISIBLE LWS_EXTERN -struct lws *lws_get_network_wsi(struct lws *wsi); - -/* - * \deprecated DEPRECATED Note: this is not normally needed as a user api. - * It's provided in case it is - * useful when integrating with other app poll loop service code. - */ -LWS_VISIBLE LWS_EXTERN int -lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len); - -/** - * lws_set_allocator() - custom allocator support - * - * \param realloc - * - * Allows you to replace the allocator (and deallocator) used by lws - */ -LWS_VISIBLE LWS_EXTERN void -lws_set_allocator(void *(*realloc)(void *ptr, size_t size, const char *reason)); -///@} - -/** \defgroup wsstatus Websocket status APIs - * ##Websocket connection status APIs - * - * These provide information about ws connection or message status - */ -///@{ -/** - * lws_send_pipe_choked() - tests if socket is writable or not - * \param wsi: lws connection - * - * Allows you to check if you can write more on the socket - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_send_pipe_choked(struct lws *wsi); - -/** - * lws_is_final_fragment() - tests if last part of ws message - * - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_final_fragment(struct lws *wsi); - -/** - * lws_is_first_fragment() - tests if first part of ws message - * - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_first_fragment(struct lws *wsi); - -/** - * lws_get_reserved_bits() - access reserved bits of ws frame - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN unsigned char -lws_get_reserved_bits(struct lws *wsi); - -/** - * lws_partial_buffered() - find out if lws buffered the last write - * \param wsi: websocket connection to check - * - * Returns 1 if you cannot use lws_write because the last - * write on this connection is still buffered, and can't be cleared without - * returning to the service loop and waiting for the connection to be - * writeable again. - * - * If you will try to do >1 lws_write call inside a single - * WRITEABLE callback, you must check this after every write and bail if - * set, ask for a new writeable callback and continue writing from there. - * - * This is never set at the start of a writeable callback, but any write - * may set it. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_partial_buffered(struct lws *wsi); - -/** - * lws_frame_is_binary(): true if the current frame was sent in binary mode - * - * \param wsi: the connection we are inquiring about - * - * This is intended to be called from the LWS_CALLBACK_RECEIVE callback if - * it's interested to see if the frame it's dealing with was sent in binary - * mode. - */ -LWS_VISIBLE LWS_EXTERN int LWS_WARN_UNUSED_RESULT -lws_frame_is_binary(struct lws *wsi); - -/** - * lws_is_ssl() - Find out if connection is using SSL - * \param wsi: websocket connection to check - * - * Returns 0 if the connection is not using SSL, 1 if using SSL and - * using verified cert, and 2 if using SSL but the cert was not - * checked (appears for client wsi told to skip check on connection) - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_ssl(struct lws *wsi); -/** - * lws_is_cgi() - find out if this wsi is running a cgi process - * \param wsi: lws connection - */ -LWS_VISIBLE LWS_EXTERN int -lws_is_cgi(struct lws *wsi); - -#ifdef LWS_OPENSSL_SUPPORT -/** - * lws_get_ssl() - Return wsi's SSL context structure - * \param wsi: websocket connection - * - * Returns pointer to the SSL library's context structure - */ -LWS_VISIBLE LWS_EXTERN SSL* -lws_get_ssl(struct lws *wsi); -#endif -///@} - -/** \defgroup lws_ring LWS Ringbuffer APIs - * ##lws_ring: generic ringbuffer struct - * - * Provides an abstract ringbuffer api supporting one head and one or an - * unlimited number of tails. - * - * All of the members are opaque and manipulated by lws_ring_...() apis. - * - * The lws_ring and its buffer is allocated at runtime on the heap, using - * - * - lws_ring_create() - * - lws_ring_destroy() - * - * It may contain any type, the size of the "element" stored in the ring - * buffer and the number of elements is given at creation time. - * - * When you create the ringbuffer, you can optionally provide an element - * destroy callback that frees any allocations inside the element. This is then - * automatically called for elements with no tail behind them, ie, elements - * which don't have any pending consumer are auto-freed. - * - * Whole elements may be inserted into the ringbuffer and removed from it, using - * - * - lws_ring_insert() - * - lws_ring_consume() - * - * You can find out how many whole elements are free or waiting using - * - * - lws_ring_get_count_free_elements() - * - lws_ring_get_count_waiting_elements() - * - * In addition there are special purpose optional byte-centric apis - * - * - lws_ring_next_linear_insert_range() - * - lws_ring_bump_head() - * - * which let you, eg, read() directly into the ringbuffer without needing - * an intermediate bounce buffer. - * - * The accessors understand that the ring wraps, and optimizes insertion and - * consumption into one or two memcpy()s depending on if the head or tail - * wraps. - * - * lws_ring only supports a single head, but optionally multiple tails with - * an API to inform it when the "oldest" tail has moved on. You can give - * NULL where-ever an api asks for a tail pointer, and it will use an internal - * single tail pointer for convenience. - * - * The "oldest tail", which is the only tail if you give it NULL instead of - * some other tail, is used to track which elements in the ringbuffer are - * still unread by anyone. - * - * - lws_ring_update_oldest_tail() - */ -///@{ -struct lws_ring; - -/** - * lws_ring_create(): create a new ringbuffer - * - * \param element_len: the size in bytes of one element in the ringbuffer - * \param count: the number of elements the ringbuffer can contain - * \param destroy_element: NULL, or callback to be called for each element - * that is removed from the ringbuffer due to the - * oldest tail moving beyond it - * - * Creates the ringbuffer and allocates the storage. Returns the new - * lws_ring *, or NULL if the allocation failed. - * - * If non-NULL, destroy_element will get called back for every element that is - * retired from the ringbuffer after the oldest tail has gone past it, and for - * any element still left in the ringbuffer when it is destroyed. It replaces - * all other element destruction code in your user code. - */ -LWS_VISIBLE LWS_EXTERN struct lws_ring * -lws_ring_create(size_t element_len, size_t count, - void (*destroy_element)(void *element)); - -/** - * lws_ring_destroy(): destroy a previously created ringbuffer - * - * \param ring: the struct lws_ring to destroy - * - * Destroys the ringbuffer allocation and the struct lws_ring itself. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_destroy(struct lws_ring *ring); - -/** - * lws_ring_get_count_free_elements(): return how many elements can fit - * in the free space - * - * \param ring: the struct lws_ring to report on - * - * Returns how much room is left in the ringbuffer for whole element insertion. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_get_count_free_elements(struct lws_ring *ring); - -/** - * lws_ring_get_count_waiting_elements(): return how many elements can be consumed - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * Returns how many elements are waiting to be consumed from the perspective - * of the tail pointer given. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_get_count_waiting_elements(struct lws_ring *ring, uint32_t *tail); - -/** - * lws_ring_insert(): attempt to insert up to max_count elements from src - * - * \param ring: the struct lws_ring to report on - * \param src: the array of elements to be inserted - * \param max_count: the number of available elements at src - * - * Attempts to insert as many of the elements at src as possible, up to the - * maximum max_count. Returns the number of elements actually inserted. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_insert(struct lws_ring *ring, const void *src, size_t max_count); - -/** - * lws_ring_consume(): attempt to copy out and remove up to max_count elements - * to src - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * \param dest: the array of elements to be inserted. or NULL for no copy - * \param max_count: the number of available elements at src - * - * Attempts to copy out as many waiting elements as possible into dest, from - * the perspective of the given tail, up to max_count. If dest is NULL, the - * copying out is not done but the elements are logically consumed as usual. - * NULL dest is useful in combination with lws_ring_get_element(), where you - * can use the element direct from the ringbuffer and then call this with NULL - * dest to logically consume it. - * - * Increments the tail position according to how many elements could be - * consumed. - * - * Returns the number of elements consumed. - */ -LWS_VISIBLE LWS_EXTERN size_t -lws_ring_consume(struct lws_ring *ring, uint32_t *tail, void *dest, - size_t max_count); - -/** - * lws_ring_get_element(): get a pointer to the next waiting element for tail - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * Points to the next element that tail would consume, directly in the - * ringbuffer. This lets you write() or otherwise use the element without - * having to copy it out somewhere first. - * - * After calling this, you must call lws_ring_consume(ring, &tail, NULL, 1) - * which will logically consume the element you used up and increment your - * tail (tail may also be NULL there if you use a single tail). - * - * Returns NULL if no waiting element, or a const void * pointing to it. - */ -LWS_VISIBLE LWS_EXTERN const void * -lws_ring_get_element(struct lws_ring *ring, uint32_t *tail); - -/** - * lws_ring_update_oldest_tail(): free up elements older than tail for reuse - * - * \param ring: the struct lws_ring to report on - * \param tail: a pointer to the tail struct to use, or NULL for single tail - * - * If you are using multiple tails, you must use this API to inform the - * lws_ring when none of the tails still need elements in the fifo any more, - * by updating it when the "oldest" tail has moved on. - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_update_oldest_tail(struct lws_ring *ring, uint32_t tail); - -/** - * lws_ring_get_oldest_tail(): get current oldest available data index - * - * \param ring: the struct lws_ring to report on - * - * If you are initializing a new ringbuffer consumer, you can set its tail to - * this to start it from the oldest ringbuffer entry still available. - */ -LWS_VISIBLE LWS_EXTERN uint32_t -lws_ring_get_oldest_tail(struct lws_ring *ring); - -/** - * lws_ring_next_linear_insert_range(): used to write directly into the ring - * - * \param ring: the struct lws_ring to report on - * \param start: pointer to a void * set to the start of the next ringbuffer area - * \param bytes: pointer to a size_t set to the max length you may use from *start - * - * This provides a low-level, bytewise access directly into the ringbuffer - * allowing direct insertion of data without having to use a bounce buffer. - * - * The api reports the position and length of the next linear range that can - * be written in the ringbuffer, ie, up to the point it would wrap, and sets - * *start and *bytes accordingly. You can then, eg, directly read() into - * *start for up to *bytes, and use lws_ring_bump_head() to update the lws_ring - * with what you have done. - * - * Returns nonzero if no insertion is currently possible. - */ -LWS_VISIBLE LWS_EXTERN int -lws_ring_next_linear_insert_range(struct lws_ring *ring, void **start, - size_t *bytes); - -/** - * lws_ring_bump_head(): used to write directly into the ring - * - * \param ring: the struct lws_ring to operate on - * \param bytes: the number of bytes you inserted at the current head - */ -LWS_VISIBLE LWS_EXTERN void -lws_ring_bump_head(struct lws_ring *ring, size_t bytes); -///@} - -/** \defgroup sha SHA and B64 helpers - * ##SHA and B64 helpers - * - * These provide SHA-1 and B64 helper apis - */ -///@{ -#ifdef LWS_SHA1_USE_OPENSSL_NAME -#define lws_SHA1 SHA1 -#else -/** - * lws_SHA1(): make a SHA-1 digest of a buffer - * - * \param d: incoming buffer - * \param n: length of incoming buffer - * \param md: buffer for message digest (must be >= 20 bytes) - * - * Reduces any size buffer into a 20-byte SHA-1 hash. - */ -LWS_VISIBLE LWS_EXTERN unsigned char * -lws_SHA1(const unsigned char *d, size_t n, unsigned char *md); -#endif -/** - * lws_b64_encode_string(): encode a string into base 64 - * - * \param in: incoming buffer - * \param in_len: length of incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Encodes a string using b64 - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_encode_string(const char *in, int in_len, char *out, int out_size); -/** - * lws_b64_decode_string(): decode a string from base 64 - * - * \param in: incoming buffer - * \param out: result buffer - * \param out_size: length of result buffer - * - * Decodes a string using b64 - */ -LWS_VISIBLE LWS_EXTERN int -lws_b64_decode_string(const char *in, char *out, int out_size); -///@} - - -/*! \defgroup cgi cgi handling - * - * ##CGI handling - * - * These functions allow low-level control over stdin/out/err of the cgi. - * - * However for most cases, binding the cgi to http in and out, the default - * lws implementation already does the right thing. - */ - -enum lws_enum_stdinouterr { - LWS_STDIN = 0, - LWS_STDOUT = 1, - LWS_STDERR = 2, -}; - -enum lws_cgi_hdr_state { - LCHS_HEADER, - LCHS_CR1, - LCHS_LF1, - LCHS_CR2, - LCHS_LF2, - LHCS_RESPONSE, - LHCS_DUMP_HEADERS, - LHCS_PAYLOAD, - LCHS_SINGLE_0A, -}; - -struct lws_cgi_args { - struct lws **stdwsi; /**< get fd with lws_get_socket_fd() */ - enum lws_enum_stdinouterr ch; /**< channel index */ - unsigned char *data; /**< for messages with payload */ - enum lws_cgi_hdr_state hdr_state; /**< track where we are in cgi headers */ - int len; /**< length */ -}; - -#ifdef LWS_WITH_CGI -/** - * lws_cgi: spawn network-connected cgi process - * - * \param wsi: connection to own the process - * \param exec_array: array of "exec-name" "arg1" ... "argn" NULL - * \param script_uri_path_len: how many chars on the left of the uri are the - * path to the cgi, or -1 to spawn without URL-related env vars - * \param timeout_secs: seconds script should be allowed to run - * \param mp_cgienv: pvo list with per-vhost cgi options to put in env - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi(struct lws *wsi, const char * const *exec_array, - int script_uri_path_len, int timeout_secs, - const struct lws_protocol_vhost_options *mp_cgienv); - -/** - * lws_cgi_write_split_stdout_headers: write cgi output accounting for header part - * - * \param wsi: connection to own the process - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi_write_split_stdout_headers(struct lws *wsi); - -/** - * lws_cgi_kill: terminate cgi process associated with wsi - * - * \param wsi: connection to own the process - */ -LWS_VISIBLE LWS_EXTERN int -lws_cgi_kill(struct lws *wsi); - -/** - * lws_cgi_get_stdwsi: get wsi for stdin, stdout, or stderr - * - * \param wsi: parent wsi that has cgi - * \param ch: which of LWS_STDIN, LWS_STDOUT or LWS_STDERR - */ -LWS_VISIBLE LWS_EXTERN struct lws * -lws_cgi_get_stdwsi(struct lws *wsi, enum lws_enum_stdinouterr ch); - -#endif -///@} - - -/*! \defgroup fops file operation wrapping - * - * ##File operation wrapping - * - * Use these helper functions if you want to access a file from the perspective - * of a specific wsi, which is usually the case. If you just want contextless - * file access, use the fops callbacks directly with NULL wsi instead of these - * helpers. - * - * If so, then it calls the platform handler or user overrides where present - * (as defined in info->fops) - * - * The advantage from all this is user code can be portable for file operations - * without having to deal with differences between platforms. - */ -//@{ - -/** struct lws_plat_file_ops - Platform-specific file operations - * - * These provide platform-agnostic ways to deal with filesystem access in the - * library and in the user code. - */ - -#if defined(LWS_WITH_ESP32) -/* sdk preprocessor defs? compiler issue? gets confused with member names */ -#define LWS_FOP_OPEN _open -#define LWS_FOP_CLOSE _close -#define LWS_FOP_SEEK_CUR _seek_cur -#define LWS_FOP_READ _read -#define LWS_FOP_WRITE _write -#else -#define LWS_FOP_OPEN open -#define LWS_FOP_CLOSE close -#define LWS_FOP_SEEK_CUR seek_cur -#define LWS_FOP_READ read -#define LWS_FOP_WRITE write -#endif - -#define LWS_FOP_FLAGS_MASK ((1 << 23) - 1) -#define LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP (1 << 24) -#define LWS_FOP_FLAG_COMPR_IS_GZIP (1 << 25) -#define LWS_FOP_FLAG_MOD_TIME_VALID (1 << 26) -#define LWS_FOP_FLAG_VIRTUAL (1 << 27) - -struct lws_plat_file_ops; - -struct lws_fop_fd { - lws_filefd_type fd; - /**< real file descriptor related to the file... */ - const struct lws_plat_file_ops *fops; - /**< fops that apply to this fop_fd */ - void *filesystem_priv; - /**< ignored by lws; owned by the fops handlers */ - lws_filepos_t pos; - /**< generic "position in file" */ - lws_filepos_t len; - /**< generic "length of file" */ - lws_fop_flags_t flags; - /**< copy of the returned flags */ - uint32_t mod_time; - /**< optional "modification time of file", only valid if .open() - * set the LWS_FOP_FLAG_MOD_TIME_VALID flag */ -}; -typedef struct lws_fop_fd *lws_fop_fd_t; - -struct lws_fops_index { - const char *sig; /* NULL or vfs signature, eg, ".zip/" */ - uint8_t len; /* length of above string */ -}; - -struct lws_plat_file_ops { - lws_fop_fd_t (*LWS_FOP_OPEN)(const struct lws_plat_file_ops *fops, - const char *filename, const char *vpath, - lws_fop_flags_t *flags); - /**< Open file (always binary access if plat supports it) - * vpath may be NULL, or if the fops understands it, the point at which - * the filename's virtual part starts. - * *flags & LWS_FOP_FLAGS_MASK should be set to O_RDONLY or O_RDWR. - * If the file may be gzip-compressed, - * LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP is set. If it actually is - * gzip-compressed, then the open handler should OR - * LWS_FOP_FLAG_COMPR_IS_GZIP on to *flags before returning. - */ - int (*LWS_FOP_CLOSE)(lws_fop_fd_t *fop_fd); - /**< close file AND set the pointer to NULL */ - lws_fileofs_t (*LWS_FOP_SEEK_CUR)(lws_fop_fd_t fop_fd, - lws_fileofs_t offset_from_cur_pos); - /**< seek from current position */ - int (*LWS_FOP_READ)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - /**< Read from file, on exit *amount is set to amount actually read */ - int (*LWS_FOP_WRITE)(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - /**< Write to file, on exit *amount is set to amount actually written */ - - struct lws_fops_index fi[3]; - /**< vfs path signatures implying use of this fops */ - - const struct lws_plat_file_ops *next; - /**< NULL or next fops in list */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ -}; - -/** - * lws_get_fops() - get current file ops - * - * \param context: context - */ -LWS_VISIBLE LWS_EXTERN struct lws_plat_file_ops * LWS_WARN_UNUSED_RESULT -lws_get_fops(struct lws_context *context); -LWS_VISIBLE LWS_EXTERN void -lws_set_fops(struct lws_context *context, const struct lws_plat_file_ops *fops); -/** - * lws_vfs_tell() - get current file position - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT -lws_vfs_tell(lws_fop_fd_t fop_fd); -/** - * lws_vfs_get_length() - get current file total length in bytes - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN lws_filepos_t LWS_WARN_UNUSED_RESULT -lws_vfs_get_length(lws_fop_fd_t fop_fd); -/** - * lws_vfs_get_mod_time() - get time file last modified - * - * \param fop_fd: fop_fd we are asking about - */ -LWS_VISIBLE LWS_EXTERN uint32_t LWS_WARN_UNUSED_RESULT -lws_vfs_get_mod_time(lws_fop_fd_t fop_fd); -/** - * lws_vfs_file_seek_set() - seek relative to start of file - * - * \param fop_fd: fop_fd we are seeking in - * \param offset: offset from start of file - */ -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -lws_vfs_file_seek_set(lws_fop_fd_t fop_fd, lws_fileofs_t offset); -/** - * lws_vfs_file_seek_end() - seek relative to end of file - * - * \param fop_fd: fop_fd we are seeking in - * \param offset: offset from start of file - */ -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -lws_vfs_file_seek_end(lws_fop_fd_t fop_fd, lws_fileofs_t offset); - -extern struct lws_plat_file_ops fops_zip; - -/** - * lws_plat_file_open() - open vfs filepath - * - * \param fops: file ops struct that applies to this descriptor - * \param vfs_path: filename to open - * \param flags: pointer to open flags - * - * The vfs_path is scanned for known fops signatures, and the open directed - * to any matching fops open. - * - * User code should use this api to perform vfs opens. - * - * returns semi-opaque handle - */ -LWS_VISIBLE LWS_EXTERN lws_fop_fd_t LWS_WARN_UNUSED_RESULT -lws_vfs_file_open(const struct lws_plat_file_ops *fops, const char *vfs_path, - lws_fop_flags_t *flags); - -/** - * lws_plat_file_close() - close file - * - * \param fop_fd: file handle to close - */ -static LWS_INLINE int -lws_vfs_file_close(lws_fop_fd_t *fop_fd) -{ - return (*fop_fd)->fops->LWS_FOP_CLOSE(fop_fd); -} - -/** - * lws_plat_file_seek_cur() - close file - * - * - * \param fop_fd: file handle - * \param offset: position to seek to - */ -static LWS_INLINE lws_fileofs_t -lws_vfs_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset) -{ - return fop_fd->fops->LWS_FOP_SEEK_CUR(fop_fd, offset); -} -/** - * lws_plat_file_read() - read from file - * - * \param fop_fd: file handle - * \param amount: how much to read (rewritten by call) - * \param buf: buffer to write to - * \param len: max length - */ -static LWS_INLINE int LWS_WARN_UNUSED_RESULT -lws_vfs_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - return fop_fd->fops->LWS_FOP_READ(fop_fd, amount, buf, len); -} -/** - * lws_plat_file_write() - write from file - * - * \param fop_fd: file handle - * \param amount: how much to write (rewritten by call) - * \param buf: buffer to read from - * \param len: max length - */ -static LWS_INLINE int LWS_WARN_UNUSED_RESULT -lws_vfs_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len) -{ - return fop_fd->fops->LWS_FOP_WRITE(fop_fd, amount, buf, len); -} - -/* these are the platform file operations implementations... they can - * be called directly and used in fops arrays - */ - -LWS_VISIBLE LWS_EXTERN lws_fop_fd_t -_lws_plat_file_open(const struct lws_plat_file_ops *fops, const char *filename, - const char *vpath, lws_fop_flags_t *flags); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_close(lws_fop_fd_t *fop_fd); -LWS_VISIBLE LWS_EXTERN lws_fileofs_t -_lws_plat_file_seek_cur(lws_fop_fd_t fop_fd, lws_fileofs_t offset); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_read(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); -LWS_VISIBLE LWS_EXTERN int -_lws_plat_file_write(lws_fop_fd_t fop_fd, lws_filepos_t *amount, - uint8_t *buf, lws_filepos_t len); - -LWS_VISIBLE LWS_EXTERN int -lws_alloc_vfs_file(struct lws_context *context, const char *filename, - uint8_t **buf, lws_filepos_t *amount); -//@} - -/** \defgroup smtp SMTP related functions - * ##SMTP related functions - * \ingroup lwsapi - * - * These apis let you communicate with a local SMTP server to send email from - * lws. It handles all the SMTP sequencing and protocol actions. - * - * Your system should have postfix, sendmail or another MTA listening on port - * 25 and able to send email using the "mail" commandline app. Usually distro - * MTAs are configured for this by default. - * - * It runs via its own libuv events if initialized (which requires giving it - * a libuv loop to attach to). - * - * It operates using three callbacks, on_next() queries if there is a new email - * to send, on_get_body() asks for the body of the email, and on_sent() is - * called after the email is successfully sent. - * - * To use it - * - * - create an lws_email struct - * - * - initialize data, loop, the email_* strings, max_content_size and - * the callbacks - * - * - call lws_email_init() - * - * When you have at least one email to send, call lws_email_check() to - * schedule starting to send it. - */ -//@{ -#ifdef LWS_WITH_SMTP - -/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */ -enum lwsgs_smtp_states { - LGSSMTP_IDLE, /**< awaiting new email */ - LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */ - LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */ - LGSSMTP_SENT_HELO, /**< sent the HELO */ - LGSSMTP_SENT_FROM, /**< sent FROM */ - LGSSMTP_SENT_TO, /**< sent TO */ - LGSSMTP_SENT_DATA, /**< sent DATA request */ - LGSSMTP_SENT_BODY, /**< sent the email body */ - LGSSMTP_SENT_QUIT, /**< sent the session quit */ -}; - -/** struct lws_email - abstract context for performing SMTP operations */ -struct lws_email { - void *data; - /**< opaque pointer set by user code and available to the callbacks */ - uv_loop_t *loop; - /**< the libuv loop we will work on */ - - char email_smtp_ip[32]; /**< Fill before init, eg, "127.0.0.1" */ - char email_helo[32]; /**< Fill before init, eg, "myserver.com" */ - char email_from[100]; /**< Fill before init or on_next */ - char email_to[100]; /**< Fill before init or on_next */ - - unsigned int max_content_size; - /**< largest possible email body size */ - - /* Fill all the callbacks before init */ - - int (*on_next)(struct lws_email *email); - /**< (Fill in before calling lws_email_init) - * called when idle, 0 = another email to send, nonzero is idle. - * If you return 0, all of the email_* char arrays must be set - * to something useful. */ - int (*on_sent)(struct lws_email *email); - /**< (Fill in before calling lws_email_init) - * called when transfer of the email to the SMTP server was - * successful, your callback would remove the current email - * from its queue */ - int (*on_get_body)(struct lws_email *email, char *buf, int len); - /**< (Fill in before calling lws_email_init) - * called when the body part of the queued email is about to be - * sent to the SMTP server. */ - - - /* private things */ - uv_timer_t timeout_email; /**< private */ - enum lwsgs_smtp_states estate; /**< private */ - uv_connect_t email_connect_req; /**< private */ - uv_tcp_t email_client; /**< private */ - time_t email_connect_started; /**< private */ - char email_buf[256]; /**< private */ - char *content; /**< private */ -}; - -/** - * lws_email_init() - Initialize a struct lws_email - * - * \param email: struct lws_email to init - * \param loop: libuv loop to use - * \param max_content: max email content size - * - * Prepares a struct lws_email for use ending SMTP - */ -LWS_VISIBLE LWS_EXTERN int -lws_email_init(struct lws_email *email, uv_loop_t *loop, int max_content); - -/** - * lws_email_check() - Request check for new email - * - * \param email: struct lws_email context to check - * - * Schedules a check for new emails in 1s... call this when you have queued an - * email for send. - */ -LWS_VISIBLE LWS_EXTERN void -lws_email_check(struct lws_email *email); -/** - * lws_email_destroy() - stop using the struct lws_email - * - * \param email: the struct lws_email context - * - * Stop sending email using email and free allocations - */ -LWS_VISIBLE LWS_EXTERN void -lws_email_destroy(struct lws_email *email); - -#endif -//@} - -/* - * Stats are all uint64_t numbers that start at 0. - * Index names here have the convention - * - * _C_ counter - * _B_ byte count - * _MS_ millisecond count - */ - -enum { - LWSSTATS_C_CONNECTIONS, /**< count incoming connections */ - LWSSTATS_C_API_CLOSE, /**< count calls to close api */ - LWSSTATS_C_API_READ, /**< count calls to read from socket api */ - LWSSTATS_C_API_LWS_WRITE, /**< count calls to lws_write API */ - LWSSTATS_C_API_WRITE, /**< count calls to write API */ - LWSSTATS_C_WRITE_PARTIALS, /**< count of partial writes */ - LWSSTATS_C_WRITEABLE_CB_REQ, /**< count of writable callback requests */ - LWSSTATS_C_WRITEABLE_CB_EFF_REQ, /**< count of effective writable callback requests */ - LWSSTATS_C_WRITEABLE_CB, /**< count of writable callbacks */ - LWSSTATS_C_SSL_CONNECTIONS_FAILED, /**< count of failed SSL connections */ - LWSSTATS_C_SSL_CONNECTIONS_ACCEPTED, /**< count of accepted SSL connections */ - LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, /**< count of SSL_accept() attempts */ - LWSSTATS_C_SSL_CONNS_HAD_RX, /**< count of accepted SSL conns that have had some RX */ - LWSSTATS_C_TIMEOUTS, /**< count of timed-out connections */ - LWSSTATS_C_SERVICE_ENTRY, /**< count of entries to lws service loop */ - LWSSTATS_B_READ, /**< aggregate bytes read */ - LWSSTATS_B_WRITE, /**< aggregate bytes written */ - LWSSTATS_B_PARTIALS_ACCEPTED_PARTS, /**< aggreate of size of accepted write data from new partials */ - LWSSTATS_MS_SSL_CONNECTIONS_ACCEPTED_DELAY, /**< aggregate delay in accepting connection */ - LWSSTATS_MS_WRITABLE_DELAY, /**< aggregate delay between asking for writable and getting cb */ - LWSSTATS_MS_WORST_WRITABLE_DELAY, /**< single worst delay between asking for writable and getting cb */ - LWSSTATS_MS_SSL_RX_DELAY, /**< aggregate delay between ssl accept complete and first RX */ - LWSSTATS_C_PEER_LIMIT_AH_DENIED, /**< number of times we would have given an ah but for the peer limit */ - LWSSTATS_C_PEER_LIMIT_WSI_DENIED, /**< number of times we would have given a wsi but for the peer limit */ - - /* Add new things just above here ---^ - * This is part of the ABI, don't needlessly break compatibility */ - LWSSTATS_SIZE -}; - -#if defined(LWS_WITH_STATS) - -LWS_VISIBLE LWS_EXTERN uint64_t -lws_stats_get(struct lws_context *context, int index); -LWS_VISIBLE LWS_EXTERN void -lws_stats_log_dump(struct lws_context *context); -#else -static LWS_INLINE uint64_t -lws_stats_get(struct lws_context *context, int index) { return 0; } -static LWS_INLINE void -lws_stats_log_dump(struct lws_context *context) { } -#endif - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/Kiwano/third-party/libwebsockets/lws_config.h b/Kiwano/third-party/libwebsockets/lws_config.h deleted file mode 100644 index a28905c7..00000000 --- a/Kiwano/third-party/libwebsockets/lws_config.h +++ /dev/null @@ -1,156 +0,0 @@ -/* lws_config.h Generated from lws_config.h.in */ - -#ifndef NDEBUG - #ifndef _DEBUG - #define _DEBUG - #endif -#endif - -#define LWS_INSTALL_DATADIR "C:/Program Files (x86)/libwebsockets/share" - -/* Define to 1 to use wolfSSL/CyaSSL as a replacement for OpenSSL. - * LWS_OPENSSL_SUPPORT needs to be set also for this to work. */ -/* #undef USE_WOLFSSL */ - -/* Also define to 1 (in addition to USE_WOLFSSL) when using the - (older) CyaSSL library */ -/* #undef USE_OLD_CYASSL */ -/* #undef LWS_WITH_BORINGSSL */ - -/* #undef LWS_WITH_MBEDTLS */ -/* #undef LWS_WITH_POLARSSL */ -/* #undef LWS_WITH_ESP8266 */ -/* #undef LWS_WITH_ESP32 */ - -/* #undef LWS_WITH_PLUGINS */ -/* #undef LWS_WITH_NO_LOGS */ - -/* The Libwebsocket version */ -#define LWS_LIBRARY_VERSION "2.4.2" - -#define LWS_LIBRARY_VERSION_MAJOR 2 -#define LWS_LIBRARY_VERSION_MINOR 4 -#define LWS_LIBRARY_VERSION_PATCH 2 -/* LWS_LIBRARY_VERSION_NUMBER looks like 1005001 for e.g. version 1.5.1 */ -#define LWS_LIBRARY_VERSION_NUMBER (LWS_LIBRARY_VERSION_MAJOR*1000000)+(LWS_LIBRARY_VERSION_MINOR*1000)+LWS_LIBRARY_VERSION_PATCH - -/* The current git commit hash that we're building from */ -#define LWS_BUILD_HASH "desktop-s54jiqa\\serveryjx@DESKTOP-S54JIQA-v2.0.0-640-g8964ce9d" - -/* Build with OpenSSL support */ -#define LWS_OPENSSL_SUPPORT - -/* The client should load and trust CA root certs it finds in the OS */ -#define LWS_SSL_CLIENT_USE_OS_CA_CERTS - -/* Sets the path where the client certs should be installed. */ -#define LWS_OPENSSL_CLIENT_CERTS "../share" - -/* Turn off websocket extensions */ -/* #undef LWS_NO_EXTENSIONS */ - -/* Enable libev io loop */ -/* #undef LWS_WITH_LIBEV */ - -/* Enable libuv io loop */ -#define LWS_WITH_LIBUV - -/* Enable libevent io loop */ -/* #undef LWS_WITH_LIBEVENT */ - -/* Build with support for ipv6 */ -/* #undef LWS_WITH_IPV6 */ - -/* Build with support for UNIX domain socket */ -/* #undef LWS_WITH_UNIX_SOCK */ - -/* Build with support for HTTP2 */ -/* #undef LWS_WITH_HTTP2 */ - -/* Turn on latency measuring code */ -/* #undef LWS_LATENCY */ - -/* Don't build the daemonizeation api */ -#define LWS_NO_DAEMONIZE - -/* Build without server support */ -/* #undef LWS_NO_SERVER */ - -/* Build without client support */ -/* #undef LWS_NO_CLIENT */ - -/* If we should compile with MinGW support */ -/* #undef LWS_MINGW_SUPPORT */ - -/* Use the BSD getifaddrs that comes with libwebsocket, for uclibc support */ -#define LWS_BUILTIN_GETIFADDRS - -/* use SHA1() not internal libwebsockets_SHA1 */ -/* #undef LWS_SHA1_USE_OPENSSL_NAME */ - -/* SSL server using ECDH certificate */ -/* #undef LWS_SSL_SERVER_WITH_ECDH_CERT */ -/* #undef LWS_HAVE_SSL_CTX_set1_param */ -#define LWS_HAVE_X509_VERIFY_PARAM_set1_host -#define LWS_HAVE_RSA_SET0_KEY - -/* #undef LWS_HAVE_UV_VERSION_H */ - -/* CGI apis */ -/* #undef LWS_WITH_CGI */ - -/* whether the Openssl is recent enough, and / or built with, ecdh */ -#define LWS_HAVE_OPENSSL_ECDH_H - -/* HTTP Proxy support */ -/* #undef LWS_WITH_HTTP_PROXY */ - -/* HTTP Ranges support */ -#define LWS_WITH_RANGES - -/* Http access log support */ -/* #undef LWS_WITH_ACCESS_LOG */ -/* #undef LWS_WITH_SERVER_STATUS */ - -/* #undef LWS_WITH_STATEFUL_URLDECODE */ -/* #undef LWS_WITH_PEER_LIMITS */ - -/* Maximum supported service threads */ -#define LWS_MAX_SMP 1 - -/* Lightweight JSON Parser */ -/* #undef LWS_WITH_LEJP */ - -/* SMTP */ -/* #undef LWS_WITH_SMTP */ - -/* OPTEE */ -/* #undef LWS_PLAT_OPTEE */ - -/* ZIP FOPS */ -#define LWS_WITH_ZIP_FOPS -#define LWS_HAVE_STDINT_H - -/* #undef LWS_AVOID_SIGPIPE_IGN */ - -#define LWS_FALLBACK_GETHOSTBYNAME - -/* #undef LWS_WITH_STATS */ -/* #undef LWS_WITH_SOCKS5 */ - -/* #undef LWS_HAVE_SYS_CAPABILITY_H */ -/* #undef LWS_HAVE_LIBCAP */ - -#define LWS_HAVE_ATOLL -#define LWS_HAVE__ATOI64 -#define LWS_HAVE__STAT32I64 - -/* OpenSSL various APIs */ - -#define LWS_HAVE_TLS_CLIENT_METHOD -#define LWS_HAVE_TLSV1_2_CLIENT_METHOD -#define LWS_HAVE_SSL_SET_INFO_CALLBACK - -#define LWS_HAS_INTPTR_T - - diff --git a/Kiwano/kiwano-audio.h b/kiwano-audio/kiwano-audio.h similarity index 92% rename from Kiwano/kiwano-audio.h rename to kiwano-audio/kiwano-audio.h index e5753889..5f287560 100644 --- a/Kiwano/kiwano-audio.h +++ b/kiwano-audio/kiwano-audio.h @@ -19,8 +19,8 @@ // THE SOFTWARE. #pragma once -#include "kiwano.h" +#include "kiwano/kiwano.h" -#include "audio/audio.h" -#include "audio/Sound.h" -#include "audio/Player.h" +#include "src/audio.h" +#include "src/Sound.h" +#include "src/Player.h" diff --git a/kiwano-audio/kiwano-audio.vcxproj b/kiwano-audio/kiwano-audio.vcxproj new file mode 100644 index 00000000..c089a6f6 --- /dev/null +++ b/kiwano-audio/kiwano-audio.vcxproj @@ -0,0 +1,186 @@ + + + + + + + + + + + + + + + + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {1B97937D-8184-426C-BE71-29A163DC76C9} + kiwano-audio + + + + StaticLibrary + true + Unicode + v100 + v110 + v120 + v140 + v141 + v142 + + + StaticLibrary + false + true + Unicode + v100 + v110 + v120 + v140 + v141 + v142 + + + StaticLibrary + true + Unicode + v100 + v110 + v120 + v140 + v141 + v142 + + + StaticLibrary + false + true + Unicode + v100 + v110 + v120 + v140 + v141 + v142 + + + + + + + + + + + + + + + + + + + + + $(ProjectDir)\output\$(Configuration).$(Platform)\ + $(ProjectDir)\build\$(Configuration).$(Platform)\ + true + + + $(ProjectDir)\output\$(Configuration).$(Platform)\ + $(ProjectDir)\build\$(Configuration).$(Platform)\ + true + + + $(ProjectDir)\output\$(Configuration).$(Platform)\ + $(ProjectDir)\build\$(Configuration).$(Platform)\ + false + + + $(ProjectDir)\output\$(Configuration).$(Platform)\ + $(ProjectDir)\build\$(Configuration).$(Platform)\ + false + + + + Level3 + Disabled + true + ../ + + + Windows + true + + + + + Level3 + Disabled + true + ../ + + + Windows + true + + + + + Level3 + MaxSpeed + true + true + false + true + ../ + + + Windows + true + true + true + + + + + Level3 + MaxSpeed + true + true + false + true + ../ + + + Windows + true + true + true + + + + + + \ No newline at end of file diff --git a/kiwano-audio/kiwano-audio.vcxproj.filters b/kiwano-audio/kiwano-audio.vcxproj.filters new file mode 100644 index 00000000..d82aacef --- /dev/null +++ b/kiwano-audio/kiwano-audio.vcxproj.filters @@ -0,0 +1,43 @@ + + + + + + src + + + src + + + src + + + src + + + src + + + + + src + + + src + + + src + + + src + + + src + + + + + {331b453a-33de-4e2a-9c06-19a3fc9d43f3} + + + \ No newline at end of file diff --git a/kiwano-audio/src/Player.cpp b/kiwano-audio/src/Player.cpp new file mode 100644 index 00000000..1c2f8506 --- /dev/null +++ b/kiwano-audio/src/Player.cpp @@ -0,0 +1,140 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// 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 "../kiwano-audio.h" +#include "Player.h" + +namespace kiwano +{ + namespace audio + { + Player::Player() + : volume_(1.f) + { + } + + Player::~Player() + { + ClearCache(); + } + + bool Player::Load(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return true; + + SoundPtr sound = new (std::nothrow) Sound(); + + if (sound) + { + if (sound->Load(res)) + { + sound->SetVolume(volume_); + sound_cache_.insert(std::make_pair(hash_code, sound)); + return true; + } + } + return false; + } + + void Player::Play(Resource const& res, int loop_count) + { + if (Load(res)) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Play(loop_count); + } + } + + void Player::Pause(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Pause(); + } + + void Player::Resume(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Resume(); + } + + void Player::Stop(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + sound_cache_[hash_code]->Stop(); + } + + bool Player::IsPlaying(Resource const& res) + { + size_t hash_code = res.GetHashCode(); + if (sound_cache_.end() != sound_cache_.find(hash_code)) + return sound_cache_[hash_code]->IsPlaying(); + return false; + } + + float Player::GetVolume() const + { + return volume_; + } + + void Player::SetVolume(float volume) + { + volume_ = std::min(std::max(volume, -224.f), 224.f); + for (const auto& pair : sound_cache_) + { + pair.second->SetVolume(volume_); + } + } + + void Player::PauseAll() + { + for (const auto& pair : sound_cache_) + { + pair.second->Pause(); + } + } + + void Player::ResumeAll() + { + for (const auto& pair : sound_cache_) + { + pair.second->Resume(); + } + } + + void Player::StopAll() + { + for (const auto& pair : sound_cache_) + { + pair.second->Stop(); + } + } + + void Player::ClearCache() + { + sound_cache_.clear(); + } + } +} \ No newline at end of file diff --git a/Kiwano/audio/Player.h b/kiwano-audio/src/Player.h similarity index 52% rename from Kiwano/audio/Player.h rename to kiwano-audio/src/Player.h index 3c157c82..032aa2c4 100644 --- a/Kiwano/audio/Player.h +++ b/kiwano-audio/src/Player.h @@ -22,72 +22,75 @@ namespace kiwano { - KGE_DECLARE_SMART_PTR(Player); - - // ÒôÀÖ²¥·ÅÆ÷ - class KGE_API Player - : protected Object + namespace audio { - using MusicMap = Map; + KGE_DECLARE_SMART_PTR(Player); - public: - Player(); + // ÒôÀÖ²¥·ÅÆ÷ + class KGE_API Player + : protected Object + { + using MusicMap = Map; - ~Player(); + public: + Player(); - // Ô¤¼ÓÔØÒôÀÖ×ÊÔ´ - bool Load( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + ~Player(); - // ²¥·ÅÒôÀÖ - void Play( - Resource const& res, /* ÒôÀÖ×ÊÔ´ */ - int loop_count = 0 /* ²¥·ÅÑ­»·´ÎÊý (-1 Ϊѭ»·²¥·Å) */ - ); + // Ô¤¼ÓÔØÒôÀÖ×ÊÔ´ + bool Load( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // ÔÝÍ£ÒôÀÖ - void Pause( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + // ²¥·ÅÒôÀÖ + void Play( + Resource const& res, /* ÒôÀÖ×ÊÔ´ */ + int loop_count = 0 /* ²¥·ÅÑ­»·´ÎÊý (-1 Ϊѭ»·²¥·Å) */ + ); - // ¼ÌÐø²¥·ÅÒôÀÖ - void Resume( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + // ÔÝÍ£ÒôÀÖ + void Pause( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // Í£Ö¹ÒôÀÖ - void Stop( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + // ¼ÌÐø²¥·ÅÒôÀÖ + void Resume( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // »ñÈ¡ÒôÀÖ²¥·Å״̬ - bool IsPlaying( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + // Í£Ö¹ÒôÀÖ + void Stop( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // »ñÈ¡ÒôÁ¿ - float GetVolume() const; + // »ñÈ¡ÒôÀÖ²¥·Å״̬ + bool IsPlaying( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // ÉèÖÃÒôÁ¿ - void SetVolume( - float volume /* 1.0 ΪԭʼÒôÁ¿ */ - ); + // »ñÈ¡ÒôÁ¿ + float GetVolume() const; - // ÔÝÍ£ËùÓÐÒôÀÖ - void PauseAll(); + // ÉèÖÃÒôÁ¿ + void SetVolume( + float volume /* 1.0 ΪԭʼÒôÁ¿ */ + ); - // ¼ÌÐø²¥·ÅËùÓÐÒôÀÖ - void ResumeAll(); + // ÔÝÍ£ËùÓÐÒôÀÖ + void PauseAll(); - // Í£Ö¹ËùÓÐÒôÀÖ - void StopAll(); + // ¼ÌÐø²¥·ÅËùÓÐÒôÀÖ + void ResumeAll(); - // Çå³ý»º´æ - void ClearCache(); + // Í£Ö¹ËùÓÐÒôÀÖ + void StopAll(); - protected: - float volume_; - MusicMap sound_cache_; - }; + // Çå³ý»º´æ + void ClearCache(); + + protected: + float volume_; + MusicMap sound_cache_; + }; + } } diff --git a/kiwano-audio/src/Sound.cpp b/kiwano-audio/src/Sound.cpp new file mode 100644 index 00000000..70e25190 --- /dev/null +++ b/kiwano-audio/src/Sound.cpp @@ -0,0 +1,223 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// 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 "../kiwano-audio.h" +#include "Sound.h" +#include "Transcoder.h" + +namespace kiwano +{ + namespace audio + { + + Sound::Sound() + : opened_(false) + , playing_(false) + , size_(0) + , wave_data_(nullptr) + , voice_(nullptr) + { + } + + Sound::Sound(Resource const& res) + : Sound() + { + Load(res); + } + + Sound::~Sound() + { + Close(); + } + + bool Sound::Load(Resource const& res) + { + if (opened_) + { + Close(); + } + + HRESULT hr = S_OK; + Transcoder transcoder; + + if (res.IsFileType()) + { +#if defined(KGE_DEBUG) + if (!FileUtil::ExistsFile(res.GetFileName())) + { + KGE_WARNING_LOG(L"Media file '%s' not found", res.GetFileName().c_str()); + return false; + } +#endif + hr = transcoder.LoadMediaFile(res.GetFileName(), &wave_data_, &size_); + } + else + { + hr = transcoder.LoadMediaResource(res, &wave_data_, &size_); + } + + if (FAILED(hr)) + { + KGE_ERROR_LOG(L"Load media file failed with HRESULT of %08X", hr); + return false; + } + + hr = Audio::Instance().CreateVoice(&voice_, transcoder.GetWaveFormatEx()); + if (FAILED(hr)) + { + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + KGE_ERROR_LOG(L"Create source voice failed with HRESULT of %08X", hr); + return false; + } + + opened_ = true; + return true; + } + + void Sound::Play(int loop_count) + { + if (!opened_) + { + KGE_ERROR_LOG(L"Sound must be opened first!"); + return; + } + + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + // if sound stream is not empty, stop() will clear it + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + if (state.BuffersQueued) + Stop(); + + // clamp loop count + loop_count = (loop_count < 0) ? XAUDIO2_LOOP_INFINITE : std::min(loop_count, XAUDIO2_LOOP_INFINITE - 1); + + XAUDIO2_BUFFER buffer = { 0 }; + buffer.pAudioData = wave_data_; + buffer.Flags = XAUDIO2_END_OF_STREAM; + buffer.AudioBytes = size_; + buffer.LoopCount = static_cast(loop_count); + + HRESULT hr = voice_->SubmitSourceBuffer(&buffer); + if (SUCCEEDED(hr)) + { + hr = voice_->Start(); + } + + if (FAILED(hr)) + { + KGE_ERROR_LOG(L"Submitting source buffer failed with HRESULT of %08X", hr); + } + + playing_ = SUCCEEDED(hr); + } + + void Sound::Pause() + { + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Stop())) + playing_ = false; + } + + void Sound::Resume() + { + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + if (SUCCEEDED(voice_->Start())) + playing_ = true; + } + + void Sound::Stop() + { + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + HRESULT hr = voice_->Stop(); + + if (SUCCEEDED(hr)) + hr = voice_->ExitLoop(); + + if (SUCCEEDED(hr)) + hr = voice_->FlushSourceBuffers(); + + if (SUCCEEDED(hr)) + playing_ = false; + } + + void Sound::Close() + { + if (voice_) + { + voice_->Stop(); + voice_->FlushSourceBuffers(); + voice_->DestroyVoice(); + voice_ = nullptr; + } + + if (wave_data_) + { + delete[] wave_data_; + wave_data_ = nullptr; + } + + opened_ = false; + playing_ = false; + } + + bool Sound::IsPlaying() const + { + if (opened_) + { + if (!voice_) + return false; + + XAUDIO2_VOICE_STATE state; + voice_->GetState(&state); + UINT32 buffers_queued = state.BuffersQueued; + + if (buffers_queued && playing_) + return true; + } + return false; + } + + float Sound::GetVolume() const + { + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + float volume = 0.0f; + voice_->GetVolume(&volume); + return volume; + } + + void Sound::SetVolume(float volume) + { + KGE_ASSERT(voice_ != nullptr && "IXAudio2SourceVoice* is NULL"); + + volume = std::min(std::max(volume, -224.f), 224.f); + voice_->SetVolume(volume); + } + } +} \ No newline at end of file diff --git a/Kiwano/audio/Sound.h b/kiwano-audio/src/Sound.h similarity index 59% rename from Kiwano/audio/Sound.h rename to kiwano-audio/src/Sound.h index a7ae2b1e..cc9c3043 100644 --- a/Kiwano/audio/Sound.h +++ b/kiwano-audio/src/Sound.h @@ -23,59 +23,62 @@ namespace kiwano { - KGE_DECLARE_SMART_PTR(Sound); - - // ÒôÀÖ¶ÔÏó - class KGE_API Sound - : public Object + namespace audio { - public: - Sound(); + KGE_DECLARE_SMART_PTR(Sound); - Sound( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + // ÒôÀÖ¶ÔÏó + class KGE_API Sound + : public Object + { + public: + Sound(); - virtual ~Sound(); + Sound( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // ´ò¿ªÒôÀÖ×ÊÔ´ - bool Load( - Resource const& res /* ÒôÀÖ×ÊÔ´ */ - ); + virtual ~Sound(); - // ²¥·Å - void Play( - int loop_count = 0 /* ²¥·ÅÑ­»·´ÎÊý (-1 Ϊѭ»·²¥·Å) */ - ); + // ´ò¿ªÒôÀÖ×ÊÔ´ + bool Load( + Resource const& res /* ÒôÀÖ×ÊÔ´ */ + ); - // ÔÝÍ£ - void Pause(); + // ²¥·Å + void Play( + int loop_count = 0 /* ²¥·ÅÑ­»·´ÎÊý (-1 Ϊѭ»·²¥·Å) */ + ); - // ¼ÌÐø - void Resume(); + // ÔÝÍ£ + void Pause(); - // Í£Ö¹ - void Stop(); + // ¼ÌÐø + void Resume(); - // ¹Ø±Õ²¢»ØÊÕ×ÊÔ´ - void Close(); + // Í£Ö¹ + void Stop(); - // ÊÇ·ñÕýÔÚ²¥·Å - bool IsPlaying() const; + // ¹Ø±Õ²¢»ØÊÕ×ÊÔ´ + void Close(); - // »ñÈ¡ÒôÁ¿ - float GetVolume() const; + // ÊÇ·ñÕýÔÚ²¥·Å + bool IsPlaying() const; - // ÉèÖÃÒôÁ¿ - void SetVolume( - float volume /* 1 ΪԭʼÒôÁ¿, ´óÓÚ 1 Ϊ·Å´óÒôÁ¿, 0 Ϊ×îСÒôÁ¿ */ - ); + // »ñÈ¡ÒôÁ¿ + float GetVolume() const; - protected: - bool opened_; - bool playing_; - UINT32 size_; - BYTE* wave_data_; - IXAudio2SourceVoice* voice_; - }; + // ÉèÖÃÒôÁ¿ + void SetVolume( + float volume /* 1 ΪԭʼÒôÁ¿, ´óÓÚ 1 Ϊ·Å´óÒôÁ¿, 0 Ϊ×îСÒôÁ¿ */ + ); + + protected: + bool opened_; + bool playing_; + UINT32 size_; + BYTE* wave_data_; + IXAudio2SourceVoice* voice_; + }; + } } diff --git a/kiwano-audio/src/Transcoder.cpp b/kiwano-audio/src/Transcoder.cpp new file mode 100644 index 00000000..1b1d6b88 --- /dev/null +++ b/kiwano-audio/src/Transcoder.cpp @@ -0,0 +1,271 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// 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 INITGUID +# define INITGUID // MFAudioFormat_PCM, MF_MT_MAJOR_TYPE, MF_MT_SUBTYPE, MFMediaType_Audio +#endif + +#include "../kiwano-audio.h" +#include "Transcoder.h" +#include "audio-modules.h" + +namespace kiwano +{ + namespace audio + { + + Transcoder::Transcoder() + : wave_format_(nullptr) + { + } + + Transcoder::~Transcoder() + { + if (wave_format_) + { + ::CoTaskMemFree(wave_format_); + wave_format_ = nullptr; + } + } + + const WAVEFORMATEX* Transcoder::GetWaveFormatEx() const + { + return wave_format_; + } + + HRESULT Transcoder::LoadMediaFile(String const& file_path, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + + ComPtr reader; + + hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromURL( + file_path.c_str(), + nullptr, + &reader + ); + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.Get(), wave_data, wave_data_size); + } + + return hr; + } + + HRESULT Transcoder::LoadMediaResource(Resource const& res, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + + ComPtr stream; + ComPtr byte_stream; + ComPtr reader; + + LPVOID buffer; + DWORD buffer_size; + if (!res.Load(buffer, buffer_size)) { return false; } + + stream = kiwano::modules::Shlwapi::Get().SHCreateMemStream( + static_cast(buffer), + static_cast(buffer_size) + ); + + if (stream == nullptr) + { + KGE_ERROR_LOG(L"SHCreateMemStream failed"); + return E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + hr = modules::MediaFoundation::Get().MFCreateMFByteStreamOnStream(stream.Get(), &byte_stream); + } + + if (SUCCEEDED(hr)) + { + hr = modules::MediaFoundation::Get().MFCreateSourceReaderFromByteStream( + byte_stream.Get(), + nullptr, + &reader + ); + } + + if (SUCCEEDED(hr)) + { + hr = ReadSource(reader.Get(), wave_data, wave_data_size); + } + + return hr; + } + + HRESULT Transcoder::ReadSource(IMFSourceReader* reader, BYTE** wave_data, UINT32* wave_data_size) + { + HRESULT hr = S_OK; + DWORD max_stream_size = 0; + + ComPtr partial_type; + ComPtr uncompressed_type; + + hr = modules::MediaFoundation::Get().MFCreateMediaType(&partial_type); + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio); + } + + if (SUCCEEDED(hr)) + { + hr = partial_type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM); + } + + // ÉèÖà source reader µÄýÌåÀàÐÍ£¬Ëü½«Ê¹ÓúÏÊʵĽâÂëÆ÷È¥½âÂëÕâ¸öÒôƵ + if (SUCCEEDED(hr)) + { + hr = reader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + partial_type.Get() + ); + } + + // ´Ó IMFMediaType ÖлñÈ¡ WAVEFORMAT ½á¹¹ + if (SUCCEEDED(hr)) + { + hr = reader->GetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + &uncompressed_type + ); + } + + // Ö¸¶¨ÒôƵÁ÷ + if (SUCCEEDED(hr)) + { + hr = reader->SetStreamSelection( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + true + ); + } + + // »ñÈ¡ WAVEFORMAT Êý¾Ý + if (SUCCEEDED(hr)) + { + UINT32 size = 0; + hr = modules::MediaFoundation::Get().MFCreateWaveFormatExFromMFMediaType( + uncompressed_type.Get(), + &wave_format_, + &size, + (DWORD)MFWaveFormatExConvertFlag_Normal + ); + } + + // ¹ÀËãÒôƵÁ÷´óС + if (SUCCEEDED(hr)) + { + PROPVARIANT prop; + PropVariantInit(&prop); + + hr = reader->GetPresentationAttribute( + (DWORD)MF_SOURCE_READER_MEDIASOURCE, + MF_PD_DURATION, + &prop + ); + + LONGLONG duration = prop.uhVal.QuadPart; + max_stream_size = static_cast( + (duration * wave_format_->nAvgBytesPerSec) / 10000000 + 1 + ); + PropVariantClear(&prop); + } + + // ¶ÁÈ¡ÒôƵÊý¾Ý + if (SUCCEEDED(hr)) + { + DWORD flags = 0; + DWORD position = 0; + BYTE* data = new (std::nothrow) BYTE[max_stream_size]; + + ComPtr sample; + ComPtr buffer; + + if (data == nullptr) + { + KGE_ERROR_LOG(L"Low memory"); + hr = E_OUTOFMEMORY; + } + else + { + while (true) + { + hr = reader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, + 0, + nullptr, + &flags, + nullptr, + &sample + ); + + if (flags & MF_SOURCE_READERF_ENDOFSTREAM) { break; } + + if (sample == nullptr) { continue; } + + if (SUCCEEDED(hr)) + { + hr = sample->ConvertToContiguousBuffer(&buffer); + + if (SUCCEEDED(hr)) + { + BYTE* audio_data = nullptr; + DWORD sample_buffer_length = 0; + + hr = buffer->Lock( + &audio_data, + nullptr, + &sample_buffer_length + ); + + if (SUCCEEDED(hr)) + { + for (DWORD i = 0; i < sample_buffer_length; i++) + { + data[position++] = audio_data[i]; + } + hr = buffer->Unlock(); + } + } + buffer = nullptr; + } + sample = nullptr; + + if (FAILED(hr)) { break; } + } + + if (SUCCEEDED(hr)) + { + *wave_data = data; + *wave_data_size = position; + } + } + } + + return hr; + } + } +} \ No newline at end of file diff --git a/Kiwano/audio/Transcoder.h b/kiwano-audio/src/Transcoder.h similarity index 70% rename from Kiwano/audio/Transcoder.h rename to kiwano-audio/src/Transcoder.h index b8582613..3b96b498 100644 --- a/Kiwano/audio/Transcoder.h +++ b/kiwano-audio/src/Transcoder.h @@ -25,33 +25,38 @@ namespace kiwano { - class KGE_API Transcoder + namespace audio { - WAVEFORMATEX* wave_format_; - public: - Transcoder(); + class KGE_API Transcoder + { + public: + Transcoder(); - ~Transcoder(); + ~Transcoder(); - const WAVEFORMATEX* GetWaveFormatEx() const; + const WAVEFORMATEX* GetWaveFormatEx() const; - HRESULT LoadMediaFile( - String const& file_path, - BYTE** wave_data, - UINT32* wave_data_size - ); + HRESULT LoadMediaFile( + String const& file_path, + BYTE** wave_data, + UINT32* wave_data_size + ); - HRESULT LoadMediaResource( - Resource const& res, - BYTE** wave_data, - UINT32* wave_data_size - ); + HRESULT LoadMediaResource( + Resource const& res, + BYTE** wave_data, + UINT32* wave_data_size + ); - HRESULT ReadSource( - IMFSourceReader* reader, - BYTE** wave_data, - UINT32* wave_data_size - ); - }; + HRESULT ReadSource( + IMFSourceReader* reader, + BYTE** wave_data, + UINT32* wave_data_size + ); + + private: + WAVEFORMATEX* wave_format_; + }; + } } diff --git a/kiwano-audio/src/audio-modules.cpp b/kiwano-audio/src/audio-modules.cpp new file mode 100644 index 00000000..1e25dcf2 --- /dev/null +++ b/kiwano-audio/src/audio-modules.cpp @@ -0,0 +1,100 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// 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 "../kiwano-audio.h" +#include "audio-modules.h" + +namespace kiwano +{ + namespace audio + { + namespace modules + { + XAudio2::XAudio2() + { + const auto xaudio2_dll_names = + { + L"xaudio2_9.dll", // for Windows 10 + L"xaudio2_8.dll", // for Windows 8 + L"xaudio2_7.dll" // for DirectX SDK + }; + + for (const auto& name : xaudio2_dll_names) + { + xaudio2 = LoadLibraryW(name); + if (xaudio2) + { + XAudio2Create = (PFN_XAudio2Create) + GetProcAddress(xaudio2, "XAudio2Create"); + break; + } + } + + if (!xaudio2) + { + KGE_ERROR_LOG(L"Load xaudio2.dll failed"); + throw std::runtime_error("Load xaudio2.dll failed"); + } + } + + MediaFoundation::MediaFoundation() + { + mfplat = LoadLibraryW(L"Mfplat.dll"); + if (mfplat) + { + MFStartup = (PFN_MFStartup) + GetProcAddress(mfplat, "MFStartup"); + + MFShutdown = (PFN_MFShutdown) + GetProcAddress(mfplat, "MFShutdown"); + + MFCreateMediaType = (PFN_MFCreateMediaType) + GetProcAddress(mfplat, "MFCreateMediaType"); + + MFCreateWaveFormatExFromMFMediaType = (PFN_MFCreateWaveFormatExFromMFMediaType) + GetProcAddress(mfplat, "MFCreateWaveFormatExFromMFMediaType"); + + MFCreateMFByteStreamOnStream = (PFN_MFCreateMFByteStreamOnStream) + GetProcAddress(mfplat, "MFCreateMFByteStreamOnStream"); + } + else + { + KGE_LOG(L"Load Mfplat.dll failed"); + throw std::runtime_error("Load Mfplat.dll failed"); + } + + mfreadwrite = LoadLibraryW(L"Mfreadwrite.dll"); + if (mfreadwrite) + { + MFCreateSourceReaderFromURL = (PFN_MFCreateSourceReaderFromURL) + GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromURL"); + + MFCreateSourceReaderFromByteStream = (PFN_MFCreateSourceReaderFromByteStream) + GetProcAddress(mfreadwrite, "MFCreateSourceReaderFromByteStream"); + } + else + { + KGE_LOG(L"Load Mfreadwrite.dll failed"); + throw std::runtime_error("Load Mfreadwrite.dll failed"); + } + } + } + } +} \ No newline at end of file diff --git a/kiwano-audio/src/audio-modules.h b/kiwano-audio/src/audio-modules.h new file mode 100644 index 00000000..689ba0dd --- /dev/null +++ b/kiwano-audio/src/audio-modules.h @@ -0,0 +1,86 @@ +// Copyright (c) 2016-2018 Kiwano - Nomango +// +// 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. + +#pragma once +#include +#include +#include +#include + +namespace kiwano +{ + namespace audio + { + namespace modules + { + class KGE_API XAudio2 + { + XAudio2(); + + HMODULE xaudio2; + + // XAudio2 functions + typedef HRESULT(WINAPI* PFN_XAudio2Create)(IXAudio2**, UINT32, XAUDIO2_PROCESSOR); + + public: + static inline XAudio2& Get() + { + static XAudio2 instance; + return instance; + } + + PFN_XAudio2Create XAudio2Create; + }; + + + class KGE_API MediaFoundation + { + MediaFoundation(); + + HMODULE mfplat; + HMODULE mfreadwrite; + + // MediaFoundation functions + typedef HRESULT(WINAPI* PFN_MFStartup)(ULONG, DWORD); + typedef HRESULT(WINAPI* PFN_MFShutdown)(); + typedef HRESULT(WINAPI* PFN_MFCreateMediaType)(IMFMediaType**); + typedef HRESULT(WINAPI* PFN_MFCreateWaveFormatExFromMFMediaType)(IMFMediaType*, WAVEFORMATEX**, UINT32*, UINT32); + typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromURL)(LPCWSTR, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI* PFN_MFCreateSourceReaderFromByteStream)(IMFByteStream*, IMFAttributes*, IMFSourceReader**); + typedef HRESULT(WINAPI* PFN_MFCreateMFByteStreamOnStream)(IStream*, IMFByteStream**); + + public: + static inline MediaFoundation& Get() + { + static MediaFoundation instance; + return instance; + } + + PFN_MFStartup MFStartup; + PFN_MFShutdown MFShutdown; + PFN_MFCreateMediaType MFCreateMediaType; + PFN_MFCreateWaveFormatExFromMFMediaType MFCreateWaveFormatExFromMFMediaType; + PFN_MFCreateSourceReaderFromURL MFCreateSourceReaderFromURL; + PFN_MFCreateSourceReaderFromByteStream MFCreateSourceReaderFromByteStream; + PFN_MFCreateMFByteStreamOnStream MFCreateMFByteStreamOnStream; + }; + } + } +} diff --git a/Kiwano/audio/audio.cpp b/kiwano-audio/src/audio.cpp similarity index 51% rename from Kiwano/audio/audio.cpp rename to kiwano-audio/src/audio.cpp index 9a6a9606..45669ed2 100644 --- a/Kiwano/audio/audio.cpp +++ b/kiwano-audio/src/audio.cpp @@ -24,73 +24,75 @@ namespace kiwano { - Audio::Audio() - : x_audio2_(nullptr) - , mastering_voice_(nullptr) + namespace audio { - } - - Audio::~Audio() - { - } - - void Audio::SetupComponent(Application*) - { - KGE_LOG(L"Creating audio resources"); - - HRESULT hr = modules::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); - - if (SUCCEEDED(hr)) + Audio::Audio() + : x_audio2_(nullptr) + , mastering_voice_(nullptr) { - hr = modules::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR); } - if (SUCCEEDED(hr)) + Audio::~Audio() { - hr = x_audio2_->CreateMasteringVoice(&mastering_voice_); } - ThrowIfFailed(hr); - } - - void Audio::DestroyComponent() - { - KGE_LOG(L"Destroying audio resources"); - - if (mastering_voice_) + void Audio::SetupComponent(Application*) { - mastering_voice_->DestroyVoice(); - mastering_voice_ = nullptr; + KGE_LOG(L"Creating audio resources"); + + HRESULT hr = modules::MediaFoundation::Get().MFStartup(MF_VERSION, MFSTARTUP_FULL); + + if (SUCCEEDED(hr)) + { + hr = modules::XAudio2::Get().XAudio2Create(&x_audio2_, 0, XAUDIO2_DEFAULT_PROCESSOR); + } + + if (SUCCEEDED(hr)) + { + hr = x_audio2_->CreateMasteringVoice(&mastering_voice_); + } + + ThrowIfFailed(hr); } - if (x_audio2_) + void Audio::DestroyComponent() { - x_audio2_->Release(); - x_audio2_ = nullptr; + KGE_LOG(L"Destroying audio resources"); + + if (mastering_voice_) + { + mastering_voice_->DestroyVoice(); + mastering_voice_ = nullptr; + } + + if (x_audio2_) + { + x_audio2_->Release(); + x_audio2_ = nullptr; + } + + modules::MediaFoundation::Get().MFShutdown(); } - modules::MediaFoundation::Get().MFShutdown(); - } - - HRESULT Audio::CreateVoice(IXAudio2SourceVoice** voice, const WAVEFORMATEX* wfx) - { - if (voice == nullptr) + HRESULT Audio::CreateVoice(IXAudio2SourceVoice** voice, const WAVEFORMATEX* wfx) { - return E_UNEXPECTED; + if (voice == nullptr) + { + return E_UNEXPECTED; + } + + HRESULT hr = x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO); + return hr; } - HRESULT hr = x_audio2_->CreateSourceVoice(voice, wfx, 0, XAUDIO2_DEFAULT_FREQ_RATIO); - return hr; - } + void Audio::Open() + { + x_audio2_->StartEngine(); + } - void Audio::Open() - { - x_audio2_->StartEngine(); + void Audio::Close() + { + x_audio2_->StopEngine(); + } } - - void Audio::Close() - { - x_audio2_->StopEngine(); - } - } \ No newline at end of file diff --git a/Kiwano/audio/audio.h b/kiwano-audio/src/audio.h similarity index 69% rename from Kiwano/audio/audio.h rename to kiwano-audio/src/audio.h index b31ec309..89e718b6 100644 --- a/Kiwano/audio/audio.h +++ b/kiwano-audio/src/audio.h @@ -22,35 +22,38 @@ namespace kiwano { - class KGE_API Audio - : public Singleton