From 1544a2a8f6cd5f051458e0104afff5b9edc3bc1f Mon Sep 17 00:00:00 2001 From: James Chen Date: Wed, 8 May 2013 15:17:54 +0800 Subject: [PATCH 01/10] CMAKE iOS ok. --- CMakeLists.txt | 18 +- ios/build_framework.py | 141 +++++++++++++++ ios/cmake/Modules/Platform/iOS.cmake | 163 ++++++++++++++++++ .../Toolchains/Toolchain-iPhoneOS_Xcode.cmake | 31 ++++ .../Toolchain-iPhoneSimulator_Xcode.cmake | 31 ++++ lib/libwebsockets.c | 2 +- 6 files changed, 376 insertions(+), 10 deletions(-) create mode 100755 ios/build_framework.py create mode 100644 ios/cmake/Modules/Platform/iOS.cmake create mode 100644 ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake create mode 100644 ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index abc0dded1c..88c45b099c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,10 +323,10 @@ add_library(websockets STATIC ${HDR_PRIVATE} ${HDR_PUBLIC} ${SOURCES}) -add_library(websockets_shared SHARED - ${HDR_PRIVATE} - ${HDR_PUBLIC} - ${SOURCES}) +#add_library(websockets_shared SHARED +# ${HDR_PRIVATE} +# ${HDR_PUBLIC} +# ${SOURCES}) if (WIN32) # On Windows libs have the same file ending (.lib) @@ -347,9 +347,9 @@ endif(WIN32) # We want the shared lib to be named "libwebsockets" # not "libwebsocket_shared". -set_target_properties(websockets_shared - PROPERTIES - OUTPUT_NAME websockets) +# set_target_properties(websockets_shared +# PROPERTIES +# OUTPUT_NAME websockets) # Set the so version of the lib. # Equivalent to LDFLAGS=-version-info x:x:x @@ -461,7 +461,7 @@ if (UNIX) endif() # Setup the linking for all libs. -foreach (lib websockets websockets_shared) +foreach (lib websockets) target_link_libraries(${lib} ${LIB_LIST}) endforeach() @@ -726,7 +726,7 @@ install(FILES ${HDR_PUBLIC} set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Header files") # Install libs. -install(TARGETS websockets websockets_shared +install(TARGETS websockets LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT libraries) diff --git a/ios/build_framework.py b/ios/build_framework.py new file mode 100755 index 0000000000..9195651473 --- /dev/null +++ b/ios/build_framework.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python +""" +The script builds OpenCV.framework for iOS. +The built framework is universal, it can be used to build app and run it on either iOS simulator or real device. + +Usage: + ./build_framework.py + +By cmake conventions (and especially if you work with OpenCV repository), +the output dir should not be a subdirectory of OpenCV source tree. + +Script will create , if it's missing, and a few its subdirectories: + + + build/ + iPhoneOS-*/ + [cmake-generated build tree for an iOS device target] + iPhoneSimulator/ + [cmake-generated build tree for iOS simulator] + opencv2.framework/ + [the framework content] + +The script should handle minor OpenCV updates efficiently +- it does not recompile the library from scratch each time. +However, opencv2.framework directory is erased and recreated on each run. +""" + +import glob, re, os, os.path, shutil, string, sys + +def build_opencv(srcroot, buildroot, target, arch): + "builds OpenCV for device or simulator" + + builddir = os.path.join(buildroot, target + '-' + arch) + if not os.path.isdir(builddir): + os.makedirs(builddir) + currdir = os.getcwd() + os.chdir(builddir) + # for some reason, if you do not specify CMAKE_BUILD_TYPE, it puts libs to "RELEASE" rather than "Release" + cmakeargs = ("-GXcode " + + "-DCMAKE_TOOLCHAIN_FILE=%s/ios/cmake/Toolchains/Toolchain-%s_Xcode.cmake " + + "-DWITH_SSL=0" + + "-DWITHOUT_SERVER=1 " + + "-DWITHOUT_TESTAPPS=1 " + + "-DWITHOUT_EXTENSIONS=1 " + + "-DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install") % (srcroot, target) + # if cmake cache exists, just rerun cmake to update OpenCV.xproj if necessary + if os.path.isfile(os.path.join(builddir, "CMakeCache.txt")): + os.system("cmake %s ." % (cmakeargs,)) + else: + os.system("cmake %s %s" % (cmakeargs, srcroot)) + + # for wlib in [builddir + "/modules/world/UninstalledProducts/libopencv_world.a", + # builddir + "/lib/Release/libopencv_world.a"]: + # if os.path.isfile(wlib): + # os.remove(wlib) + + os.system("xcodebuild -parallelizeTargets ARCHS=%s -jobs 8 -sdk %s -configuration Release -target ALL_BUILD" % (arch, target.lower())) + os.system("xcodebuild ARCHS=%s -sdk %s -configuration Release -target install install" % (arch, target.lower())) + os.chdir(currdir) + +def put_framework_together(srcroot, dstroot): + "constructs the framework directory after all the targets are built" + + # find the list of targets (basically, ["iPhoneOS", "iPhoneSimulator"]) + targetlist = glob.glob(os.path.join(dstroot, "build", "*")) + targetlist = [os.path.basename(t) for t in targetlist] + + # set the current dir to the dst root + currdir = os.getcwd() + framework_dir = dstroot + "/opencv2.framework" + if os.path.isdir(framework_dir): + shutil.rmtree(framework_dir) + os.makedirs(framework_dir) + os.chdir(framework_dir) + + # determine OpenCV version (without subminor part) + tdir0 = "../build/" + targetlist[0] + cfg = open(tdir0 + "/cvconfig.h", "rt") + for l in cfg.readlines(): + if l.startswith("#define VERSION"): + opencv_version = l[l.find("\"")+1:l.rfind(".")] + break + cfg.close() + + # form the directory tree + dstdir = "Versions/A" + os.makedirs(dstdir + "/Resources") + + # copy headers + shutil.copytree(tdir0 + "/install/include/opencv2", dstdir + "/Headers") + + # make universal static lib + wlist = " ".join(["../build/" + t + "/lib/Release/libopencv_world.a" for t in targetlist]) + os.system("lipo -create " + wlist + " -o " + dstdir + "/opencv2") + + # form Info.plist + srcfile = open(srcroot + "/ios/Info.plist.in", "rt") + dstfile = open(dstdir + "/Resources/Info.plist", "wt") + for l in srcfile.readlines(): + dstfile.write(l.replace("${VERSION}", opencv_version)) + srcfile.close() + dstfile.close() + + # copy cascades + # TODO ... + + # make symbolic links + os.symlink("A", "Versions/Current") + os.symlink("Versions/Current/Headers", "Headers") + os.symlink("Versions/Current/Resources", "Resources") + os.symlink("Versions/Current/opencv2", "opencv2") + + +def build_framework(srcroot, dstroot): + "main function to do all the work" + + targets = ["iPhoneOS", "iPhoneOS", "iPhoneSimulator"] + archs = ["armv7", "armv7s", "i386"] + for i in range(len(targets)): + build_opencv(srcroot, os.path.join(dstroot, "build"), targets[i], archs[i]) + + # put_framework_together(srcroot, dstroot) + print("srcroot:"+srcroot+",dstroot:"+dstroot) + build_root = dstroot+"/build" + lib_path = "/UninstalledProducts/libwebsockets.a " + lipo = "xcrun -sdk iphoneos lipo " + strip = "xcrun -sdk iphoneos strip " + os.system(lipo + "-create -output " + build_root+"/libwebsockets.a " + + build_root+"/iPhoneSimulator-i386"+lib_path + + build_root+"/iPhoneOS-armv7"+lib_path + + build_root+"/iPhoneOS-armv7s"+lib_path + ) + os.system(strip + "-S " + build_root+"/libwebsockets.a") + os.system(lipo + "-info " + build_root+"/libwebsockets.a") + +if __name__ == "__main__": + if len(sys.argv) != 2: + print "Usage:\n\t./build_framework.py \n\n" + sys.exit(0) + + build_framework(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")), os.path.abspath(sys.argv[1])) \ No newline at end of file diff --git a/ios/cmake/Modules/Platform/iOS.cmake b/ios/cmake/Modules/Platform/iOS.cmake new file mode 100644 index 0000000000..4d196e65d9 --- /dev/null +++ b/ios/cmake/Modules/Platform/iOS.cmake @@ -0,0 +1,163 @@ +# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake +# files which are included with CMake 2.8.4 +# It has been altered for iOS development +set (UNIX 1) +set (APPLE 1) +set (IOS 1) + +# Darwin versions: +# 6.x == Mac OSX 10.2 +# 7.x == Mac OSX 10.3 +# 8.x == Mac OSX 10.4 +# 9.x == Mac OSX 10.5 +# 10.x == Mac OSX 10.6 (Snow Leopard) +string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_SYSTEM_VERSION}") +string (REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\2" DARWIN_MINOR_VERSION "${CMAKE_SYSTEM_VERSION}") + +# Do not use the "-Wl,-search_paths_first" flag with the OSX 10.2 compiler. +# Done this way because it is too early to do a TRY_COMPILE. +if (NOT DEFINED HAVE_FLAG_SEARCH_PATHS_FIRST) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 0) + if ("${DARWIN_MAJOR_VERSION}" GREATER 6) + set (HAVE_FLAG_SEARCH_PATHS_FIRST 1) + endif ("${DARWIN_MAJOR_VERSION}" GREATER 6) +endif (NOT DEFINED HAVE_FLAG_SEARCH_PATHS_FIRST) +# More desirable, but does not work: +#INCLUDE(CheckCXXCompilerFlag) +#CHECK_CXX_COMPILER_FLAG("-Wl,-search_paths_first" HAVE_FLAG_SEARCH_PATHS_FIRST) + +set (CMAKE_SHARED_LIBRARY_PREFIX "lib") +set (CMAKE_SHARED_LIBRARY_SUFFIX ".dylib") +set (CMAKE_SHARED_MODULE_PREFIX "lib") +set (CMAKE_SHARED_MODULE_SUFFIX ".so") +set (CMAKE_MODULE_EXISTS 1) +set (CMAKE_DL_LIBS "") + +set (CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ") +set (CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ") +set (CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}") +set (CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}") + +# Hidden visibilty is required for cxx on iOS +set (CMAKE_C_FLAGS "") +set (CMAKE_CXX_FLAGS "-stdlib=libc++ -headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden") + +set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -fomit-frame-pointer -ffast-math") + +if (HAVE_FLAG_SEARCH_PATHS_FIRST) + set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}") + set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}") +endif (HAVE_FLAG_SEARCH_PATHS_FIRST) + +set (CMAKE_PLATFORM_HAS_INSTALLNAME 1) +set (CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names") +set (CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,") +set (CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,") +set (CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a") + +# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree +# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache +# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun) +# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex +if (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool) +endif (NOT DEFINED CMAKE_INSTALL_NAME_TOOL) + +# Setup iOS developer location +if (IPHONEOS) + set (_CMAKE_IOS_DEVELOPER_ROOT "/Developer/Platforms/iPhoneOS.platform/Developer") +else () + if (IPHONESIMULATOR) + set (_CMAKE_IOS_DEVELOPER_ROOT "/Developer/Platforms/iPhoneSimulator.platform/Developer") + endif () +endif () +# Find installed iOS SDKs +file (GLOB _CMAKE_IOS_SDKS "${_CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*") + +# Find and use the most recent iOS sdk +if (_CMAKE_IOS_SDKS) + list (SORT _CMAKE_IOS_SDKS) + list (REVERSE _CMAKE_IOS_SDKS) + list (GET _CMAKE_IOS_SDKS 0 _CMAKE_IOS_SDK_ROOT) + + # Set the sysroot default to the most recent SDK + set (CMAKE_OSX_SYSROOT ${_CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support") + + # set the architecture for iOS - this env var sets armv6,armv7 and appears to be XCode's standard. The other found is ARCHS_UNIVERSAL_IPHONE_OS but that is armv7 only + set (CMAKE_OSX_ARCHITECTURES "$(ARCHS_STANDARD_32_BIT)" CACHE string "Build architecture for iOS") + + # Set the default based on this file and not the environment variable + set (CMAKE_FIND_ROOT_PATH ${_CMAKE_IOS_DEVELOPER_ROOT} ${_CMAKE_IOS_SDK_ROOT} CACHE string "iOS library search path root") + + # default to searching for frameworks first + set (CMAKE_FIND_FRAMEWORK FIRST) + + # set up the default search directories for frameworks + set (CMAKE_SYSTEM_FRAMEWORK_PATH + ${_CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks + ${_CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks + ${_CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks + ) +endif (_CMAKE_IOS_SDKS) + +if ("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$") + set (CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -flat_namespace -undefined suppress") +endif ("${CMAKE_BACKWARDS_COMPATIBILITY}" MATCHES "^1\\.[0-6]$") + +if (NOT XCODE) + # Enable shared library versioning. This flag is not actually referenced + # but the fact that the setting exists will cause the generators to support + # soname computation. + set (CMAKE_SHARED_LIBRARY_SONAME_C_FLAG "-install_name") +endif (NOT XCODE) + +# Xcode does not support -isystem yet. +if (XCODE) + set (CMAKE_INCLUDE_SYSTEM_FLAG_C) + set (CMAKE_INCLUDE_SYSTEM_FLAG_CXX) +endif (XCODE) + +# Need to list dependent shared libraries on link line. When building +# with -isysroot (for universal binaries), the linker always looks for +# dependent libraries under the sysroot. Listing them on the link +# line works around the problem. +set (CMAKE_LINK_DEPENDENT_LIBRARY_FILES 1) + +set (CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) +set (CMAKE_CXX_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) + +set (CMAKE_C_CREATE_SHARED_LIBRARY + " -o -install_name ") +set (CMAKE_CXX_CREATE_SHARED_LIBRARY + " -o -install_name ") + +set (CMAKE_CXX_CREATE_SHARED_MODULE + " -o ") + +set (CMAKE_C_CREATE_SHARED_MODULE + " -o ") + +set (CMAKE_C_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") +set (CMAKE_CXX_CREATE_MACOSX_FRAMEWORK + " -o -install_name ") + + +# Add the install directory of the running cmake to the search directories +# CMAKE_ROOT is CMAKE_INSTALL_PREFIX/share/cmake, so we need to go two levels up +get_filename_component (_CMAKE_INSTALL_DIR "${CMAKE_ROOT}" PATH) +get_filename_component (_CMAKE_INSTALL_DIR "${_CMAKE_INSTALL_DIR}" PATH) + +# List common installation prefixes. These will be used for all search types +list (APPEND CMAKE_SYSTEM_PREFIX_PATH + # Standard + ${_CMAKE_IOS_DEVELOPER_ROOT}/usr + ${_CMAKE_IOS_SDK_ROOT}/usr + + # CMake install location + "${_CMAKE_INSTALL_DIR}" + + # Project install destination. + "${CMAKE_INSTALL_PREFIX}" +) \ No newline at end of file diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake new file mode 100644 index 0000000000..67343253bd --- /dev/null +++ b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake @@ -0,0 +1,31 @@ +message (STATUS "Setting up iPhoneOS toolchain") +set (IPHONEOS TRUE) + +# Standard settings +set (CMAKE_SYSTEM_NAME iOS) +# Include extra modules for the iOS platform files +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cmake/Modules") + +# Force the compilers to gcc for iOS +include (CMakeForceCompiler) +#CMAKE_FORCE_C_COMPILER (gcc gcc) +#CMAKE_FORCE_CXX_COMPILER (g++ g++) + +set (CMAKE_C_SIZEOF_DATA_PTR 4) +set (CMAKE_C_HAS_ISYSROOT 1) +set (CMAKE_C_COMPILER_ABI ELF) +set (CMAKE_CXX_SIZEOF_DATA_PTR 4) +set (CMAKE_CXX_HAS_ISYSROOT 1) +set (CMAKE_CXX_COMPILER_ABI ELF) + +# Skip the platform compiler checks for cross compiling +set (CMAKE_CXX_COMPILER_WORKS TRUE) +set (CMAKE_C_COMPILER_WORKS TRUE) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +message (STATUS "iPhoneOS toolchain loaded") \ No newline at end of file diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake new file mode 100644 index 0000000000..7ef8113edb --- /dev/null +++ b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake @@ -0,0 +1,31 @@ +message (STATUS "Setting up iPhoneSimulator toolchain") +set (IPHONESIMULATOR TRUE) + +# Standard settings +set (CMAKE_SYSTEM_NAME iOS) +# Include extra modules for the iOS platform files +set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cmake/Modules") + +# Force the compilers to gcc for iOS +include (CMakeForceCompiler) +#CMAKE_FORCE_C_COMPILER (gcc gcc) +#CMAKE_FORCE_CXX_COMPILER (g++ g++) + +set (CMAKE_C_SIZEOF_DATA_PTR 4) +set (CMAKE_C_HAS_ISYSROOT 1) +set (CMAKE_C_COMPILER_ABI ELF) +set (CMAKE_CXX_SIZEOF_DATA_PTR 4) +set (CMAKE_CXX_HAS_ISYSROOT 1) +set (CMAKE_CXX_COMPILER_ABI ELF) + +# Skip the platform compiler checks for cross compiling +set (CMAKE_CXX_COMPILER_WORKS TRUE) +set (CMAKE_C_COMPILER_WORKS TRUE) + +# Search for programs in the build host directories +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) +# for libraries and headers in the target directories +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +message (STATUS "iPhoneSimulator toolchain loaded") \ No newline at end of file diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 983af5e2cb..97d911baa6 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1506,7 +1506,7 @@ lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi, #ifdef LWS_NO_SERVER int -_libwebsocket_rx_flow_control(struct libswebsocket *wsi) +_libwebsocket_rx_flow_control(struct libwebsocket *wsi) { return 0; } From 0615f48109fff56f00e26ed62b33212e4a7ce684 Mon Sep 17 00:00:00 2001 From: James Chen Date: Wed, 8 May 2013 18:23:25 +0800 Subject: [PATCH 02/10] bug fixes. --- CMakeLists.txt | 18 +++++++++--------- ios/build_framework.py | 3 +-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88c45b099c..b1ee45683b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,7 +309,7 @@ endif(UNIX) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set( CMAKE_C_FLAGS "-Wall -Werror -O4 -fvisibility=hidden " ) + set( CMAKE_C_FLAGS "-Wall -O4 -fvisibility=hidden " ) endif () source_group("Headers Private" FILES ${HDR_PRIVATE}) @@ -353,13 +353,13 @@ endif(WIN32) # Set the so version of the lib. # Equivalent to LDFLAGS=-version-info x:x:x -if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - foreach(lib websockets websockets_shared) - set_target_properties(${lib} - PROPERTIES - SOVERSION ${SOVERSION}) - endforeach() -endif() +# if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) +# foreach(lib websockets websockets_shared) +# set_target_properties(${lib} +# PROPERTIES +# SOVERSION ${SOVERSION}) +# endforeach() +# endif() set(LIB_LIST) @@ -407,7 +407,7 @@ if (NOT WITHOUT_EXTENSIONS) endif() # Make sure ZLib is compiled before the libs. - foreach (lib websockets websockets_shared) + foreach (lib websockets) add_dependencies(${lib} ZLIB) endforeach() diff --git a/ios/build_framework.py b/ios/build_framework.py index 9195651473..352c503078 100755 --- a/ios/build_framework.py +++ b/ios/build_framework.py @@ -38,10 +38,9 @@ def build_opencv(srcroot, buildroot, target, arch): # for some reason, if you do not specify CMAKE_BUILD_TYPE, it puts libs to "RELEASE" rather than "Release" cmakeargs = ("-GXcode " + "-DCMAKE_TOOLCHAIN_FILE=%s/ios/cmake/Toolchains/Toolchain-%s_Xcode.cmake " + - "-DWITH_SSL=0" + + "-DWITH_SSL=0 " + "-DWITHOUT_SERVER=1 " + "-DWITHOUT_TESTAPPS=1 " + - "-DWITHOUT_EXTENSIONS=1 " + "-DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install") % (srcroot, target) # if cmake cache exists, just rerun cmake to update OpenCV.xproj if necessary if os.path.isfile(os.path.join(builddir, "CMakeCache.txt")): From f77b8b6b23cbaa12c185a3db58b8e82718d9985e Mon Sep 17 00:00:00 2001 From: James Chen Date: Thu, 30 May 2013 18:59:56 +0800 Subject: [PATCH 03/10] Add android script. --- android/android.toolchain.cmake | 1746 ++++++++++++++++++ android/scripts/ABI_compat_generator.py | 228 +++ android/scripts/build.cmd | 90 + android/scripts/camera_build.conf | 23 + android/scripts/cmake_android.cmd | 5 + android/scripts/cmake_android.sh | 8 + android/scripts/cmake_android_all_cameras.py | 80 + android/scripts/cmake_android_armeabi.sh | 29 + android/scripts/cmake_android_mips.sh | 8 + android/scripts/cmake_android_neon.sh | 8 + android/scripts/cmake_android_service.sh | 7 + android/scripts/cmake_android_x86.sh | 9 + android/scripts/wincfg.cmd.tmpl | 30 + 13 files changed, 2271 insertions(+) create mode 100644 android/android.toolchain.cmake create mode 100755 android/scripts/ABI_compat_generator.py create mode 100644 android/scripts/build.cmd create mode 100644 android/scripts/camera_build.conf create mode 100644 android/scripts/cmake_android.cmd create mode 100755 android/scripts/cmake_android.sh create mode 100755 android/scripts/cmake_android_all_cameras.py create mode 100755 android/scripts/cmake_android_armeabi.sh create mode 100755 android/scripts/cmake_android_mips.sh create mode 100755 android/scripts/cmake_android_neon.sh create mode 100755 android/scripts/cmake_android_service.sh create mode 100755 android/scripts/cmake_android_x86.sh create mode 100644 android/scripts/wincfg.cmd.tmpl diff --git a/android/android.toolchain.cmake b/android/android.toolchain.cmake new file mode 100644 index 0000000000..e9a3c01d5e --- /dev/null +++ b/android/android.toolchain.cmake @@ -0,0 +1,1746 @@ +# Copyright (c) 2010-2011, Ethan Rublee +# Copyright (c) 2011-2013, Andrey Kamaev +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# 3. The name of the copyright holders may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +# ------------------------------------------------------------------------------ +# Android CMake toolchain file, for use with the Android NDK r5-r8 +# Requires cmake 2.6.3 or newer (2.8.5 or newer is recommended). +# See home page: https://github.com/taka-no-me/android-cmake +# +# The file is mantained by the OpenCV project. The latest version can be get at +# http://code.opencv.org/projects/opencv/repository/revisions/master/changes/android/android.toolchain.cmake +# +# Usage Linux: +# $ export ANDROID_NDK=/absolute/path/to/the/android-ndk +# $ mkdir build && cd build +# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. +# $ make -j8 +# +# Usage Linux (using standalone toolchain): +# $ export ANDROID_STANDALONE_TOOLCHAIN=/absolute/path/to/android-toolchain +# $ mkdir build && cd build +# $ cmake -DCMAKE_TOOLCHAIN_FILE=path/to/the/android.toolchain.cmake .. +# $ make -j8 +# +# Usage Windows: +# You need native port of make to build your project. +# Android NDK r7 (or newer) already has make.exe on board. +# For older NDK you have to install it separately. +# For example, this one: http://gnuwin32.sourceforge.net/packages/make.htm +# +# $ SET ANDROID_NDK=C:\absolute\path\to\the\android-ndk +# $ mkdir build && cd build +# $ cmake.exe -G"MinGW Makefiles" +# -DCMAKE_TOOLCHAIN_FILE=path\to\the\android.toolchain.cmake +# -DCMAKE_MAKE_PROGRAM="%ANDROID_NDK%\prebuilt\windows\bin\make.exe" .. +# $ cmake.exe --build . +# +# +# Options (can be set as cmake parameters: -D=): +# ANDROID_NDK=/opt/android-ndk - path to the NDK root. +# Can be set as environment variable. Can be set only at first cmake run. +# +# ANDROID_STANDALONE_TOOLCHAIN=/opt/android-toolchain - path to the +# standalone toolchain. This option is not used if full NDK is found +# (ignored if ANDROID_NDK is set). +# Can be set as environment variable. Can be set only at first cmake run. +# +# ANDROID_ABI=armeabi-v7a - specifies the target Application Binary +# Interface (ABI). This option nearly matches to the APP_ABI variable +# used by ndk-build tool from Android NDK. +# +# Possible targets are: +# "armeabi" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "armeabi-v7a" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "armeabi-v7a with NEON" - same as armeabi-v7a, but +# sets NEON as floating-point unit +# "armeabi-v7a with VFPV3" - same as armeabi-v7a, but +# sets VFPV3 as floating-point unit (has 32 registers instead of 16). +# "armeabi-v6 with VFP" - tuned for ARMv6 processors having VFP. +# "x86" - matches to the NDK ABI with the same name. +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# "mips" - matches to the NDK ABI with the same name +# (It is not tested on real devices by the authos of this toolchain) +# See ${ANDROID_NDK}/docs/CPU-ARCH-ABIS.html for the documentation. +# +# ANDROID_NATIVE_API_LEVEL=android-8 - level of Android API compile for. +# Option is read-only when standalone toolchain is used. +# +# ANDROID_TOOLCHAIN_NAME=arm-linux-androideabi-4.6 - the name of compiler +# toolchain to be used. The list of possible values depends on the NDK +# version. For NDK r8c the possible values are: +# +# * arm-linux-androideabi-4.4.3 +# * arm-linux-androideabi-4.6 +# * arm-linux-androideabi-clang3.1 +# * mipsel-linux-android-4.4.3 +# * mipsel-linux-android-4.6 +# * mipsel-linux-android-clang3.1 +# * x86-4.4.3 +# * x86-4.6 +# * x86-clang3.1 +# +# ANDROID_FORCE_ARM_BUILD=OFF - set ON to generate 32-bit ARM instructions +# instead of Thumb. Is not available for "x86" (inapplicable) and +# "armeabi-v6 with VFP" (is forced to be ON) ABIs. +# +# ANDROID_NO_UNDEFINED=ON - set ON to show all undefined symbols as linker +# errors even if they are not used. +# +# ANDROID_SO_UNDEFINED=OFF - set ON to allow undefined symbols in shared +# libraries. Automatically turned for NDK r5x and r6x due to GLESv2 +# problems. +# +# LIBRARY_OUTPUT_PATH_ROOT=${CMAKE_SOURCE_DIR} - where to output binary +# files. See additional details below. +# +# ANDROID_SET_OBSOLETE_VARIABLES=ON - if set, then toolchain defines some +# obsolete variables which were used by previous versions of this file for +# backward compatibility. +# +# ANDROID_STL=gnustl_static - specify the runtime to use. +# +# Possible values are: +# none -> Do not configure the runtime. +# system -> Use the default minimal system C++ runtime library. +# Implies -fno-rtti -fno-exceptions. +# Is not available for standalone toolchain. +# system_re -> Use the default minimal system C++ runtime library. +# Implies -frtti -fexceptions. +# Is not available for standalone toolchain. +# gabi++_static -> Use the GAbi++ runtime as a static library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# gabi++_shared -> Use the GAbi++ runtime as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_static -> Use the STLport runtime as a static library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# stlport_shared -> Use the STLport runtime as a shared library. +# Implies -fno-rtti -fno-exceptions for NDK before r7. +# Implies -frtti -fno-exceptions for NDK r7 and newer. +# Is not available for standalone toolchain. +# gnustl_static -> Use the GNU STL as a static library. +# Implies -frtti -fexceptions. +# gnustl_shared -> Use the GNU STL as a shared library. +# Implies -frtti -fno-exceptions. +# Available for NDK r7b and newer. +# Silently degrades to gnustl_static if not available. +# +# ANDROID_STL_FORCE_FEATURES=ON - turn rtti and exceptions support based on +# chosen runtime. If disabled, then the user is responsible for settings +# these options. +# +# What?: +# android-cmake toolchain searches for NDK/toolchain in the following order: +# ANDROID_NDK - cmake parameter +# ANDROID_NDK - environment variable +# ANDROID_STANDALONE_TOOLCHAIN - cmake parameter +# ANDROID_STANDALONE_TOOLCHAIN - environment variable +# ANDROID_NDK - default locations +# ANDROID_STANDALONE_TOOLCHAIN - default locations +# +# Make sure to do the following in your scripts: +# SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${my_cxx_flags}" ) +# SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${my_cxx_flags}" ) +# The flags will be prepopulated with critical flags, so don't loose them. +# Also be aware that toolchain also sets configuration-specific compiler +# flags and linker flags. +# +# ANDROID and BUILD_ANDROID will be set to true, you may test any of these +# variables to make necessary Android-specific configuration changes. +# +# Also ARMEABI or ARMEABI_V7A or X86 or MIPS will be set true, mutually +# exclusive. NEON option will be set true if VFP is set to NEON. +# +# LIBRARY_OUTPUT_PATH_ROOT should be set in cache to determine where Android +# libraries will be installed. +# Default is ${CMAKE_SOURCE_DIR}, and the android libs will always be +# under the ${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME} +# (depending on the target ABI). This is convenient for Android packaging. +# +# Change Log: +# - initial version December 2010 +# - April 2011 +# [+] added possibility to build with NDK (without standalone toolchain) +# [+] support cross-compilation on Windows (native, no cygwin support) +# [+] added compiler option to force "char" type to be signed +# [+] added toolchain option to compile to 32-bit ARM instructions +# [+] added toolchain option to disable SWIG search +# [+] added platform "armeabi-v7a with VFPV3" +# [~] ARM_TARGETS renamed to ARM_TARGET +# [+] EXECUTABLE_OUTPUT_PATH is set by toolchain (required on Windows) +# [~] Fixed bug with ANDROID_API_LEVEL variable +# [~] turn off SWIG search if it is not found first time +# - May 2011 +# [~] ANDROID_LEVEL is renamed to ANDROID_API_LEVEL +# [+] ANDROID_API_LEVEL is detected by toolchain if not specified +# [~] added guard to prevent changing of output directories on the first +# cmake pass +# [~] toolchain exits with error if ARM_TARGET is not recognized +# - June 2011 +# [~] default NDK path is updated for version r5c +# [+] variable CMAKE_SYSTEM_PROCESSOR is set based on ARM_TARGET +# [~] toolchain install directory is added to linker paths +# [-] removed SWIG-related stuff from toolchain +# [+] added macro find_host_package, find_host_program to search +# packages/programs on the host system +# [~] fixed path to STL library +# - July 2011 +# [~] fixed options caching +# [~] search for all supported NDK versions +# [~] allowed spaces in NDK path +# - September 2011 +# [~] updated for NDK r6b +# - November 2011 +# [*] rewritten for NDK r7 +# [+] x86 toolchain support (experimental) +# [+] added "armeabi-v6 with VFP" ABI for ARMv6 processors. +# [~] improved compiler and linker flags management +# [+] support different build flags for Release and Debug configurations +# [~] by default compiler flags the same as used by ndk-build (but only +# where reasonable) +# [~] ANDROID_NDK_TOOLCHAIN_ROOT is splitted to ANDROID_STANDALONE_TOOLCHAIN +# and ANDROID_TOOLCHAIN_ROOT +# [~] ARM_TARGET is renamed to ANDROID_ABI +# [~] ARMEABI_NDK_NAME is renamed to ANDROID_NDK_ABI_NAME +# [~] ANDROID_API_LEVEL is renamed to ANDROID_NATIVE_API_LEVEL +# - January 2012 +# [+] added stlport_static support (experimental) +# [+] added special check for cygwin +# [+] filtered out hidden files (starting with .) while globbing inside NDK +# [+] automatically applied GLESv2 linkage fix for NDK revisions 5-6 +# [+] added ANDROID_GET_ABI_RAWNAME to get NDK ABI names by CMake flags +# - February 2012 +# [+] updated for NDK r7b +# [~] fixed cmake try_compile() command +# [~] Fix for missing install_name_tool on OS X +# - March 2012 +# [~] fixed incorrect C compiler flags +# [~] fixed CMAKE_SYSTEM_PROCESSOR change on ANDROID_ABI change +# [+] improved toolchain loading speed +# [+] added assembler language support (.S) +# [+] allowed preset search paths and extra search suffixes +# - April 2012 +# [+] updated for NDK r7c +# [~] fixed most of problems with compiler/linker flags and caching +# [+] added option ANDROID_FUNCTION_LEVEL_LINKING +# - May 2012 +# [+] updated for NDK r8 +# [+] added mips architecture support +# - August 2012 +# [+] updated for NDK r8b +# [~] all intermediate files generated by toolchain are moved to CMakeFiles +# [~] libstdc++ and libsupc are removed from explicit link libraries +# [+] added CCache support (via NDK_CCACHE environment or cmake variable) +# [+] added gold linker support for NDK r8b +# [~] fixed mips linker flags for NDK r8b +# - September 2012 +# [+] added NDK release name detection (see ANDROID_NDK_RELEASE) +# [+] added support for all C++ runtimes from NDK +# (system, gabi++, stlport, gnustl) +# [+] improved warnings on known issues of NDKs +# [~] use gold linker as default if available (NDK r8b) +# [~] globally turned off rpath +# [~] compiler options are aligned with NDK r8b +# - October 2012 +# [~] fixed C++ linking: explicitly link with math library (OpenCV #2426) +# - November 2012 +# [+] updated for NDK r8c +# [+] added support for clang compiler +# - December 2012 +# [+] suppress warning about unused CMAKE_TOOLCHAIN_FILE variable +# [+] adjust API level to closest compatible as NDK does +# [~] fixed ccache full path search +# [+] updated for NDK r8d +# [~] compiler options are aligned with NDK r8d +# - March 2013 +# [+] updated for NDK r8e (x86 version) +# [+] support x86_64 version of NDK +# - April 2013 +# [+] support non-release NDK layouts (from Linaro git and Android git) +# [~] automatically detect if explicit link to crtbegin_*.o is needed +# ------------------------------------------------------------------------------ + +cmake_minimum_required( VERSION 2.6.3 ) + +if( DEFINED CMAKE_CROSSCOMPILING ) + # subsequent toolchain loading is not really needed + return() +endif() + +if( CMAKE_TOOLCHAIN_FILE ) + # touch toolchain variable only to suppress "unused variable" warning +endif() + +get_property( _CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE ) +if( _CMAKE_IN_TRY_COMPILE ) + include( "${CMAKE_CURRENT_SOURCE_DIR}/../android.toolchain.config.cmake" OPTIONAL ) +endif() + +# this one is important +set( CMAKE_SYSTEM_NAME Linux ) +# this one not so much +set( CMAKE_SYSTEM_VERSION 1 ) + +# rpath makes low sence for Android +set( CMAKE_SKIP_RPATH TRUE CACHE BOOL "If set, runtime paths are not added when using shared libraries." ) + +set( ANDROID_SUPPORTED_NDK_VERSIONS ${ANDROID_EXTRA_NDK_VERSIONS} -r8e -r8d -r8c -r8b -r8 -r7c -r7b -r7 -r6b -r6 -r5c -r5b -r5 "" ) +if(NOT DEFINED ANDROID_NDK_SEARCH_PATHS) + if( CMAKE_HOST_WIN32 ) + file( TO_CMAKE_PATH "$ENV{PROGRAMFILES}" ANDROID_NDK_SEARCH_PATHS ) + set( ANDROID_NDK_SEARCH_PATHS "${ANDROID_NDK_SEARCH_PATHS}/android-ndk" "$ENV{SystemDrive}/NVPACK/android-ndk" ) + else() + file( TO_CMAKE_PATH "$ENV{HOME}" ANDROID_NDK_SEARCH_PATHS ) + set( ANDROID_NDK_SEARCH_PATHS /opt/android-ndk "${ANDROID_NDK_SEARCH_PATHS}/NVPACK/android-ndk" ) + endif() +endif() +if(NOT DEFINED ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH) + set( ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH /opt/android-toolchain ) +endif() + +set( ANDROID_SUPPORTED_ABIS_arm "armeabi-v7a;armeabi;armeabi-v7a with NEON;armeabi-v7a with VFPV3;armeabi-v6 with VFP" ) +set( ANDROID_SUPPORTED_ABIS_x86 "x86" ) +set( ANDROID_SUPPORTED_ABIS_mipsel "mips" ) + +set( ANDROID_DEFAULT_NDK_API_LEVEL 8 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_x86 9 ) +set( ANDROID_DEFAULT_NDK_API_LEVEL_mips 9 ) + + +macro( __LIST_FILTER listvar regex ) + if( ${listvar} ) + foreach( __val ${${listvar}} ) + if( __val MATCHES "${regex}" ) + list( REMOVE_ITEM ${listvar} "${__val}" ) + endif() + endforeach() + endif() +endmacro() + +macro( __INIT_VARIABLE var_name ) + set( __test_path 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "PATH" ) + set( __test_path 1 ) + break() + endif() + endforeach() + if( __test_path AND NOT EXISTS "${${var_name}}" ) + unset( ${var_name} CACHE ) + endif() + if( "${${var_name}}" STREQUAL "" ) + set( __values 0 ) + foreach( __var ${ARGN} ) + if( __var STREQUAL "VALUES" ) + set( __values 1 ) + elseif( NOT __var STREQUAL "PATH" ) + set( __obsolete 0 ) + if( __var MATCHES "^OBSOLETE_.*$" ) + string( REPLACE "OBSOLETE_" "" __var "${__var}" ) + set( __obsolete 1 ) + endif() + if( __var MATCHES "^ENV_.*$" ) + string( REPLACE "ENV_" "" __var "${__var}" ) + set( __value "$ENV{${__var}}" ) + elseif( DEFINED ${__var} ) + set( __value "${${__var}}" ) + else() + if( __values ) + set( __value "${__var}" ) + else() + set( __value "" ) + endif() + endif() + if( NOT "${__value}" STREQUAL "" ) + if( __test_path ) + if( EXISTS "${__value}" ) + file( TO_CMAKE_PATH "${__value}" ${var_name} ) + if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) + endif() + break() + endif() + else() + set( ${var_name} "${__value}" ) + if( __obsolete AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Using value of obsolete variable ${__var} as initial value for ${var_name}. Please note, that ${__var} can be completely removed in future versions of the toolchain." ) + endif() + break() + endif() + endif() + endif() + endforeach() + unset( __value ) + unset( __values ) + unset( __obsolete ) + elseif( __test_path ) + file( TO_CMAKE_PATH "${${var_name}}" ${var_name} ) + endif() + unset( __test_path ) +endmacro() + +macro( __DETECT_NATIVE_API_LEVEL _var _path ) + SET( __ndkApiLevelRegex "^[\t ]*#define[\t ]+__ANDROID_API__[\t ]+([0-9]+)[\t ]*$" ) + FILE( STRINGS ${_path} __apiFileContent REGEX "${__ndkApiLevelRegex}" ) + if( NOT __apiFileContent ) + message( SEND_ERROR "Could not get Android native API level. Probably you have specified invalid level value, or your copy of NDK/toolchain is broken." ) + endif() + string( REGEX REPLACE "${__ndkApiLevelRegex}" "\\1" ${_var} "${__apiFileContent}" ) + unset( __apiFileContent ) + unset( __ndkApiLevelRegex ) +endmacro() + +macro( __DETECT_TOOLCHAIN_MACHINE_NAME _var _root ) + if( EXISTS "${_root}" ) + file( GLOB __gccExePath RELATIVE "${_root}/bin/" "${_root}/bin/*-gcc${TOOL_OS_SUFFIX}" ) + __LIST_FILTER( __gccExePath "^[.].*" ) + list( LENGTH __gccExePath __gccExePathsCount ) + if( NOT __gccExePathsCount EQUAL 1 AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "Could not determine machine name for compiler from ${_root}" ) + set( ${_var} "" ) + else() + get_filename_component( __gccExeName "${__gccExePath}" NAME_WE ) + string( REPLACE "-gcc" "" ${_var} "${__gccExeName}" ) + endif() + unset( __gccExePath ) + unset( __gccExePathsCount ) + unset( __gccExeName ) + else() + set( ${_var} "" ) + endif() +endmacro() + + +# fight against cygwin +set( ANDROID_FORBID_SYGWIN TRUE CACHE BOOL "Prevent cmake from working under cygwin and using cygwin tools") +mark_as_advanced( ANDROID_FORBID_SYGWIN ) +if( ANDROID_FORBID_SYGWIN ) + if( CYGWIN ) + message( FATAL_ERROR "Android NDK and android-cmake toolchain are not welcome Cygwin. It is unlikely that this cmake toolchain will work under cygwin. But if you want to try then you can set cmake variable ANDROID_FORBID_SYGWIN to FALSE and rerun cmake." ) + endif() + + if( CMAKE_HOST_WIN32 ) + # remove cygwin from PATH + set( __new_path "$ENV{PATH}") + __LIST_FILTER( __new_path "cygwin" ) + set(ENV{PATH} "${__new_path}") + unset(__new_path) + endif() +endif() + + +# detect current host platform +if( NOT DEFINED ANDROID_NDK_HOST_X64 AND CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|AMD64") + set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) + mark_as_advanced( ANDROID_NDK_HOST_X64 ) +endif() + +set( TOOL_OS_SUFFIX "" ) +if( CMAKE_HOST_APPLE ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "darwin-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "darwin-x86" ) +elseif( CMAKE_HOST_WIN32 ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "windows-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "windows" ) + set( TOOL_OS_SUFFIX ".exe" ) +elseif( CMAKE_HOST_UNIX ) + set( ANDROID_NDK_HOST_SYSTEM_NAME "linux-x86_64" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME2 "linux-x86" ) +else() + message( FATAL_ERROR "Cross-compilation on your platform is not supported by this cmake toolchain" ) +endif() + +if( NOT ANDROID_NDK_HOST_X64 ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) +endif() + +# see if we have path to Android NDK +__INIT_VARIABLE( ANDROID_NDK PATH ENV_ANDROID_NDK ) +if( NOT ANDROID_NDK ) + # see if we have path to Android standalone toolchain + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ENV_ANDROID_STANDALONE_TOOLCHAIN OBSOLETE_ANDROID_NDK_TOOLCHAIN_ROOT OBSOLETE_ENV_ANDROID_NDK_TOOLCHAIN_ROOT ) + + if( NOT ANDROID_STANDALONE_TOOLCHAIN ) + #try to find Android NDK in one of the the default locations + set( __ndkSearchPaths ) + foreach( __ndkSearchPath ${ANDROID_NDK_SEARCH_PATHS} ) + foreach( suffix ${ANDROID_SUPPORTED_NDK_VERSIONS} ) + list( APPEND __ndkSearchPaths "${__ndkSearchPath}${suffix}" ) + endforeach() + endforeach() + __INIT_VARIABLE( ANDROID_NDK PATH VALUES ${__ndkSearchPaths} ) + unset( __ndkSearchPaths ) + + if( ANDROID_NDK ) + message( STATUS "Using default path for Android NDK: ${ANDROID_NDK}" ) + message( STATUS " If you prefer to use a different location, please define a cmake or environment variable: ANDROID_NDK" ) + else() + #try to find Android standalone toolchain in one of the the default locations + __INIT_VARIABLE( ANDROID_STANDALONE_TOOLCHAIN PATH ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH ) + + if( ANDROID_STANDALONE_TOOLCHAIN ) + message( STATUS "Using default path for standalone toolchain ${ANDROID_STANDALONE_TOOLCHAIN}" ) + message( STATUS " If you prefer to use a different location, please define the variable: ANDROID_STANDALONE_TOOLCHAIN" ) + endif( ANDROID_STANDALONE_TOOLCHAIN ) + endif( ANDROID_NDK ) + endif( NOT ANDROID_STANDALONE_TOOLCHAIN ) +endif( NOT ANDROID_NDK ) + +# remember found paths +if( ANDROID_NDK ) + get_filename_component( ANDROID_NDK "${ANDROID_NDK}" ABSOLUTE ) + set( ANDROID_NDK "${ANDROID_NDK}" CACHE INTERNAL "Path of the Android NDK" FORCE ) + set( BUILD_WITH_ANDROID_NDK True ) + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT" ) + file( STRINGS "${ANDROID_NDK}/RELEASE.TXT" ANDROID_NDK_RELEASE_FULL LIMIT_COUNT 1 REGEX r[0-9]+[a-z]? ) + string( REGEX MATCH r[0-9]+[a-z]? ANDROID_NDK_RELEASE "${ANDROID_NDK_RELEASE_FULL}" ) + else() + set( ANDROID_NDK_RELEASE "r1x" ) + set( ANDROID_NDK_RELEASE_FULL "unreleased" ) + endif() +elseif( ANDROID_STANDALONE_TOOLCHAIN ) + get_filename_component( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" ABSOLUTE ) + # try to detect change + if( CMAKE_AR ) + string( LENGTH "${ANDROID_STANDALONE_TOOLCHAIN}" __length ) + string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidStandaloneToolchainPreviousPath ) + if( NOT __androidStandaloneToolchainPreviousPath STREQUAL ANDROID_STANDALONE_TOOLCHAIN ) + message( FATAL_ERROR "It is not possible to change path to the Android standalone toolchain on subsequent run." ) + endif() + unset( __androidStandaloneToolchainPreviousPath ) + unset( __length ) + endif() + set( ANDROID_STANDALONE_TOOLCHAIN "${ANDROID_STANDALONE_TOOLCHAIN}" CACHE INTERNAL "Path of the Android standalone toolchain" FORCE ) + set( BUILD_WITH_STANDALONE_TOOLCHAIN True ) +else() + list(GET ANDROID_NDK_SEARCH_PATHS 0 ANDROID_NDK_SEARCH_PATH) + message( FATAL_ERROR "Could not find neither Android NDK nor Android standalone toolchain. + You should either set an environment variable: + export ANDROID_NDK=~/my-android-ndk + or + export ANDROID_STANDALONE_TOOLCHAIN=~/my-android-toolchain + or put the toolchain or NDK in the default path: + sudo ln -s ~/my-android-ndk ${ANDROID_NDK_SEARCH_PATH} + sudo ln -s ~/my-android-toolchain ${ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH}" ) +endif() + +# android NDK layout +if( BUILD_WITH_ANDROID_NDK ) + if( NOT DEFINED ANDROID_NDK_LAYOUT ) + # try to automatically detect the layout + if( EXISTS "${ANDROID_NDK}/RELEASE.TXT") + set( ANDROID_NDK_LAYOUT "RELEASE" ) + elseif( EXISTS "${ANDROID_NDK}/../../linux-x86/toolchain/" ) + set( ANDROID_NDK_LAYOUT "LINARO" ) + elseif( EXISTS "${ANDROID_NDK}/../../gcc/" ) + set( ANDROID_NDK_LAYOUT "ANDROID" ) + endif() + endif() + set( ANDROID_NDK_LAYOUT "${ANDROID_NDK_LAYOUT}" CACHE STRING "The inner layout of NDK" ) + mark_as_advanced( ANDROID_NDK_LAYOUT ) + if( ANDROID_NDK_LAYOUT STREQUAL "LINARO" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../${ANDROID_NDK_HOST_SYSTEM_NAME}/toolchain" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + elseif( ANDROID_NDK_LAYOUT STREQUAL "ANDROID" ) + set( ANDROID_NDK_HOST_SYSTEM_NAME ${ANDROID_NDK_HOST_SYSTEM_NAME2} ) # only 32-bit at the moment + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/../../gcc/${ANDROID_NDK_HOST_SYSTEM_NAME}/arm" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "" ) + else() # ANDROID_NDK_LAYOUT STREQUAL "RELEASE" + set( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK}/toolchains" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME}" ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH2 "/prebuilt/${ANDROID_NDK_HOST_SYSTEM_NAME2}" ) + endif() + get_filename_component( ANDROID_NDK_TOOLCHAINS_PATH "${ANDROID_NDK_TOOLCHAINS_PATH}" ABSOLUTE ) + + # try to detect change of NDK + if( CMAKE_AR ) + string( LENGTH "${ANDROID_NDK_TOOLCHAINS_PATH}" __length ) + string( SUBSTRING "${CMAKE_AR}" 0 ${__length} __androidNdkPreviousPath ) + if( NOT __androidNdkPreviousPath STREQUAL ANDROID_NDK_TOOLCHAINS_PATH ) + message( FATAL_ERROR "It is not possible to change the path to the NDK on subsequent CMake run. You must remove all generated files from your build folder first. + " ) + endif() + unset( __androidNdkPreviousPath ) + unset( __length ) + endif() +endif() + + +# get all the details about standalone toolchain +if( BUILD_WITH_STANDALONE_TOOLCHAIN ) + __DETECT_NATIVE_API_LEVEL( ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot/usr/include/android/api-level.h" ) + set( ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + set( __availableToolchains "standalone" ) + __DETECT_TOOLCHAIN_MACHINE_NAME( __availableToolchainMachines "${ANDROID_STANDALONE_TOOLCHAIN}" ) + if( NOT __availableToolchainMachines ) + message( FATAL_ERROR "Could not determine machine name of your toolchain. Probably your Android standalone toolchain is broken." ) + endif() + if( __availableToolchainMachines MATCHES i686 ) + set( __availableToolchainArchs "x86" ) + elseif( __availableToolchainMachines MATCHES arm ) + set( __availableToolchainArchs "arm" ) + elseif( __availableToolchainMachines MATCHES mipsel ) + set( __availableToolchainArchs "mipsel" ) + endif() + execute_process( COMMAND "${ANDROID_STANDALONE_TOOLCHAIN}/bin/${__availableToolchainMachines}-gcc${TOOL_OS_SUFFIX}" -dumpversion + OUTPUT_VARIABLE __availableToolchainCompilerVersions OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9]+)?" __availableToolchainCompilerVersions "${__availableToolchainCompilerVersions}" ) + if( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/bin/clang${TOOL_OS_SUFFIX}" ) + list( APPEND __availableToolchains "standalone-clang" ) + list( APPEND __availableToolchainMachines ${__availableToolchainMachines} ) + list( APPEND __availableToolchainArchs ${__availableToolchainArchs} ) + list( APPEND __availableToolchainCompilerVersions ${__availableToolchainCompilerVersions} ) + endif() +endif() + +macro( __GLOB_NDK_TOOLCHAINS __availableToolchainsVar __availableToolchainsLst __toolchain_subpath ) + foreach( __toolchain ${${__availableToolchainsLst}} ) + if( "${__toolchain}" MATCHES "-clang3[.][0-9]$" AND NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${__toolchain}${__toolchain_subpath}" ) + string( REGEX REPLACE "-clang3[.][0-9]$" "-4.6" __gcc_toolchain "${__toolchain}" ) + else() + set( __gcc_toolchain "${__toolchain}" ) + endif() + __DETECT_TOOLCHAIN_MACHINE_NAME( __machine "${ANDROID_NDK_TOOLCHAINS_PATH}/${__gcc_toolchain}${__toolchain_subpath}" ) + if( __machine ) + string( REGEX MATCH "[0-9]+[.][0-9]+([.][0-9x]+)?$" __version "${__gcc_toolchain}" ) + if( __machine MATCHES i686 ) + set( __arch "x86" ) + elseif( __machine MATCHES arm ) + set( __arch "arm" ) + elseif( __machine MATCHES mipsel ) + set( __arch "mipsel" ) + endif() + list( APPEND __availableToolchainMachines "${__machine}" ) + list( APPEND __availableToolchainArchs "${__arch}" ) + list( APPEND __availableToolchainCompilerVersions "${__version}" ) + list( APPEND ${__availableToolchainsVar} "${__toolchain}" ) + endif() + unset( __gcc_toolchain ) + endforeach() +endmacro() + +# get all the details about NDK +if( BUILD_WITH_ANDROID_NDK ) + file( GLOB ANDROID_SUPPORTED_NATIVE_API_LEVELS RELATIVE "${ANDROID_NDK}/platforms" "${ANDROID_NDK}/platforms/android-*" ) + string( REPLACE "android-" "" ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_SUPPORTED_NATIVE_API_LEVELS}" ) + set( __availableToolchains "" ) + set( __availableToolchainMachines "" ) + set( __availableToolchainArchs "" ) + set( __availableToolchainCompilerVersions "" ) + if( ANDROID_TOOLCHAIN_NAME AND EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_TOOLCHAIN_NAME}/" ) + # do not go through all toolchains if we know the name + set( __availableToolchainsLst "${ANDROID_TOOLCHAIN_NAME}" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() + endif() + endif() + if( NOT __availableToolchains ) + file( GLOB __availableToolchainsLst RELATIVE "${ANDROID_NDK_TOOLCHAINS_PATH}" "${ANDROID_NDK_TOOLCHAINS_PATH}/*" ) + if( __availableToolchains ) + list(SORT __availableToolchainsLst) # we need clang to go after gcc + endif() + __LIST_FILTER( __availableToolchainsLst "^[.]" ) + __LIST_FILTER( __availableToolchainsLst "llvm" ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + if( NOT __availableToolchains AND NOT ANDROID_NDK_TOOLCHAINS_SUBPATH STREQUAL ANDROID_NDK_TOOLCHAINS_SUBPATH2 ) + __GLOB_NDK_TOOLCHAINS( __availableToolchains __availableToolchainsLst "${ANDROID_NDK_TOOLCHAINS_SUBPATH2}" ) + if( __availableToolchains ) + set( ANDROID_NDK_TOOLCHAINS_SUBPATH ${ANDROID_NDK_TOOLCHAINS_SUBPATH2} ) + endif() + endif() + endif() + if( NOT __availableToolchains ) + message( FATAL_ERROR "Could not find any working toolchain in the NDK. Probably your Android NDK is broken." ) + endif() +endif() + +# build list of available ABIs +set( ANDROID_SUPPORTED_ABIS "" ) +set( __uniqToolchainArchNames ${__availableToolchainArchs} ) +list( REMOVE_DUPLICATES __uniqToolchainArchNames ) +list( SORT __uniqToolchainArchNames ) +foreach( __arch ${__uniqToolchainArchNames} ) + list( APPEND ANDROID_SUPPORTED_ABIS ${ANDROID_SUPPORTED_ABIS_${__arch}} ) +endforeach() +unset( __uniqToolchainArchNames ) +if( NOT ANDROID_SUPPORTED_ABIS ) + message( FATAL_ERROR "No one of known Android ABIs is supported by this cmake toolchain." ) +endif() + +# choose target ABI +__INIT_VARIABLE( ANDROID_ABI OBSOLETE_ARM_TARGET OBSOLETE_ARM_TARGETS VALUES ${ANDROID_SUPPORTED_ABIS} ) +# verify that target ABI is supported +list( FIND ANDROID_SUPPORTED_ABIS "${ANDROID_ABI}" __androidAbiIdx ) +if( __androidAbiIdx EQUAL -1 ) + string( REPLACE ";" "\", \"", PRINTABLE_ANDROID_SUPPORTED_ABIS "${ANDROID_SUPPORTED_ABIS}" ) + message( FATAL_ERROR "Specified ANDROID_ABI = \"${ANDROID_ABI}\" is not supported by this cmake toolchain or your NDK/toolchain. + Supported values are: \"${PRINTABLE_ANDROID_SUPPORTED_ABIS}\" + " ) +endif() +unset( __androidAbiIdx ) + +# set target ABI options +if( ANDROID_ABI STREQUAL "x86" ) + set( X86 true ) + set( ANDROID_NDK_ABI_NAME "x86" ) + set( ANDROID_ARCH_NAME "x86" ) + set( ANDROID_ARCH_FULLNAME "x86" ) + set( ANDROID_LLVM_TRIPLE "i686-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "i686" ) +elseif( ANDROID_ABI STREQUAL "mips" ) + set( MIPS true ) + set( ANDROID_NDK_ABI_NAME "mips" ) + set( ANDROID_ARCH_NAME "mips" ) + set( ANDROID_ARCH_FULLNAME "mipsel" ) + set( ANDROID_LLVM_TRIPLE "mipsel-none-linux-android" ) + set( CMAKE_SYSTEM_PROCESSOR "mips" ) +elseif( ANDROID_ABI STREQUAL "armeabi" ) + set( ARMEABI true ) + set( ANDROID_NDK_ABI_NAME "armeabi" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv5te" ) +elseif( ANDROID_ABI STREQUAL "armeabi-v6 with VFP" ) + set( ARMEABI_V6 true ) + set( ANDROID_NDK_ABI_NAME "armeabi" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv5te-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv6" ) + # need always fallback to older platform + set( ARMEABI true ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a") + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a with VFPV3" ) + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) + set( VFPV3 true ) +elseif( ANDROID_ABI STREQUAL "armeabi-v7a with NEON" ) + set( ARMEABI_V7A true ) + set( ANDROID_NDK_ABI_NAME "armeabi-v7a" ) + set( ANDROID_ARCH_NAME "arm" ) + set( ANDROID_ARCH_FULLNAME "arm" ) + set( ANDROID_LLVM_TRIPLE "armv7-none-linux-androideabi" ) + set( CMAKE_SYSTEM_PROCESSOR "armv7-a" ) + set( VFPV3 true ) + set( NEON true ) +else() + message( SEND_ERROR "Unknown ANDROID_ABI=\"${ANDROID_ABI}\" is specified." ) +endif() + +if( CMAKE_BINARY_DIR AND EXISTS "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" ) + # really dirty hack + # it is not possible to change CMAKE_SYSTEM_PROCESSOR after the first run... + file( APPEND "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeSystem.cmake" "SET(CMAKE_SYSTEM_PROCESSOR \"${CMAKE_SYSTEM_PROCESSOR}\")\n" ) +endif() + +if( ANDROID_ARCH_NAME STREQUAL "arm" AND NOT ARMEABI_V6 ) + __INIT_VARIABLE( ANDROID_FORCE_ARM_BUILD OBSOLETE_FORCE_ARM VALUES OFF ) + set( ANDROID_FORCE_ARM_BUILD ${ANDROID_FORCE_ARM_BUILD} CACHE BOOL "Use 32-bit ARM instructions instead of Thumb-1" FORCE ) + mark_as_advanced( ANDROID_FORCE_ARM_BUILD ) +else() + unset( ANDROID_FORCE_ARM_BUILD CACHE ) +endif() + +# choose toolchain +if( ANDROID_TOOLCHAIN_NAME ) + list( FIND __availableToolchains "${ANDROID_TOOLCHAIN_NAME}" __toolchainIdx ) + if( __toolchainIdx EQUAL -1 ) + list( SORT __availableToolchains ) + string( REPLACE ";" "\n * " toolchains_list "${__availableToolchains}" ) + set( toolchains_list " * ${toolchains_list}") + message( FATAL_ERROR "Specified toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is missing in your NDK or broken. Please verify that your NDK is working or select another compiler toolchain. +To configure the toolchain set CMake variable ANDROID_TOOLCHAIN_NAME to one of the following values:\n${toolchains_list}\n" ) + endif() + list( GET __availableToolchainArchs ${__toolchainIdx} __toolchainArch ) + if( NOT __toolchainArch STREQUAL ANDROID_ARCH_FULLNAME ) + message( SEND_ERROR "Selected toolchain \"${ANDROID_TOOLCHAIN_NAME}\" is not able to compile binaries for the \"${ANDROID_ARCH_NAME}\" platform." ) + endif() +else() + set( __toolchainIdx -1 ) + set( __applicableToolchains "" ) + set( __toolchainMaxVersion "0.0.0" ) + list( LENGTH __availableToolchains __availableToolchainsCount ) + math( EXPR __availableToolchainsCount "${__availableToolchainsCount}-1" ) + foreach( __idx RANGE ${__availableToolchainsCount} ) + list( GET __availableToolchainArchs ${__idx} __toolchainArch ) + if( __toolchainArch STREQUAL ANDROID_ARCH_FULLNAME ) + list( GET __availableToolchainCompilerVersions ${__idx} __toolchainVersion ) + string( REPLACE "x" "99" __toolchainVersion "${__toolchainVersion}") + if( __toolchainVersion VERSION_GREATER __toolchainMaxVersion ) + set( __toolchainMaxVersion "${__toolchainVersion}" ) + set( __toolchainIdx ${__idx} ) + endif() + endif() + endforeach() + unset( __availableToolchainsCount ) + unset( __toolchainMaxVersion ) + unset( __toolchainVersion ) +endif() +unset( __toolchainArch ) +if( __toolchainIdx EQUAL -1 ) + message( FATAL_ERROR "No one of available compiler toolchains is able to compile for ${ANDROID_ARCH_NAME} platform." ) +endif() +list( GET __availableToolchains ${__toolchainIdx} ANDROID_TOOLCHAIN_NAME ) +list( GET __availableToolchainMachines ${__toolchainIdx} ANDROID_TOOLCHAIN_MACHINE_NAME ) +list( GET __availableToolchainCompilerVersions ${__toolchainIdx} ANDROID_COMPILER_VERSION ) + +unset( __toolchainIdx ) +unset( __availableToolchains ) +unset( __availableToolchainMachines ) +unset( __availableToolchainArchs ) +unset( __availableToolchainCompilerVersions ) + +# choose native API level +__INIT_VARIABLE( ANDROID_NATIVE_API_LEVEL ENV_ANDROID_NATIVE_API_LEVEL ANDROID_API_LEVEL ENV_ANDROID_API_LEVEL ANDROID_STANDALONE_TOOLCHAIN_API_LEVEL ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME} ANDROID_DEFAULT_NDK_API_LEVEL ) +string( REGEX MATCH "[0-9]+" ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" ) +# adjust API level +set( __real_api_level ${ANDROID_DEFAULT_NDK_API_LEVEL_${ANDROID_ARCH_NAME}} ) +foreach( __level ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + if( NOT __level GREATER ANDROID_NATIVE_API_LEVEL AND NOT __level LESS __real_api_level ) + set( __real_api_level ${__level} ) + endif() +endforeach() +if( __real_api_level AND NOT ANDROID_NATIVE_API_LEVEL EQUAL __real_api_level ) + message( STATUS "Adjusting Android API level 'android-${ANDROID_NATIVE_API_LEVEL}' to 'android-${__real_api_level}'") + set( ANDROID_NATIVE_API_LEVEL ${__real_api_level} ) +endif() +unset(__real_api_level) +# validate +list( FIND ANDROID_SUPPORTED_NATIVE_API_LEVELS "${ANDROID_NATIVE_API_LEVEL}" __levelIdx ) +if( __levelIdx EQUAL -1 ) + message( SEND_ERROR "Specified Android native API level 'android-${ANDROID_NATIVE_API_LEVEL}' is not supported by your NDK/toolchain." ) +else() + if( BUILD_WITH_ANDROID_NDK ) + __DETECT_NATIVE_API_LEVEL( __realApiLevel "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}/usr/include/android/api-level.h" ) + if( NOT __realApiLevel EQUAL ANDROID_NATIVE_API_LEVEL ) + message( SEND_ERROR "Specified Android API level (${ANDROID_NATIVE_API_LEVEL}) does not match to the level found (${__realApiLevel}). Probably your copy of NDK is broken." ) + endif() + unset( __realApiLevel ) + endif() + set( ANDROID_NATIVE_API_LEVEL "${ANDROID_NATIVE_API_LEVEL}" CACHE STRING "Android API level for native code" FORCE ) + if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_NATIVE_API_LEVELS ) + set_property( CACHE ANDROID_NATIVE_API_LEVEL PROPERTY STRINGS ${ANDROID_SUPPORTED_NATIVE_API_LEVELS} ) + endif() +endif() +unset( __levelIdx ) + + +# remember target ABI +set( ANDROID_ABI "${ANDROID_ABI}" CACHE STRING "The target ABI for Android. If arm, then armeabi-v7a is recommended for hardware floating point." FORCE ) +if( CMAKE_VERSION VERSION_GREATER "2.8" ) + list( SORT ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME} ) + set_property( CACHE ANDROID_ABI PROPERTY STRINGS ${ANDROID_SUPPORTED_ABIS_${ANDROID_ARCH_FULLNAME}} ) +endif() + + +# runtime choice (STL, rtti, exceptions) +if( NOT ANDROID_STL ) + # honor legacy ANDROID_USE_STLPORT + if( DEFINED ANDROID_USE_STLPORT ) + if( ANDROID_USE_STLPORT ) + set( ANDROID_STL stlport_static ) + endif() + message( WARNING "You are using an obsolete variable ANDROID_USE_STLPORT to select the STL variant. Use -DANDROID_STL=stlport_static instead." ) + endif() + if( NOT ANDROID_STL ) + set( ANDROID_STL gnustl_static ) + endif() +endif() +set( ANDROID_STL "${ANDROID_STL}" CACHE STRING "C++ runtime" ) +set( ANDROID_STL_FORCE_FEATURES ON CACHE BOOL "automatically configure rtti and exceptions support based on C++ runtime" ) +mark_as_advanced( ANDROID_STL ANDROID_STL_FORCE_FEATURES ) + +if( BUILD_WITH_ANDROID_NDK ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|system|system_re|gabi\\+\\+_static|gabi\\+\\+_shared|stlport_static|stlport_shared|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + system -> Use the default minimal system C++ runtime library. + system_re -> Same as system but with rtti and exceptions. + gabi++_static -> Use the GAbi++ runtime as a static library. + gabi++_shared -> Use the GAbi++ runtime as a shared library. + stlport_static -> Use the STLport runtime as a static library. + stlport_shared -> Use the STLport runtime as a shared library. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) + endif() +elseif( BUILD_WITH_STANDALONE_TOOLCHAIN ) + if( NOT "${ANDROID_STL}" MATCHES "^(none|gnustl_static|gnustl_shared)$") + message( FATAL_ERROR "ANDROID_STL is set to invalid value \"${ANDROID_STL}\". +The possible values are: + none -> Do not configure the runtime. + gnustl_static -> (default) Use the GNU STL as a static library. + gnustl_shared -> Use the GNU STL as a shared library. +" ) + endif() +endif() + +unset( ANDROID_RTTI ) +unset( ANDROID_EXCEPTIONS ) +unset( ANDROID_STL_INCLUDE_DIRS ) +unset( __libstl ) +unset( __libsupcxx ) + +if( NOT _CMAKE_IN_TRY_COMPILE AND ANDROID_NDK_RELEASE STREQUAL "r7b" AND ARMEABI_V7A AND NOT VFPV3 AND ANDROID_STL MATCHES "gnustl" ) + message( WARNING "The GNU STL armeabi-v7a binaries from NDK r7b can crash non-NEON devices. The files provided with NDK r7b were not configured properly, resulting in crashes on Tegra2-based devices and others when trying to use certain floating-point functions (e.g., cosf, sinf, expf). +You are strongly recommended to switch to another NDK release. +" ) +endif() + +if( NOT _CMAKE_IN_TRY_COMPILE AND X86 AND ANDROID_STL MATCHES "gnustl" AND ANDROID_NDK_RELEASE STREQUAL "r6" ) + message( WARNING "The x86 system header file from NDK r6 has incorrect definition for ptrdiff_t. You are recommended to upgrade to a newer NDK release or manually patch the header: +See https://android.googlesource.com/platform/development.git f907f4f9d4e56ccc8093df6fee54454b8bcab6c2 + diff --git a/ndk/platforms/android-9/arch-x86/include/machine/_types.h b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + index 5e28c64..65892a1 100644 + --- a/ndk/platforms/android-9/arch-x86/include/machine/_types.h + +++ b/ndk/platforms/android-9/arch-x86/include/machine/_types.h + @@ -51,7 +51,11 @@ typedef long int ssize_t; + #endif + #ifndef _PTRDIFF_T + #define _PTRDIFF_T + -typedef long ptrdiff_t; + +# ifdef __ANDROID__ + + typedef int ptrdiff_t; + +# else + + typedef long ptrdiff_t; + +# endif + #endif +" ) +endif() + + +# setup paths and STL for standalone toolchain +if( BUILD_WITH_STANDALONE_TOOLCHAIN ) + set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_STANDALONE_TOOLCHAIN}" ) + set( ANDROID_SYSROOT "${ANDROID_STANDALONE_TOOLCHAIN}/sysroot" ) + + if( NOT ANDROID_STL STREQUAL "none" ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/include/c++/${ANDROID_COMPILER_VERSION}" ) + if( ARMEABI_V7A AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb/bits" ) + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/thumb" ) + else() + list( APPEND ANDROID_STL_INCLUDE_DIRS "${ANDROID_STL_INCLUDE_DIRS}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" ) + endif() + # always search static GNU STL to get the location of libsupc++.a + if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb" ) + elseif( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libstdc++.a" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib" ) + endif() + if( __libstl ) + set( __libsupcxx "${__libstl}/libsupc++.a" ) + set( __libstl "${__libstl}/libstdc++.a" ) + endif() + if( NOT EXISTS "${__libsupcxx}" ) + message( FATAL_ERROR "The required libstdsupc++.a is missing in your standalone toolchain. + Usually it happens because of bug in make-standalone-toolchain.sh script from NDK r7, r7b and r7c. + You need to either upgrade to newer NDK or manually copy + $ANDROID_NDK/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a + to + ${__libsupcxx} + " ) + endif() + if( ANDROID_STL STREQUAL "gnustl_shared" ) + if( ARMEABI_V7A AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libgnustl_shared.so" ) + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD AND EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libgnustl_shared.so" ) + elseif( EXISTS "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + set( __libstl "${ANDROID_STANDALONE_TOOLCHAIN}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libgnustl_shared.so" ) + endif() + endif() + endif() +endif() + +# clang +if( "${ANDROID_TOOLCHAIN_NAME}" STREQUAL "standalone-clang" ) + set( ANDROID_COMPILER_IS_CLANG 1 ) + execute_process( COMMAND "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/clang${TOOL_OS_SUFFIX}" --version OUTPUT_VARIABLE ANDROID_CLANG_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE ) + string( REGEX MATCH "[0-9]+[.][0-9]+" ANDROID_CLANG_VERSION "${ANDROID_CLANG_VERSION}") +elseif( "${ANDROID_TOOLCHAIN_NAME}" MATCHES "-clang3[.][0-9]?$" ) + string( REGEX MATCH "3[.][0-9]$" ANDROID_CLANG_VERSION "${ANDROID_TOOLCHAIN_NAME}") + string( REGEX REPLACE "-clang${ANDROID_CLANG_VERSION}$" "-4.6" ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + if( NOT EXISTS "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}/bin/clang${TOOL_OS_SUFFIX}" ) + message( FATAL_ERROR "Could not find the Clang compiler driver" ) + endif() + set( ANDROID_COMPILER_IS_CLANG 1 ) + set( ANDROID_CLANG_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/llvm-${ANDROID_CLANG_VERSION}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) +else() + set( ANDROID_GCC_TOOLCHAIN_NAME "${ANDROID_TOOLCHAIN_NAME}" ) + unset( ANDROID_COMPILER_IS_CLANG CACHE ) +endif() + +string( REPLACE "." "" _clang_name "clang${ANDROID_CLANG_VERSION}" ) +if( NOT EXISTS "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" ) + set( _clang_name "clang" ) +endif() + + +# setup paths and STL for NDK +if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_TOOLCHAIN_ROOT "${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}${ANDROID_NDK_TOOLCHAINS_SUBPATH}" ) + set( ANDROID_SYSROOT "${ANDROID_NDK}/platforms/android-${ANDROID_NATIVE_API_LEVEL}/arch-${ANDROID_ARCH_NAME}" ) + + if( ANDROID_STL STREQUAL "none" ) + # do nothing + elseif( ANDROID_STL STREQUAL "system" ) + set( ANDROID_RTTI OFF ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL STREQUAL "system_re" ) + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/system/include" ) + elseif( ANDROID_STL MATCHES "gabi" ) + if( ANDROID_NDK_RELEASE STRLESS "r7" ) + message( FATAL_ERROR "gabi++ is not awailable in your NDK. You have to upgrade to NDK r7 or newer to use gabi++.") + endif() + set( ANDROID_RTTI ON ) + set( ANDROID_EXCEPTIONS OFF ) + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/gabi++/include" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gabi++/libs/${ANDROID_NDK_ABI_NAME}/libgabi++_static.a" ) + elseif( ANDROID_STL MATCHES "stlport" ) + if( NOT ANDROID_NDK_RELEASE STRLESS "r8d" ) + set( ANDROID_EXCEPTIONS ON ) + else() + set( ANDROID_EXCEPTIONS OFF ) + endif() + if( ANDROID_NDK_RELEASE STRLESS "r7" ) + set( ANDROID_RTTI OFF ) + else() + set( ANDROID_RTTI ON ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${ANDROID_NDK}/sources/cxx-stl/stlport/stlport" ) + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/stlport/libs/${ANDROID_NDK_ABI_NAME}/libstlport_static.a" ) + elseif( ANDROID_STL MATCHES "gnustl" ) + set( ANDROID_EXCEPTIONS ON ) + set( ANDROID_RTTI ON ) + if( EXISTS "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + if( ARMEABI_V7A AND ANDROID_COMPILER_VERSION VERSION_EQUAL "4.7" AND ANDROID_NDK_RELEASE STREQUAL "r8d" ) + # gnustl binary for 4.7 compiler is buggy :( + # TODO: look for right fix + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/4.6" ) + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}" ) + endif() + else() + set( __libstl "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++" ) + endif() + set( ANDROID_STL_INCLUDE_DIRS "${__libstl}/include" "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/include" ) + if( EXISTS "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libgnustl_static.a" ) + else() + set( __libstl "${__libstl}/libs/${ANDROID_NDK_ABI_NAME}/libstdc++.a" ) + endif() + else() + message( FATAL_ERROR "Unknown runtime: ${ANDROID_STL}" ) + endif() + # find libsupc++.a - rtti & exceptions + if( ANDROID_STL STREQUAL "system_re" OR ANDROID_STL MATCHES "gnustl" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/${ANDROID_COMPILER_VERSION}/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r8b or newer + if( NOT EXISTS "${__libsupcxx}" ) + set( __libsupcxx "${ANDROID_NDK}/sources/cxx-stl/gnu-libstdc++/libs/${ANDROID_NDK_ABI_NAME}/libsupc++.a" ) # r7-r8 + endif() + if( NOT EXISTS "${__libsupcxx}" ) # before r7 + if( ARMEABI_V7A ) + if( ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/${CMAKE_SYSTEM_PROCESSOR}/thumb/libsupc++.a" ) + endif() + elseif( ARMEABI AND NOT ANDROID_FORCE_ARM_BUILD ) + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/thumb/libsupc++.a" ) + else() + set( __libsupcxx "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}/lib/libsupc++.a" ) + endif() + endif() + if( NOT EXISTS "${__libsupcxx}") + message( ERROR "Could not find libsupc++.a for a chosen platform. Either your NDK is not supported or is broken.") + endif() + endif() +endif() + + +# case of shared STL linkage +if( ANDROID_STL MATCHES "shared" AND DEFINED __libstl ) + string( REPLACE "_static.a" "_shared.so" __libstl "${__libstl}" ) + if( NOT _CMAKE_IN_TRY_COMPILE AND __libstl MATCHES "[.]so$" ) + get_filename_component( __libstlname "${__libstl}" NAME ) + execute_process( COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${__libstl}" "${LIBRARY_OUTPUT_PATH}/${__libstlname}" RESULT_VARIABLE __fileCopyProcess ) + if( NOT __fileCopyProcess EQUAL 0 OR NOT EXISTS "${LIBRARY_OUTPUT_PATH}/${__libstlname}") + message( SEND_ERROR "Failed copying of ${__libstl} to the ${LIBRARY_OUTPUT_PATH}/${__libstlname}" ) + endif() + unset( __fileCopyProcess ) + unset( __libstlname ) + endif() +endif() + + +# ccache support +__INIT_VARIABLE( _ndk_ccache NDK_CCACHE ENV_NDK_CCACHE ) +if( _ndk_ccache ) + if( DEFINED NDK_CCACHE AND NOT EXISTS NDK_CCACHE ) + unset( NDK_CCACHE CACHE ) + endif() + find_program( NDK_CCACHE "${_ndk_ccache}" DOC "The path to ccache binary") +else() + unset( NDK_CCACHE CACHE ) +endif() +unset( _ndk_ccache ) + + +# setup the cross-compiler +if( NOT CMAKE_C_COMPILER ) + if( NDK_CCACHE AND NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( CMAKE_C_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C compiler" ) + set( CMAKE_CXX_COMPILER "${NDK_CCACHE}" CACHE PATH "ccache as C++ compiler" ) + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER_ARG1 "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + endif() + else() + if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}${TOOL_OS_SUFFIX}" CACHE PATH "C compiler") + set( CMAKE_CXX_COMPILER "${ANDROID_CLANG_TOOLCHAIN_ROOT}/bin/${_clang_name}++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler") + else() + set( CMAKE_C_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "C compiler" ) + set( CMAKE_CXX_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-g++${TOOL_OS_SUFFIX}" CACHE PATH "C++ compiler" ) + endif() + endif() + set( CMAKE_ASM_COMPILER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-gcc${TOOL_OS_SUFFIX}" CACHE PATH "assembler" ) + set( CMAKE_STRIP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-strip${TOOL_OS_SUFFIX}" CACHE PATH "strip" ) + set( CMAKE_AR "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ar${TOOL_OS_SUFFIX}" CACHE PATH "archive" ) + set( CMAKE_LINKER "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ld${TOOL_OS_SUFFIX}" CACHE PATH "linker" ) + set( CMAKE_NM "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-nm${TOOL_OS_SUFFIX}" CACHE PATH "nm" ) + set( CMAKE_OBJCOPY "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objcopy${TOOL_OS_SUFFIX}" CACHE PATH "objcopy" ) + set( CMAKE_OBJDUMP "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-objdump${TOOL_OS_SUFFIX}" CACHE PATH "objdump" ) + set( CMAKE_RANLIB "${ANDROID_TOOLCHAIN_ROOT}/bin/${ANDROID_TOOLCHAIN_MACHINE_NAME}-ranlib${TOOL_OS_SUFFIX}" CACHE PATH "ranlib" ) +endif() + +set( _CMAKE_TOOLCHAIN_PREFIX "${ANDROID_TOOLCHAIN_MACHINE_NAME}-" ) +if( CMAKE_VERSION VERSION_LESS 2.8.5 ) + set( CMAKE_ASM_COMPILER_ARG1 "-c" ) +endif() +if( APPLE ) + find_program( CMAKE_INSTALL_NAME_TOOL NAMES install_name_tool ) + if( NOT CMAKE_INSTALL_NAME_TOOL ) + message( FATAL_ERROR "Could not find install_name_tool, please check your installation." ) + endif() + mark_as_advanced( CMAKE_INSTALL_NAME_TOOL ) +endif() + +# Force set compilers because standard identification works badly for us +include( CMakeForceCompiler ) +CMAKE_FORCE_C_COMPILER( "${CMAKE_C_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_C_COMPILER_ID Clang) +endif() +set( CMAKE_C_PLATFORM_ID Linux ) +set( CMAKE_C_SIZEOF_DATA_PTR 4 ) +set( CMAKE_C_HAS_ISYSROOT 1 ) +set( CMAKE_C_COMPILER_ABI ELF ) +CMAKE_FORCE_CXX_COMPILER( "${CMAKE_CXX_COMPILER}" GNU ) +if( ANDROID_COMPILER_IS_CLANG ) + set( CMAKE_CXX_COMPILER_ID Clang) +endif() +set( CMAKE_CXX_PLATFORM_ID Linux ) +set( CMAKE_CXX_SIZEOF_DATA_PTR 4 ) +set( CMAKE_CXX_HAS_ISYSROOT 1 ) +set( CMAKE_CXX_COMPILER_ABI ELF ) +set( CMAKE_CXX_SOURCE_FILE_EXTENSIONS cc cp cxx cpp CPP c++ C ) +# force ASM compiler (required for CMake < 2.8.5) +set( CMAKE_ASM_COMPILER_ID_RUN TRUE ) +set( CMAKE_ASM_COMPILER_ID GNU ) +set( CMAKE_ASM_COMPILER_WORKS TRUE ) +set( CMAKE_ASM_COMPILER_FORCED TRUE ) +set( CMAKE_COMPILER_IS_GNUASM 1) +set( CMAKE_ASM_SOURCE_FILE_EXTENSIONS s S asm ) + +# flags and definitions +remove_definitions( -DANDROID ) +add_definitions( -DANDROID ) + +if( ANDROID_SYSROOT MATCHES "[ ;\"]" ) + if( CMAKE_HOST_WIN32 ) + # try to convert path to 8.3 form + file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "@echo %~s1" ) + execute_process( COMMAND "$ENV{ComSpec}" /c "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/cvt83.cmd" "${ANDROID_SYSROOT}" + OUTPUT_VARIABLE __path OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE __result ERROR_QUIET ) + if( __result EQUAL 0 ) + file( TO_CMAKE_PATH "${__path}" ANDROID_SYSROOT ) + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) + else() + set( ANDROID_CXX_FLAGS "--sysroot=\"${ANDROID_SYSROOT}\"" ) + endif() + else() + set( ANDROID_CXX_FLAGS "'--sysroot=${ANDROID_SYSROOT}'" ) + endif() + if( NOT _CMAKE_IN_TRY_COMPILE ) + # quotes can break try_compile and compiler identification + message(WARNING "Path to your Android NDK (or toolchain) has non-alphanumeric symbols.\nThe build might be broken.\n") + endif() +else() + set( ANDROID_CXX_FLAGS "--sysroot=${ANDROID_SYSROOT}" ) +endif() + +# NDK flags +if( ARMEABI OR ARMEABI_V7A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -funwind-tables" ) + if( NOT ANDROID_FORCE_ARM_BUILD AND NOT ARMEABI_V6 ) + set( ANDROID_CXX_FLAGS_RELEASE "-mthumb -fomit-frame-pointer -fno-strict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -finline-limit=64" ) + endif() + else() + # always compile ARMEABI_V6 in arm mode; otherwise there is no difference from ARMEABI + set( ANDROID_CXX_FLAGS_RELEASE "-marm -fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-marm -fno-omit-frame-pointer -fno-strict-aliasing" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + endif() + endif() +elseif( X86 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funwind-tables" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -funswitch-loops -finline-limit=300" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fPIC" ) + endif() + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer -fstrict-aliasing" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer -fno-strict-aliasing" ) +elseif( MIPS ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fpic -fno-strict-aliasing -finline-functions -ffunction-sections -funwind-tables -fmessage-length=0" ) + set( ANDROID_CXX_FLAGS_RELEASE "-fomit-frame-pointer" ) + set( ANDROID_CXX_FLAGS_DEBUG "-fno-omit-frame-pointer" ) + if( NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fno-inline-functions-called-once -fgcse-after-reload -frerun-cse-after-loop -frename-registers" ) + set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} -funswitch-loops -finline-limit=300" ) + endif() +elseif() + set( ANDROID_CXX_FLAGS_RELEASE "" ) + set( ANDROID_CXX_FLAGS_DEBUG "" ) +endif() + +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fsigned-char" ) # good/necessary when porting desktop libraries + +if( NOT X86 AND NOT ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-Wno-psabi ${ANDROID_CXX_FLAGS}" ) +endif() + +if( NOT ANDROID_COMPILER_VERSION VERSION_LESS "4.6" ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -no-canonical-prefixes" ) # see https://android-review.googlesource.com/#/c/47564/ +endif() + +# ABI-specific flags +if( ARMEABI_V7A ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv7-a -mfloat-abi=softfp" ) + if( NEON ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=neon" ) + elseif( VFPV3 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -mfpu=vfpv3-d16" ) + endif() +elseif( ARMEABI_V6 ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv6 -mfloat-abi=softfp -mfpu=vfp" ) # vfp == vfpv2 +elseif( ARMEABI ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -march=armv5te -mtune=xscale -msoft-float" ) +endif() + +if( ANDROID_STL MATCHES "gnustl" AND (EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}") ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) +else() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_CXX_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_CXX_LINK_EXECUTABLE " -o " ) +endif() + +# STL +if( EXISTS "${__libstl}" OR EXISTS "${__libsupcxx}" ) + if( EXISTS "${__libstl}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libstl}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libstl}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libstl}\"" ) + endif() + if( EXISTS "${__libsupcxx}" ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) + # C objects: + set( CMAKE_C_CREATE_SHARED_LIBRARY " -o " ) + set( CMAKE_C_CREATE_SHARED_MODULE " -o " ) + set( CMAKE_C_LINK_EXECUTABLE " -o " ) + set( CMAKE_C_CREATE_SHARED_LIBRARY "${CMAKE_C_CREATE_SHARED_LIBRARY} \"${__libsupcxx}\"" ) + set( CMAKE_C_CREATE_SHARED_MODULE "${CMAKE_C_CREATE_SHARED_MODULE} \"${__libsupcxx}\"" ) + set( CMAKE_C_LINK_EXECUTABLE "${CMAKE_C_LINK_EXECUTABLE} \"${__libsupcxx}\"" ) + endif() + if( ANDROID_STL MATCHES "gnustl" ) + if( NOT EXISTS "${ANDROID_LIBM_PATH}" ) + set( ANDROID_LIBM_PATH -lm ) + endif() + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} ${ANDROID_LIBM_PATH}" ) + set( CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${ANDROID_LIBM_PATH}" ) + endif() +endif() + +# variables controlling optional build flags +if (ANDROID_NDK_RELEASE STRLESS "r7") + # libGLESv2.so in NDK's prior to r7 refers to missing external symbols. + # So this flag option is required for all projects using OpenGL from native. + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES ON ) +else() + __INIT_VARIABLE( ANDROID_SO_UNDEFINED VALUES OFF ) +endif() +__INIT_VARIABLE( ANDROID_NO_UNDEFINED OBSOLETE_NO_UNDEFINED VALUES ON ) +__INIT_VARIABLE( ANDROID_FUNCTION_LEVEL_LINKING VALUES ON ) +__INIT_VARIABLE( ANDROID_GOLD_LINKER VALUES ON ) +__INIT_VARIABLE( ANDROID_NOEXECSTACK VALUES ON ) +__INIT_VARIABLE( ANDROID_RELRO VALUES ON ) + +set( ANDROID_NO_UNDEFINED ${ANDROID_NO_UNDEFINED} CACHE BOOL "Show all undefined symbols as linker errors" ) +set( ANDROID_SO_UNDEFINED ${ANDROID_SO_UNDEFINED} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_FUNCTION_LEVEL_LINKING ${ANDROID_FUNCTION_LEVEL_LINKING} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_GOLD_LINKER ${ANDROID_GOLD_LINKER} CACHE BOOL "Enables gold linker (only avaialble for NDK r8b for ARM and x86 architectures on linux-86 and darwin-x86 hosts)" ) +set( ANDROID_NOEXECSTACK ${ANDROID_NOEXECSTACK} CACHE BOOL "Allows or disallows undefined symbols in shared libraries" ) +set( ANDROID_RELRO ${ANDROID_RELRO} CACHE BOOL "Enables RELRO - a memory corruption mitigation technique" ) +mark_as_advanced( ANDROID_NO_UNDEFINED ANDROID_SO_UNDEFINED ANDROID_FUNCTION_LEVEL_LINKING ANDROID_GOLD_LINKER ANDROID_NOEXECSTACK ANDROID_RELRO ) + +# linker flags +set( ANDROID_LINKER_FLAGS "" ) + +if( ARMEABI_V7A ) + # this is *required* to use the following linker flags that routes around + # a CPU bug in some Cortex-A8 implementations: + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--fix-cortex-a8" ) +endif() + +if( ANDROID_NO_UNDEFINED ) + if( MIPS ) + # there is some sysroot-related problem in mips linker... + if( NOT ANDROID_SYSROOT MATCHES "[ ;\"]" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} --sysroot=${ANDROID_SYSROOT} -Wl,--no-undefined -Wl,-rpath-link,${ANDROID_SYSROOT}/usr/lib" ) + endif() + else() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} --sysroot=${ANDROID_SYSROOT} -Wl,--no-undefined" ) + endif() +endif() + +if( ANDROID_SO_UNDEFINED ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-allow-shlib-undefined" ) +endif() + +if( ANDROID_FUNCTION_LEVEL_LINKING ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -fdata-sections -ffunction-sections" ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,--gc-sections" ) +endif() + +if( ANDROID_COMPILER_VERSION VERSION_EQUAL "4.6" ) + if( ANDROID_GOLD_LINKER AND (CMAKE_HOST_UNIX OR ANDROID_NDK_RELEASE STRGREATER "r8b") AND (ARMEABI OR ARMEABI_V7A OR X86) ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=gold" ) + elseif( ANDROID_NDK_RELEASE STRGREATER "r8b") + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -fuse-ld=bfd" ) + elseif( ANDROID_NDK_RELEASE STREQUAL "r8b" AND ARMEABI AND NOT _CMAKE_IN_TRY_COMPILE ) + message( WARNING "The default bfd linker from arm GCC 4.6 toolchain can fail with 'unresolvable R_ARM_THM_CALL relocation' error message. See https://code.google.com/p/android/issues/detail?id=35342 + On Linux and OS X host platform you can workaround this problem using gold linker (default). + Rerun cmake with -DANDROID_GOLD_LINKER=ON option in case of problems. +" ) + endif() +endif() # version 4.6 + +if( ANDROID_NOEXECSTACK ) + if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Xclang -mnoexecstack" ) + else() + set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS} -Wa,--noexecstack" ) + endif() + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,noexecstack" ) +endif() + +if( ANDROID_RELRO ) + set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} -Wl,-z,relro -Wl,-z,now" ) +endif() + +if( ANDROID_COMPILER_IS_CLANG ) + set( ANDROID_CXX_FLAGS "-Qunused-arguments ${ANDROID_CXX_FLAGS}" ) + if( ARMEABI_V7A AND NOT ANDROID_FORCE_ARM_BUILD ) + set( ANDROID_CXX_FLAGS_RELEASE "-target thumbv7-none-linux-androideabi ${ANDROID_CXX_FLAGS_RELEASE}" ) + set( ANDROID_CXX_FLAGS_DEBUG "-target ${ANDROID_LLVM_TRIPLE} ${ANDROID_CXX_FLAGS_DEBUG}" ) + else() + set( ANDROID_CXX_FLAGS "-target ${ANDROID_LLVM_TRIPLE} ${ANDROID_CXX_FLAGS}" ) + endif() + if( BUILD_WITH_ANDROID_NDK ) + set( ANDROID_CXX_FLAGS "-gcc-toolchain ${ANDROID_TOOLCHAIN_ROOT} ${ANDROID_CXX_FLAGS}" ) + endif() +endif() + +# cache flags +set( CMAKE_CXX_FLAGS "" CACHE STRING "c++ flags" ) +set( CMAKE_C_FLAGS "" CACHE STRING "c flags" ) +set( CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c++ Release flags" ) +set( CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG" CACHE STRING "c Release flags" ) +set( CMAKE_CXX_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c++ Debug flags" ) +set( CMAKE_C_FLAGS_DEBUG "-O0 -g -DDEBUG -D_DEBUG" CACHE STRING "c Debug flags" ) +set( CMAKE_SHARED_LINKER_FLAGS "" CACHE STRING "shared linker flags" ) +set( CMAKE_MODULE_LINKER_FLAGS "" CACHE STRING "module linker flags" ) +set( CMAKE_EXE_LINKER_FLAGS "-Wl,-z,nocopyreloc" CACHE STRING "executable linker flags" ) + +# put flags to cache (for debug purpose only) +set( ANDROID_CXX_FLAGS "${ANDROID_CXX_FLAGS}" CACHE INTERNAL "Android specific c/c++ flags" ) +set( ANDROID_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE}" CACHE INTERNAL "Android specific c/c++ Release flags" ) +set( ANDROID_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG}" CACHE INTERNAL "Android specific c/c++ Debug flags" ) +set( ANDROID_LINKER_FLAGS "${ANDROID_LINKER_FLAGS}" CACHE INTERNAL "Android specific c/c++ linker flags" ) + + + +# finish flags +set( CMAKE_CXX_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_CXX_FLAGS}" ) +set( CMAKE_C_FLAGS "${ANDROID_CXX_FLAGS} ${CMAKE_C_FLAGS}" ) +set( CMAKE_CXX_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}" ) +set( CMAKE_C_FLAGS_RELEASE "${ANDROID_CXX_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}" ) +set( CMAKE_CXX_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_CXX_FLAGS_DEBUG}" ) +set( CMAKE_C_FLAGS_DEBUG "${ANDROID_CXX_FLAGS_DEBUG} ${CMAKE_C_FLAGS_DEBUG}" ) +set( CMAKE_SHARED_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}" ) +set( CMAKE_MODULE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}" ) +set( CMAKE_EXE_LINKER_FLAGS "${ANDROID_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}" ) + +if( MIPS AND BUILD_WITH_ANDROID_NDK AND ANDROID_NDK_RELEASE STREQUAL "r8" ) + set( CMAKE_SHARED_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_SHARED_LINKER_FLAGS}" ) + set( CMAKE_MODULE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.xsc ${CMAKE_MODULE_LINKER_FLAGS}" ) + set( CMAKE_EXE_LINKER_FLAGS "-Wl,-T,${ANDROID_NDK_TOOLCHAINS_PATH}/${ANDROID_GCC_TOOLCHAIN_NAME}/mipself.x ${CMAKE_EXE_LINKER_FLAGS}" ) +endif() + +# configure rtti +if( DEFINED ANDROID_RTTI AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_RTTI ) + set( CMAKE_CXX_FLAGS "-frtti ${CMAKE_CXX_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-rtti ${CMAKE_CXX_FLAGS}" ) + endif() +endif() + +# configure exceptios +if( DEFINED ANDROID_EXCEPTIONS AND ANDROID_STL_FORCE_FEATURES ) + if( ANDROID_EXCEPTIONS ) + set( CMAKE_CXX_FLAGS "-fexceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fexceptions ${CMAKE_C_FLAGS}" ) + else() + set( CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}" ) + set( CMAKE_C_FLAGS "-fno-exceptions ${CMAKE_C_FLAGS}" ) + endif() +endif() + +# global includes and link directories +include_directories( SYSTEM "${ANDROID_SYSROOT}/usr/include" ${ANDROID_STL_INCLUDE_DIRS} ) +link_directories( "${CMAKE_INSTALL_PREFIX}/libs/${ANDROID_NDK_ABI_NAME}" ) + +# detect if need link crtbegin_so.o explicitly +if( NOT DEFINED ANDROID_EXPLICIT_CRT_LINK ) + set( __cmd "${CMAKE_CXX_CREATE_SHARED_LIBRARY}" ) + string( REPLACE "" "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_CXX_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_SHARED_LINKER_FLAGS}" __cmd "${__cmd}" ) + string( REPLACE "" "-shared" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + string( REPLACE "" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/toolchain_crtlink_test.so" __cmd "${__cmd}" ) + string( REPLACE "" "\"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" __cmd "${__cmd}" ) + string( REPLACE "" "" __cmd "${__cmd}" ) + separate_arguments( __cmd ) + foreach( __var ANDROID_NDK ANDROID_NDK_TOOLCHAINS_PATH ANDROID_STANDALONE_TOOLCHAIN ) + if( ${__var} ) + set( __tmp "${${__var}}" ) + separate_arguments( __tmp ) + string( REPLACE "${__tmp}" "${${__var}}" __cmd "${__cmd}") + endif() + endforeach() + string( REPLACE "'" "" __cmd "${__cmd}" ) + string( REPLACE "\"" "" __cmd "${__cmd}" ) + execute_process( COMMAND ${__cmd} RESULT_VARIABLE __cmd_result OUTPUT_QUIET ERROR_QUIET ) + if( __cmd_result EQUAL 0 ) + set( ANDROID_EXPLICIT_CRT_LINK ON ) + else() + set( ANDROID_EXPLICIT_CRT_LINK OFF ) + endif() +endif() + +if( ANDROID_EXPLICIT_CRT_LINK ) + set( CMAKE_CXX_CREATE_SHARED_LIBRARY "${CMAKE_CXX_CREATE_SHARED_LIBRARY} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) + set( CMAKE_CXX_CREATE_SHARED_MODULE "${CMAKE_CXX_CREATE_SHARED_MODULE} \"${ANDROID_SYSROOT}/usr/lib/crtbegin_so.o\"" ) +endif() + +# setup output directories +set( LIBRARY_OUTPUT_PATH_ROOT ${CMAKE_SOURCE_DIR} CACHE PATH "root for library output, set this to change where android libs are installed to" ) +set( CMAKE_INSTALL_PREFIX "${ANDROID_TOOLCHAIN_ROOT}/user" CACHE STRING "path for installing" ) + +if(NOT _CMAKE_IN_TRY_COMPILE) + if( EXISTS "${CMAKE_SOURCE_DIR}/jni/CMakeLists.txt" ) + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin/${ANDROID_NDK_ABI_NAME}" CACHE PATH "Output directory for applications" ) + else() + set( EXECUTABLE_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/bin" CACHE PATH "Output directory for applications" ) + endif() + set( LIBRARY_OUTPUT_PATH "${LIBRARY_OUTPUT_PATH_ROOT}/libs/${ANDROID_NDK_ABI_NAME}" CACHE PATH "path for android libs" ) +endif() + +# set these global flags for cmake client scripts to change behavior +set( ANDROID True ) +set( BUILD_ANDROID True ) + +# where is the target environment +set( CMAKE_FIND_ROOT_PATH "${ANDROID_TOOLCHAIN_ROOT}/bin" "${ANDROID_TOOLCHAIN_ROOT}/${ANDROID_TOOLCHAIN_MACHINE_NAME}" "${ANDROID_SYSROOT}" "${CMAKE_INSTALL_PREFIX}" "${CMAKE_INSTALL_PREFIX}/share" ) + +# only search for libraries and includes in the ndk toolchain +set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) +set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) + + +# macro to find packages on the host OS +macro( find_host_package ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) + if( CMAKE_HOST_WIN32 ) + SET( WIN32 1 ) + SET( UNIX ) + elseif( CMAKE_HOST_APPLE ) + SET( APPLE 1 ) + SET( UNIX ) + endif() + find_package( ${ARGN} ) + SET( WIN32 ) + SET( APPLE ) + SET( UNIX 1 ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) +endmacro() + + +# macro to find programs on the host OS +macro( find_host_program ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER ) + if( CMAKE_HOST_WIN32 ) + SET( WIN32 1 ) + SET( UNIX ) + elseif( CMAKE_HOST_APPLE ) + SET( APPLE 1 ) + SET( UNIX ) + endif() + find_program( ${ARGN} ) + SET( WIN32 ) + SET( APPLE ) + SET( UNIX 1 ) + set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY ) + set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY ) +endmacro() + + +macro( ANDROID_GET_ABI_RAWNAME TOOLCHAIN_FLAG VAR ) + if( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI" ) + set( ${VAR} "armeabi" ) + elseif( "${TOOLCHAIN_FLAG}" STREQUAL "ARMEABI_V7A" ) + set( ${VAR} "armeabi-v7a" ) + elseif( "${TOOLCHAIN_FLAG}" STREQUAL "X86" ) + set( ${VAR} "x86" ) + elseif( "${TOOLCHAIN_FLAG}" STREQUAL "MIPS" ) + set( ${VAR} "mips" ) + else() + set( ${VAR} "unknown" ) + endif() +endmacro() + + +# export toolchain settings for the try_compile() command +if( NOT PROJECT_NAME STREQUAL "CMAKE_TRY_COMPILE" ) + set( __toolchain_config "") + foreach( __var NDK_CCACHE LIBRARY_OUTPUT_PATH_ROOT ANDROID_FORBID_SYGWIN ANDROID_SET_OBSOLETE_VARIABLES + ANDROID_NDK_HOST_X64 + ANDROID_NDK + ANDROID_NDK_LAYOUT + ANDROID_STANDALONE_TOOLCHAIN + ANDROID_TOOLCHAIN_NAME + ANDROID_ABI + ANDROID_NATIVE_API_LEVEL + ANDROID_STL + ANDROID_STL_FORCE_FEATURES + ANDROID_FORCE_ARM_BUILD + ANDROID_NO_UNDEFINED + ANDROID_SO_UNDEFINED + ANDROID_FUNCTION_LEVEL_LINKING + ANDROID_GOLD_LINKER + ANDROID_NOEXECSTACK + ANDROID_RELRO + ANDROID_LIBM_PATH + ANDROID_EXPLICIT_CRT_LINK + ) + if( DEFINED ${__var} ) + if( "${__var}" MATCHES " ") + set( __toolchain_config "${__toolchain_config}set( ${__var} \"${${__var}}\" CACHE INTERNAL \"\" )\n" ) + else() + set( __toolchain_config "${__toolchain_config}set( ${__var} ${${__var}} CACHE INTERNAL \"\" )\n" ) + endif() + endif() + endforeach() + file( WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/android.toolchain.config.cmake" "${__toolchain_config}" ) + unset( __toolchain_config ) +endif() + + +# set some obsolete variables for backward compatibility +set( ANDROID_SET_OBSOLETE_VARIABLES ON CACHE BOOL "Define obsolete Andrid-specific cmake variables" ) +mark_as_advanced( ANDROID_SET_OBSOLETE_VARIABLES ) +if( ANDROID_SET_OBSOLETE_VARIABLES ) + set( ANDROID_API_LEVEL ${ANDROID_NATIVE_API_LEVEL} ) + set( ARM_TARGET "${ANDROID_ABI}" ) + set( ARMEABI_NDK_NAME "${ANDROID_NDK_ABI_NAME}" ) +endif() + + +# Variables controlling behavior or set by cmake toolchain: +# ANDROID_ABI : "armeabi-v7a" (default), "armeabi", "armeabi-v7a with NEON", "armeabi-v7a with VFPV3", "armeabi-v6 with VFP", "x86", "mips" +# ANDROID_NATIVE_API_LEVEL : 3,4,5,8,9,14 (depends on NDK version) +# ANDROID_STL : gnustl_static/gnustl_shared/stlport_static/stlport_shared/gabi++_static/gabi++_shared/system_re/system/none +# ANDROID_FORBID_SYGWIN : ON/OFF +# ANDROID_NO_UNDEFINED : ON/OFF +# ANDROID_SO_UNDEFINED : OFF/ON (default depends on NDK version) +# ANDROID_FUNCTION_LEVEL_LINKING : ON/OFF +# ANDROID_GOLD_LINKER : ON/OFF +# ANDROID_NOEXECSTACK : ON/OFF +# ANDROID_RELRO : ON/OFF +# ANDROID_FORCE_ARM_BUILD : ON/OFF +# ANDROID_STL_FORCE_FEATURES : ON/OFF +# ANDROID_SET_OBSOLETE_VARIABLES : ON/OFF +# Can be set only at the first run: +# ANDROID_NDK +# ANDROID_STANDALONE_TOOLCHAIN +# ANDROID_TOOLCHAIN_NAME : the NDK name of compiler toolchain +# ANDROID_NDK_HOST_X64 : try to use x86_64 toolchain (default for x64 host systems) +# ANDROID_NDK_LAYOUT : the inner NDK structure (RELEASE, LINARO, ANDROID) +# LIBRARY_OUTPUT_PATH_ROOT : +# NDK_CCACHE : +# Obsolete: +# ANDROID_API_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL +# ARM_TARGET : superseded by ANDROID_ABI +# ARM_TARGETS : superseded by ANDROID_ABI (can be set only) +# ANDROID_NDK_TOOLCHAIN_ROOT : superseded by ANDROID_STANDALONE_TOOLCHAIN (can be set only) +# ANDROID_USE_STLPORT : superseded by ANDROID_STL=stlport_static +# ANDROID_LEVEL : superseded by ANDROID_NATIVE_API_LEVEL (completely removed) +# +# Primary read-only variables: +# ANDROID : always TRUE +# ARMEABI : TRUE for arm v6 and older devices +# ARMEABI_V6 : TRUE for arm v6 +# ARMEABI_V7A : TRUE for arm v7a +# NEON : TRUE if NEON unit is enabled +# VFPV3 : TRUE if VFP version 3 is enabled +# X86 : TRUE if configured for x86 +# MIPS : TRUE if configured for mips +# BUILD_ANDROID : always TRUE +# BUILD_WITH_ANDROID_NDK : TRUE if NDK is used +# BUILD_WITH_STANDALONE_TOOLCHAIN : TRUE if standalone toolchain is used +# ANDROID_NDK_HOST_SYSTEM_NAME : "windows", "linux-x86" or "darwin-x86" depending on host platform +# ANDROID_NDK_ABI_NAME : "armeabi", "armeabi-v7a", "x86" or "mips" depending on ANDROID_ABI +# ANDROID_NDK_RELEASE : one of r5, r5b, r5c, r6, r6b, r7, r7b, r7c, r8, r8b, r8c, r8d, r8e; set only for NDK +# ANDROID_ARCH_NAME : "arm" or "x86" or "mips" depending on ANDROID_ABI +# ANDROID_SYSROOT : path to the compiler sysroot +# TOOL_OS_SUFFIX : "" or ".exe" depending on host platform +# ANDROID_COMPILER_IS_CLANG : TRUE if clang compiler is used +# Obsolete: +# ARMEABI_NDK_NAME : superseded by ANDROID_NDK_ABI_NAME +# +# Secondary (less stable) read-only variables: +# ANDROID_COMPILER_VERSION : GCC version used +# ANDROID_CXX_FLAGS : C/C++ compiler flags required by Android platform +# ANDROID_SUPPORTED_ABIS : list of currently allowed values for ANDROID_ABI +# ANDROID_TOOLCHAIN_MACHINE_NAME : "arm-linux-androideabi", "arm-eabi" or "i686-android-linux" +# ANDROID_TOOLCHAIN_ROOT : path to the top level of toolchain (standalone or placed inside NDK) +# ANDROID_CLANG_TOOLCHAIN_ROOT : path to clang tools +# ANDROID_SUPPORTED_NATIVE_API_LEVELS : list of native API levels found inside NDK +# ANDROID_STL_INCLUDE_DIRS : stl include paths +# ANDROID_RTTI : if rtti is enabled by the runtime +# ANDROID_EXCEPTIONS : if exceptions are enabled by the runtime +# ANDROID_GCC_TOOLCHAIN_NAME : read-only, differs from ANDROID_TOOLCHAIN_NAME only if clang is used +# ANDROID_CLANG_VERSION : version of clang compiler if clang is used +# ANDROID_LIBM_PATH : path to libm.so (set to something like $(TOP)/out/target/product//obj/lib/libm.so) to workaround unresolved `sincos` +# +# Defaults: +# ANDROID_DEFAULT_NDK_API_LEVEL +# ANDROID_DEFAULT_NDK_API_LEVEL_${ARCH} +# ANDROID_NDK_SEARCH_PATHS +# ANDROID_STANDALONE_TOOLCHAIN_SEARCH_PATH +# ANDROID_SUPPORTED_ABIS_${ARCH} +# ANDROID_SUPPORTED_NDK_VERSIONS diff --git a/android/scripts/ABI_compat_generator.py b/android/scripts/ABI_compat_generator.py new file mode 100755 index 0000000000..39253bbdec --- /dev/null +++ b/android/scripts/ABI_compat_generator.py @@ -0,0 +1,228 @@ +#!/usr/bin/python + +from optparse import OptionParser +from shutil import rmtree +import os + + +architecture = 'armeabi' +excludedHeaders = set(['hdf5.h', 'cap_ios.h', + 'eigen.hpp', 'cxeigen.hpp' #TOREMOVE + ]) +systemIncludes = ['sources/cxx-stl/gnu-libstdc++/4.6/include', \ + '/opt/android-ndk-r8c/platforms/android-8/arch-arm', # TODO: check if this one could be passed as command line arg + 'sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include'] +targetLibs = ['libopencv_java.so'] +preamble = ['Eigen/Core'] +# TODO: get gcc_options automatically +gcc_options = ['-fexceptions', '-frtti', '-Wno-psabi', '--sysroot=/opt/android-ndk-r8c/platforms/android-8/arch-arm', '-fpic', '-D__ARM_ARCH_5__', '-D__ARM_ARCH_5T__', '-D__ARM_ARCH_5E__', '-D__ARM_ARCH_5TE__', '-fsigned-char', '-march=armv5te', '-mtune=xscale', '-msoft-float', '-fdata-sections', '-ffunction-sections', '-Wa,--noexecstack ', '-W', '-Wall', '-Werror=return-type', '-Werror=address', '-Werror=sequence-point', '-Wformat', '-Werror=format-security', '-Wmissing-declarations', '-Wundef', '-Winit-self', '-Wpointer-arith', '-Wshadow', '-Wsign-promo', '-Wno-narrowing', '-fdiagnostics-show-option', '-fomit-frame-pointer', '-mthumb', '-fomit-frame-pointer', '-O3', '-DNDEBUG ', '-DNDEBUG'] +excludedOptionsPrefix = '-W' + + + +def GetHeaderFiles(root): + headers = [] + for path in os.listdir(root): + if not os.path.isdir(os.path.join(root, path)) \ + and os.path.splitext(path)[1] in ['.h', '.hpp'] \ + and not path in excludedHeaders: + headers.append(os.path.join(root, path)) + return sorted(headers) + + + +def GetClasses(root, prefix): + classes = [] + if ('' != prefix): + prefix = prefix + '.' + for path in os.listdir(root): + currentPath = os.path.join(root, path) + if (os.path.isdir(currentPath)): + classes += GetClasses(currentPath, prefix + path) + else: + name = str.split(path, '.')[0] + ext = str.split(path, '.')[1] + if (ext == 'class'): + classes.append(prefix + name) + return classes + + + +def GetJavaHHeaders(): + print('\nGenerating JNI headers for Java API ...') + + javahHeaders = os.path.join(managerDir, 'javah_generated_headers') + if os.path.exists(javahHeaders): + rmtree(javahHeaders) + os.makedirs(os.path.join(os.getcwd(), javahHeaders)) + + AndroidJavaDeps = os.path.join(SDK_path, 'platforms/android-11/android.jar') + + classPath = os.path.join(managerDir, 'sdk/java/bin/classes') + if not os.path.exists(classPath): + print('Error: no Java classes found in \'%s\'' % classPath) + quit() + + allJavaClasses = GetClasses(classPath, '') + if not allJavaClasses: + print('Error: no Java classes found') + quit() + + for currentClass in allJavaClasses: + os.system('javah -d %s -classpath %s:%s %s' % (javahHeaders, classPath, \ + AndroidJavaDeps, currentClass)) + + print('\nBuilding JNI headers list ...') + jniHeaders = GetHeaderFiles(javahHeaders) + + return jniHeaders + + + +def GetImmediateSubdirs(dir): + return [name for name in os.listdir(dir) + if os.path.isdir(os.path.join(dir, name))] + + + +def GetOpenCVModules(): + makefile = open(os.path.join(managerDir, 'sdk/native/jni/OpenCV.mk'), 'r') + makefileStr = makefile.read() + left = makefileStr.find('OPENCV_MODULES:=') + len('OPENCV_MODULES:=') + right = makefileStr[left:].find('\n') + modules = makefileStr[left:left+right].split() + modules = filter(lambda x: x != 'ts' and x != 'androidcamera', modules) + return modules + + + +def FindHeaders(): + headers = [] + + print('\nBuilding Native OpenCV header list ...') + + cppHeadersFolder = os.path.join(managerDir, 'sdk/native/jni/include/opencv2') + + modulesFolders = GetImmediateSubdirs(cppHeadersFolder) + modules = GetOpenCVModules() + + cppHeaders = [] + for m in modules: + for f in modulesFolders: + moduleHeaders = [] + if f == m: + moduleHeaders += GetHeaderFiles(os.path.join(cppHeadersFolder, f)) + if m == 'flann': + flann = os.path.join(cppHeadersFolder, f, 'flann.hpp') + moduleHeaders.remove(flann) + moduleHeaders.insert(0, flann) + cppHeaders += moduleHeaders + + + cppHeaders += GetHeaderFiles(cppHeadersFolder) + headers += cppHeaders + + cHeaders = GetHeaderFiles(os.path.join(managerDir, \ + 'sdk/native/jni/include/opencv')) + headers += cHeaders + + headers += GetJavaHHeaders() + + return headers + + + +def FindLibraries(): + libraries = [] + for lib in targetLibs: + libraries.append(os.path.join(managerDir, 'sdk/native/libs', architecture, lib)) + return libraries + + + +def FindIncludes(): + includes = [os.path.join(managerDir, 'sdk', 'native', 'jni', 'include'), + os.path.join(managerDir, 'sdk', 'native', 'jni', 'include', 'opencv'), + os.path.join(managerDir, 'sdk', 'native', 'jni', 'include', 'opencv2')] + + for inc in systemIncludes: + includes.append(os.path.join(NDK_path, inc)) + + return includes + + + +def FilterGCCOptions(): + gcc = filter(lambda x: not x.startswith(excludedOptionsPrefix), gcc_options) + return sorted(gcc) + + + +def WriteXml(version, headers, includes, libraries): + xmlName = version + '.xml' + + print '\noutput file: ' + xmlName + try: + xml = open(xmlName, 'w') + except: + print 'Error: Cannot open output file "%s" for writing' % xmlName + quit() + + xml.write('') + + xml.write('\n\n') + xml.write('\n\t%s' % version) + xml.write('\n') + + xml.write('\n\n') + xml.write('\n\t%s' % '\n\t'.join(headers)) + xml.write('\n') + + xml.write('\n\n') + xml.write('\n\t%s' % '\n\t'.join(includes)) + xml.write('\n') + + # TODO: uncomment when Eigen problem is solved + # xml.write('\n\n') + # xml.write('\n\t%s' % '\n\t'.join(preamble)) + # xml.write('\n') + + xml.write('\n\n') + xml.write('\n\t%s' % '\n\t'.join(libraries)) + xml.write('\n') + + xml.write('\n\n') + xml.write('\n\t%s' % '\n\t'.join(gcc_options)) + xml.write('\n') + + xml.write('\n\n') + + + +if __name__ == '__main__': + usage = '%prog ' + parser = OptionParser(usage = usage) + + args = parser.parse_args() + if 2 != len(args): + parser.print_help() + quit() + + managerDir = args[1][0] + version = args[1][1] + + NDK_path = '/opt/android-ndk-r8c' + print '\nUsing Android NDK from "%s"' % NDK_path + + SDK_path = '~/NVPACK/android-sdk-linux' + print '\nUsing Android SDK from "%s"' % SDK_path + + headers = FindHeaders() + + includes = FindIncludes() + + libraries = FindLibraries() + + gcc_options = FilterGCCOptions() + + WriteXml(version, headers, includes, libraries) diff --git a/android/scripts/build.cmd b/android/scripts/build.cmd new file mode 100644 index 0000000000..45bd643501 --- /dev/null +++ b/android/scripts/build.cmd @@ -0,0 +1,90 @@ +@ECHO OFF + +:: enable command extensions +VERIFY BADVALUE 2>NUL +SETLOCAL ENABLEEXTENSIONS || (ECHO Unable to enable command extensions. & EXIT \B) + +:: build environment +SET SOURCE_DIR=%cd% +IF EXIST .\android.toolchain.cmake (SET BUILD_OPENCV=1) ELSE (SET BUILD_OPENCV=0) +IF EXIST .\jni\nul (SET BUILD_JAVA_PART=1) ELSE (SET BUILD_JAVA_PART=0) + +:: load configuration +PUSHD %~dp0 +SET SCRIPTS_DIR=%cd% +IF EXIST .\wincfg.cmd CALL .\wincfg.cmd +POPD + +:: inherit old names +IF NOT DEFINED CMAKE SET CMAKE=%CMAKE_EXE% +IF NOT DEFINED MAKE SET MAKE=%MAKE_EXE% + +:: defaults +IF NOT DEFINED BUILD_DIR SET BUILD_DIR=build +IF NOT DEFINED ANDROID_ABI SET ANDROID_ABI=armeabi-v7a +SET OPENCV_BUILD_DIR=%SCRIPTS_DIR%\..\%BUILD_DIR% + +:: check that all required variables defined +PUSHD . +IF NOT DEFINED ANDROID_NDK (ECHO. & ECHO You should set an environment variable ANDROID_NDK to the full path to your copy of Android NDK & GOTO end) +(CD "%ANDROID_NDK%") || (ECHO. & ECHO Directory "%ANDROID_NDK%" specified by ANDROID_NDK variable does not exist & GOTO end) + +IF NOT EXIST "%CMAKE%" (ECHO. & ECHO You should set an environment variable CMAKE to the full path to cmake executable & GOTO end) +IF NOT EXIST "%MAKE%" (ECHO. & ECHO You should set an environment variable MAKE to the full path to native port of make executable & GOTO end) + +IF NOT %BUILD_JAVA_PART%==1 GOTO required_variables_checked + +IF NOT DEFINED ANDROID_SDK (ECHO. & ECHO You should set an environment variable ANDROID_SDK to the full path to your copy of Android SDK & GOTO end) +(CD "%ANDROID_SDK%" 2>NUL) || (ECHO. & ECHO Directory "%ANDROID_SDK%" specified by ANDROID_SDK variable does not exist & GOTO end) + +IF NOT DEFINED ANT_DIR (ECHO. & ECHO You should set an environment variable ANT_DIR to the full path to Apache Ant root & GOTO end) +(CD "%ANT_DIR%" 2>NUL) || (ECHO. & ECHO Directory "%ANT_DIR%" specified by ANT_DIR variable does not exist & GOTO end) + +IF NOT DEFINED JAVA_HOME (ECHO. & ECHO You should set an environment variable JAVA_HOME to the full path to JDK & GOTO end) +(CD "%JAVA_HOME%" 2>NUL) || (ECHO. & ECHO Directory "%JAVA_HOME%" specified by JAVA_HOME variable does not exist & GOTO end) + +:required_variables_checked +POPD + +:: check for ninja +echo "%MAKE%"|findstr /i ninja >nul: +IF %errorlevel%==1 (SET BUILD_WITH_NINJA=0) ELSE (SET BUILD_WITH_NINJA=1) +IF %BUILD_WITH_NINJA%==1 (SET CMAKE_GENERATOR=Ninja) ELSE (SET CMAKE_GENERATOR=MinGW Makefiles) + +:: create build dir +IF DEFINED REBUILD rmdir /S /Q "%BUILD_DIR%" 2>NUL +MKDIR "%BUILD_DIR%" 2>NUL +PUSHD "%BUILD_DIR%" || (ECHO. & ECHO Directory "%BUILD_DIR%" is not found & GOTO end) + +:: run cmake +ECHO. & ECHO Runnning cmake... +ECHO ANDROID_ABI=%ANDROID_ABI% +ECHO. +IF NOT %BUILD_OPENCV%==1 GOTO other-cmake +:opencv-cmake +("%CMAKE%" -G"%CMAKE_GENERATOR%" -DANDROID_ABI="%ANDROID_ABI%" -DCMAKE_TOOLCHAIN_FILE="%SOURCE_DIR%"\android.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%MAKE%" %* "%SOURCE_DIR%\..") && GOTO cmakefin +ECHO. & ECHO cmake failed & GOTO end +:other-cmake +("%CMAKE%" -G"%CMAKE_GENERATOR%" -DANDROID_ABI="%ANDROID_ABI%" -DOpenCV_DIR="%OPENCV_BUILD_DIR%" -DCMAKE_TOOLCHAIN_FILE="%OPENCV_BUILD_DIR%\..\android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM="%MAKE%" %* "%SOURCE_DIR%") && GOTO cmakefin +ECHO. & ECHO cmake failed & GOTO end +:cmakefin + +:: run make +ECHO. & ECHO Building native libs... +IF %BUILD_WITH_NINJA%==0 ("%MAKE%" -j %NUMBER_OF_PROCESSORS% VERBOSE=%VERBOSE%) || (ECHO. & ECHO make failed & GOTO end) +IF %BUILD_WITH_NINJA%==1 ("%MAKE%") || (ECHO. & ECHO ninja failed & GOTO end) + +IF NOT %BUILD_JAVA_PART%==1 GOTO end +POPD && PUSHD %SOURCE_DIR% + +:: configure java part +ECHO. & ECHO Updating Android project... +(CALL "%ANDROID_SDK%\tools\android" update project --name %PROJECT_NAME% --path .) || (ECHO. & ECHO failed to update android project & GOTO end) + +:: compile java part +ECHO. & ECHO Compiling Android project... +(CALL "%ANT_DIR%\bin\ant" debug) || (ECHO. & ECHO failed to compile android project & GOTO end) + +:end +POPD +ENDLOCAL diff --git a/android/scripts/camera_build.conf b/android/scripts/camera_build.conf new file mode 100644 index 0000000000..cd172b4fde --- /dev/null +++ b/android/scripts/camera_build.conf @@ -0,0 +1,23 @@ +# make target; arch; API level; Android Source Code Root +native_camera_r2.2.0; armeabi; 8; $ANDROID_STUB_ROOT/2.2.2 +native_camera_r2.2.0; armeabi-v7a; 8; $ANDROID_STUB_ROOT/2.2.2 +native_camera_r2.3.3; armeabi; 9; $ANDROID_STUB_ROOT/2.3.3 +native_camera_r2.3.3; armeabi-v7a; 9; $ANDROID_STUB_ROOT/2.3.3 +native_camera_r2.3.3; x86; 9; $ANDROID_STUB_ROOT/2.3.3 +native_camera_r3.0.1; armeabi; 9; $ANDROID_STUB_ROOT/3.0.1 +native_camera_r3.0.1; armeabi-v7a; 9; $ANDROID_STUB_ROOT/3.0.1 +native_camera_r3.0.1; x86; 9; $ANDROID_STUB_ROOT/3.0.1 +native_camera_r4.0.3; armeabi; 14; $ANDROID_STUB_ROOT/4.0.3 +native_camera_r4.0.3; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.0.3 +native_camera_r4.0.3; x86; 14; $ANDROID_STUB_ROOT/4.0.3 +native_camera_r4.0.3; mips; 14; $ANDROID_STUB_ROOT/4.0.3_mips +native_camera_r4.0.0; armeabi; 14; $ANDROID_STUB_ROOT/4.0.0 +native_camera_r4.0.0; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.0.0 +native_camera_r4.1.1; armeabi; 14; $ANDROID_STUB_ROOT/4.1.1 +native_camera_r4.1.1; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.1.1 +native_camera_r4.1.1; x86; 14; $ANDROID_STUB_ROOT/4.1.1 +native_camera_r4.1.1; mips; 14; $ANDROID_STUB_ROOT/4.1.1 +native_camera_r4.2.0; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.2.0 +native_camera_r4.2.0; armeabi; 14; $ANDROID_STUB_ROOT/4.2.0 +native_camera_r4.2.0; x86; 14; $ANDROID_STUB_ROOT/4.2.0 +native_camera_r4.2.0; mips; 14; $ANDROID_STUB_ROOT/4.2.0 diff --git a/android/scripts/cmake_android.cmd b/android/scripts/cmake_android.cmd new file mode 100644 index 0000000000..3e6d923953 --- /dev/null +++ b/android/scripts/cmake_android.cmd @@ -0,0 +1,5 @@ +@ECHO OFF + +PUSHD %~dp0.. +CALL .\scripts\build.cmd %* -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON +POPD \ No newline at end of file diff --git a/android/scripts/cmake_android.sh b/android/scripts/cmake_android.sh new file mode 100755 index 0000000000..101ba3cee8 --- /dev/null +++ b/android/scripts/cmake_android.sh @@ -0,0 +1,8 @@ +#!/bin/sh +cd `dirname $0`/.. + +mkdir -p build +cd build + +cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. + diff --git a/android/scripts/cmake_android_all_cameras.py b/android/scripts/cmake_android_all_cameras.py new file mode 100755 index 0000000000..0ef430a3d4 --- /dev/null +++ b/android/scripts/cmake_android_all_cameras.py @@ -0,0 +1,80 @@ +#!/usr/bin/python + +import os +import sys +import shutil + +ScriptHome = os.path.split(sys.argv[0])[0] +ConfFile = open(os.path.join(ScriptHome, "camera_build.conf"), "rt") +HomeDir = os.getcwd() + +stub = "" +try: + stub = os.environ["ANDROID_STUB_ROOT"] +except: + None + +if (stub == ""): + print("Warning: ANDROID_STUB_ROOT environment variable is not set") + +for s in ConfFile.readlines(): + s = s[0:s.find("#")] + if (not s): + continue + keys = s.split(";") + if (len(keys) < 4): + print("Error: invalid config line: \"%s\"" % s) + continue + MakeTarget = str.strip(keys[0]) + Arch = str.strip(keys[1]) + NativeApiLevel = str.strip(keys[2]) + AndroidTreeRoot = str.strip(keys[3]) + AndroidTreeRoot = str.strip(AndroidTreeRoot, "\n") + AndroidTreeRoot = os.path.expandvars(AndroidTreeRoot) + print("Building %s for %s" % (MakeTarget, Arch)) + BuildDir = os.path.join(HomeDir, MakeTarget + "_" + Arch) + + if (os.path.exists(BuildDir)): + shutil.rmtree(BuildDir) + + try: + os.mkdir(BuildDir) + except: + print("Error: cannot create direcotry \"%s\"" % BuildDir) + continue + + shutil.rmtree(os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system"), ignore_errors=True) + + LinkerLibs = os.path.join(AndroidTreeRoot, "bin_arm", "system") + if (Arch == "x86"): + LinkerLibs = os.path.join(AndroidTreeRoot, "bin_x86", "system") + elif (Arch == "mips"): + LinkerLibs = os.path.join(AndroidTreeRoot, "bin_mips", "system") + + if (not os.path.exists(LinkerLibs)): + print("Error: Paltform libs for linker in path \"%s\" not found" % LinkerLibs) + print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)) + continue + + shutil.copytree(LinkerLibs, os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system")) + + os.chdir(BuildDir) + BuildLog = os.path.join(BuildDir, "build.log") + CmakeCmdLine = "cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_SOURCE_TREE=\"%s\" -DANDROID_NATIVE_API_LEVEL=\"%s\" -DANDROID_ABI=\"%s\" -DANDROID_STL=stlport_static ../../ > \"%s\" 2>&1" % (AndroidTreeRoot, NativeApiLevel, Arch, BuildLog) + MakeCmdLine = "make %s >> \"%s\" 2>&1" % (MakeTarget, BuildLog); + #print(CmakeCmdLine) + os.system(CmakeCmdLine) + #print(MakeCmdLine) + os.system(MakeCmdLine) + os.chdir(HomeDir) + CameraLib = os.path.join(BuildDir, "lib", Arch, "lib" + MakeTarget + ".so") + if (os.path.exists(CameraLib)): + try: + shutil.copyfile(CameraLib, os.path.join("..", "3rdparty", "lib", Arch, "lib" + MakeTarget + ".so")) + print("Building %s for %s\t[\033[92mOK\033[0m]" % (MakeTarget, Arch)); + except: + print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)); + else: + print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)); + +ConfFile.close() diff --git a/android/scripts/cmake_android_armeabi.sh b/android/scripts/cmake_android_armeabi.sh new file mode 100755 index 0000000000..cab1800174 --- /dev/null +++ b/android/scripts/cmake_android_armeabi.sh @@ -0,0 +1,29 @@ +#!/bin/sh +cd `dirname $0`/.. + +mkdir -p build_armeabi +cd build_armeabi + +cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/armv5 $@ ../.. +make +make install + + +cd ../ +mkdir -p build_armeabi-v7a +cd build_armeabi-v7a + +cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/armv7a $@ ../.. +make +make install + +cd ../ +mkdir -p build_x86 +cd build_x86 + +cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/x86 $@ ../.. +make +make install + +# cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../scripts/toolchain-android-ndk-r8e.cmake $@ ../.. + diff --git a/android/scripts/cmake_android_mips.sh b/android/scripts/cmake_android_mips.sh new file mode 100755 index 0000000000..17d2ff937e --- /dev/null +++ b/android/scripts/cmake_android_mips.sh @@ -0,0 +1,8 @@ +#!/bin/sh +cd `dirname $0`/.. + +mkdir -p build_mips +cd build_mips + +cmake -DANDROID_ABI=mips -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. + diff --git a/android/scripts/cmake_android_neon.sh b/android/scripts/cmake_android_neon.sh new file mode 100755 index 0000000000..5e85605b56 --- /dev/null +++ b/android/scripts/cmake_android_neon.sh @@ -0,0 +1,8 @@ +#!/bin/sh +cd `dirname $0`/.. + +mkdir -p build_neon +cd build_neon + +cmake -DANDROID_ABI="armeabi-v7a with NEON" -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. + diff --git a/android/scripts/cmake_android_service.sh b/android/scripts/cmake_android_service.sh new file mode 100755 index 0000000000..0dbd482520 --- /dev/null +++ b/android/scripts/cmake_android_service.sh @@ -0,0 +1,7 @@ +#!/bin/sh +cd `dirname $0`/.. + +mkdir -p build_service +cd build_service + +cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME="arm-linux-androideabi-4.4.3" -DANDROID_STL=stlport_static -DANDROID_STL_FORCE_FEATURES=OFF -DBUILD_ANDROID_SERVICE=ON -DANDROID_SOURCE_TREE=~/Projects/AndroidSource/ServiceStub/ $@ ../.. diff --git a/android/scripts/cmake_android_x86.sh b/android/scripts/cmake_android_x86.sh new file mode 100755 index 0000000000..a01df2e668 --- /dev/null +++ b/android/scripts/cmake_android_x86.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +cd `dirname $0`/.. + +mkdir -p build_x86 +cd build_x86 + +cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. + diff --git a/android/scripts/wincfg.cmd.tmpl b/android/scripts/wincfg.cmd.tmpl new file mode 100644 index 0000000000..f4168f5b11 --- /dev/null +++ b/android/scripts/wincfg.cmd.tmpl @@ -0,0 +1,30 @@ +:: variables required for OpenCV build :: +:: Note: all pathes should be specified without tailing slashes! +SET ANDROID_NDK=C:\full\path\to\your\copy\of\android\NDK\android-ndk-r7b +SET CMAKE_EXE=C:\full\path\to\cmake\utility\cmake.exe +SET MAKE_EXE=%ANDROID_NDK%\prebuilt\windows\bin\make.exe + +:: variables required for android-opencv build :: +SET ANDROID_SDK=C:\full\path\to\your\copy\of\android\SDK\android-sdk-windows +SET ANT_DIR=C:\full\path\to\ant\directory\apache-ant-1.8.2 +SET JAVA_HOME=C:\full\path\to\JDK\jdk1.6.0_25 + +:: configuration options :: +:::: general ARM-V7 settings +SET ANDROID_ABI=armeabi-v7a +SET BUILD_DIR=build + +:::: uncomment following lines to compile for old emulator or old device +::SET ANDROID_ABI=armeabi +::SET BUILD_DIR=build_armeabi + +:::: uncomment following lines to compile for ARM-V7 with NEON support +::SET ANDROID_ABI=armeabi-v7a with NEON +::SET BUILD_DIR=build_neon + +:::: uncomment following lines to compile for x86 +::SET ANDROID_ABI=x86 +::SET BUILD_DIR=build_x86 + +:::: other options +::SET ANDROID_NATIVE_API_LEVEL=8 &:: android-3 is enough for native part of OpenCV but android-8 is required for Java API From 167f0cffa4c5ebd76b59492fc012f4c1ab2aaae3 Mon Sep 17 00:00:00 2001 From: James Chen Date: Thu, 30 May 2013 21:32:24 +0800 Subject: [PATCH 04/10] SHA1 -> ws_SHA1. --- lib/client.c | 2 +- lib/private-libwebsockets.h | 4 ++-- lib/server-handshake.c | 2 +- lib/sha-1.c | 2 +- test-server/test-ping.c | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/client.c b/lib/client.c index b6236a2b80..dc6dacb94f 100644 --- a/lib/client.c +++ b/lib/client.c @@ -850,7 +850,7 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, key_b64[39] = '\0'; /* enforce composed length below buf sizeof */ n = sprintf(buf, "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", key_b64); - SHA1((unsigned char *)buf, n, (unsigned char *)hash); + ws_SHA1((unsigned char *)buf, n, (unsigned char *)hash); lws_b64_encode_string(hash, 20, wsi->u.hdr.ah->initial_handshake_hash_base64, diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index d1ca6d2135..bb6f64544c 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -96,7 +96,7 @@ #include #include unsigned char * -SHA1(const unsigned char *d, size_t n, unsigned char *md); +ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); #else #include #include @@ -532,7 +532,7 @@ extern int interface_to_sa(const char *ifname, #ifndef LWS_OPENSSL_SUPPORT unsigned char * -SHA1(const unsigned char *d, size_t n, unsigned char *md); +ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); #else diff --git a/lib/server-handshake.c b/lib/server-handshake.c index 53da4e7ad5..536ce8ccb4 100644 --- a/lib/server-handshake.c +++ b/lib/server-handshake.c @@ -64,7 +64,7 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); - SHA1(context->service_buffer, n, hash); + ws_SHA1(context->service_buffer, n, hash); accept_len = lws_b64_encode_string((char *)hash, 20, (char *)context->service_buffer, diff --git a/lib/sha-1.c b/lib/sha-1.c index 6020c227e0..67b17e1eb2 100644 --- a/lib/sha-1.c +++ b/lib/sha-1.c @@ -313,7 +313,7 @@ sha1_result(struct sha1_ctxt *ctxt, void *digest0) */ unsigned char * -SHA1(const unsigned char *d, size_t n, unsigned char *md) +ws_SHA1(const unsigned char *d, size_t n, unsigned char *md) { struct sha1_ctxt ctx; diff --git a/test-server/test-ping.c b/test-server/test-ping.c index 098981cf7a..155ac7dd30 100644 --- a/test-server/test-ping.c +++ b/test-server/test-ping.c @@ -338,7 +338,7 @@ int main(int argc, char **argv) char ip[30]; #ifndef WIN32 struct sigaction sa; - struct winsize w; + // struct winsize w; #endif struct timeval tv; unsigned long oldus = 0; @@ -420,10 +420,10 @@ int main(int argc, char **argv) } #ifndef WIN32 - if (isatty(STDOUT_FILENO)) - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) - if (w.ws_col > 0) - screen_width = w.ws_col; + // if (isatty(STDOUT_FILENO)) + // if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + // if (w.ws_col > 0) + // screen_width = w.ws_col; #endif info.port = CONTEXT_PORT_NO_LISTEN; From 482c279b5daedea61288e4e50944f46b01888254 Mon Sep 17 00:00:00 2001 From: James Chen Date: Thu, 1 Aug 2013 11:42:14 +0800 Subject: [PATCH 05/10] Updating script for android. --- CMakeLists.txt | 34 +-- android/scripts/ABI_compat_generator.py | 228 ------------------- android/scripts/build.cmd | 90 -------- android/scripts/camera_build.conf | 23 -- android/scripts/cmake_android.cmd | 5 - android/scripts/cmake_android.sh | 8 - android/scripts/cmake_android_all_cameras.py | 80 ------- android/scripts/cmake_android_armeabi.sh | 13 +- android/scripts/cmake_android_mips.sh | 8 - android/scripts/cmake_android_neon.sh | 8 - android/scripts/cmake_android_service.sh | 7 - android/scripts/cmake_android_x86.sh | 9 - android/scripts/wincfg.cmd.tmpl | 30 --- 13 files changed, 26 insertions(+), 517 deletions(-) delete mode 100755 android/scripts/ABI_compat_generator.py delete mode 100644 android/scripts/build.cmd delete mode 100644 android/scripts/camera_build.conf delete mode 100644 android/scripts/cmake_android.cmd delete mode 100755 android/scripts/cmake_android.sh delete mode 100755 android/scripts/cmake_android_all_cameras.py delete mode 100755 android/scripts/cmake_android_mips.sh delete mode 100755 android/scripts/cmake_android_neon.sh delete mode 100755 android/scripts/cmake_android_service.sh delete mode 100755 android/scripts/cmake_android_x86.sh delete mode 100644 android/scripts/wincfg.cmd.tmpl diff --git a/CMakeLists.txt b/CMakeLists.txt index b1ee45683b..15bd8fcdd4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -323,10 +323,10 @@ add_library(websockets STATIC ${HDR_PRIVATE} ${HDR_PUBLIC} ${SOURCES}) -#add_library(websockets_shared SHARED -# ${HDR_PRIVATE} -# ${HDR_PUBLIC} -# ${SOURCES}) +add_library(websockets_shared SHARED + ${HDR_PRIVATE} + ${HDR_PUBLIC} + ${SOURCES}) if (WIN32) # On Windows libs have the same file ending (.lib) @@ -347,19 +347,19 @@ endif(WIN32) # We want the shared lib to be named "libwebsockets" # not "libwebsocket_shared". -# set_target_properties(websockets_shared -# PROPERTIES -# OUTPUT_NAME websockets) +set_target_properties(websockets_shared + PROPERTIES + OUTPUT_NAME websockets) # Set the so version of the lib. # Equivalent to LDFLAGS=-version-info x:x:x -# if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) -# foreach(lib websockets websockets_shared) -# set_target_properties(${lib} -# PROPERTIES -# SOVERSION ${SOVERSION}) -# endforeach() -# endif() +if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + foreach(lib websockets websockets_shared) + set_target_properties(${lib} + PROPERTIES + SOVERSION ${SOVERSION}) + endforeach() +endif() set(LIB_LIST) @@ -407,7 +407,7 @@ if (NOT WITHOUT_EXTENSIONS) endif() # Make sure ZLib is compiled before the libs. - foreach (lib websockets) + foreach (lib websockets websockets_shared) add_dependencies(${lib} ZLIB) endforeach() @@ -461,7 +461,7 @@ if (UNIX) endif() # Setup the linking for all libs. -foreach (lib websockets) +foreach (lib websockets websockets_shared) target_link_libraries(${lib} ${LIB_LIST}) endforeach() @@ -726,7 +726,7 @@ install(FILES ${HDR_PUBLIC} set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Header files") # Install libs. -install(TARGETS websockets +install(TARGETS websockets websockets_shared LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} COMPONENT libraries) diff --git a/android/scripts/ABI_compat_generator.py b/android/scripts/ABI_compat_generator.py deleted file mode 100755 index 39253bbdec..0000000000 --- a/android/scripts/ABI_compat_generator.py +++ /dev/null @@ -1,228 +0,0 @@ -#!/usr/bin/python - -from optparse import OptionParser -from shutil import rmtree -import os - - -architecture = 'armeabi' -excludedHeaders = set(['hdf5.h', 'cap_ios.h', - 'eigen.hpp', 'cxeigen.hpp' #TOREMOVE - ]) -systemIncludes = ['sources/cxx-stl/gnu-libstdc++/4.6/include', \ - '/opt/android-ndk-r8c/platforms/android-8/arch-arm', # TODO: check if this one could be passed as command line arg - 'sources/cxx-stl/gnu-libstdc++/4.6/libs/armeabi-v7a/include'] -targetLibs = ['libopencv_java.so'] -preamble = ['Eigen/Core'] -# TODO: get gcc_options automatically -gcc_options = ['-fexceptions', '-frtti', '-Wno-psabi', '--sysroot=/opt/android-ndk-r8c/platforms/android-8/arch-arm', '-fpic', '-D__ARM_ARCH_5__', '-D__ARM_ARCH_5T__', '-D__ARM_ARCH_5E__', '-D__ARM_ARCH_5TE__', '-fsigned-char', '-march=armv5te', '-mtune=xscale', '-msoft-float', '-fdata-sections', '-ffunction-sections', '-Wa,--noexecstack ', '-W', '-Wall', '-Werror=return-type', '-Werror=address', '-Werror=sequence-point', '-Wformat', '-Werror=format-security', '-Wmissing-declarations', '-Wundef', '-Winit-self', '-Wpointer-arith', '-Wshadow', '-Wsign-promo', '-Wno-narrowing', '-fdiagnostics-show-option', '-fomit-frame-pointer', '-mthumb', '-fomit-frame-pointer', '-O3', '-DNDEBUG ', '-DNDEBUG'] -excludedOptionsPrefix = '-W' - - - -def GetHeaderFiles(root): - headers = [] - for path in os.listdir(root): - if not os.path.isdir(os.path.join(root, path)) \ - and os.path.splitext(path)[1] in ['.h', '.hpp'] \ - and not path in excludedHeaders: - headers.append(os.path.join(root, path)) - return sorted(headers) - - - -def GetClasses(root, prefix): - classes = [] - if ('' != prefix): - prefix = prefix + '.' - for path in os.listdir(root): - currentPath = os.path.join(root, path) - if (os.path.isdir(currentPath)): - classes += GetClasses(currentPath, prefix + path) - else: - name = str.split(path, '.')[0] - ext = str.split(path, '.')[1] - if (ext == 'class'): - classes.append(prefix + name) - return classes - - - -def GetJavaHHeaders(): - print('\nGenerating JNI headers for Java API ...') - - javahHeaders = os.path.join(managerDir, 'javah_generated_headers') - if os.path.exists(javahHeaders): - rmtree(javahHeaders) - os.makedirs(os.path.join(os.getcwd(), javahHeaders)) - - AndroidJavaDeps = os.path.join(SDK_path, 'platforms/android-11/android.jar') - - classPath = os.path.join(managerDir, 'sdk/java/bin/classes') - if not os.path.exists(classPath): - print('Error: no Java classes found in \'%s\'' % classPath) - quit() - - allJavaClasses = GetClasses(classPath, '') - if not allJavaClasses: - print('Error: no Java classes found') - quit() - - for currentClass in allJavaClasses: - os.system('javah -d %s -classpath %s:%s %s' % (javahHeaders, classPath, \ - AndroidJavaDeps, currentClass)) - - print('\nBuilding JNI headers list ...') - jniHeaders = GetHeaderFiles(javahHeaders) - - return jniHeaders - - - -def GetImmediateSubdirs(dir): - return [name for name in os.listdir(dir) - if os.path.isdir(os.path.join(dir, name))] - - - -def GetOpenCVModules(): - makefile = open(os.path.join(managerDir, 'sdk/native/jni/OpenCV.mk'), 'r') - makefileStr = makefile.read() - left = makefileStr.find('OPENCV_MODULES:=') + len('OPENCV_MODULES:=') - right = makefileStr[left:].find('\n') - modules = makefileStr[left:left+right].split() - modules = filter(lambda x: x != 'ts' and x != 'androidcamera', modules) - return modules - - - -def FindHeaders(): - headers = [] - - print('\nBuilding Native OpenCV header list ...') - - cppHeadersFolder = os.path.join(managerDir, 'sdk/native/jni/include/opencv2') - - modulesFolders = GetImmediateSubdirs(cppHeadersFolder) - modules = GetOpenCVModules() - - cppHeaders = [] - for m in modules: - for f in modulesFolders: - moduleHeaders = [] - if f == m: - moduleHeaders += GetHeaderFiles(os.path.join(cppHeadersFolder, f)) - if m == 'flann': - flann = os.path.join(cppHeadersFolder, f, 'flann.hpp') - moduleHeaders.remove(flann) - moduleHeaders.insert(0, flann) - cppHeaders += moduleHeaders - - - cppHeaders += GetHeaderFiles(cppHeadersFolder) - headers += cppHeaders - - cHeaders = GetHeaderFiles(os.path.join(managerDir, \ - 'sdk/native/jni/include/opencv')) - headers += cHeaders - - headers += GetJavaHHeaders() - - return headers - - - -def FindLibraries(): - libraries = [] - for lib in targetLibs: - libraries.append(os.path.join(managerDir, 'sdk/native/libs', architecture, lib)) - return libraries - - - -def FindIncludes(): - includes = [os.path.join(managerDir, 'sdk', 'native', 'jni', 'include'), - os.path.join(managerDir, 'sdk', 'native', 'jni', 'include', 'opencv'), - os.path.join(managerDir, 'sdk', 'native', 'jni', 'include', 'opencv2')] - - for inc in systemIncludes: - includes.append(os.path.join(NDK_path, inc)) - - return includes - - - -def FilterGCCOptions(): - gcc = filter(lambda x: not x.startswith(excludedOptionsPrefix), gcc_options) - return sorted(gcc) - - - -def WriteXml(version, headers, includes, libraries): - xmlName = version + '.xml' - - print '\noutput file: ' + xmlName - try: - xml = open(xmlName, 'w') - except: - print 'Error: Cannot open output file "%s" for writing' % xmlName - quit() - - xml.write('') - - xml.write('\n\n') - xml.write('\n\t%s' % version) - xml.write('\n') - - xml.write('\n\n') - xml.write('\n\t%s' % '\n\t'.join(headers)) - xml.write('\n') - - xml.write('\n\n') - xml.write('\n\t%s' % '\n\t'.join(includes)) - xml.write('\n') - - # TODO: uncomment when Eigen problem is solved - # xml.write('\n\n') - # xml.write('\n\t%s' % '\n\t'.join(preamble)) - # xml.write('\n') - - xml.write('\n\n') - xml.write('\n\t%s' % '\n\t'.join(libraries)) - xml.write('\n') - - xml.write('\n\n') - xml.write('\n\t%s' % '\n\t'.join(gcc_options)) - xml.write('\n') - - xml.write('\n\n') - - - -if __name__ == '__main__': - usage = '%prog ' - parser = OptionParser(usage = usage) - - args = parser.parse_args() - if 2 != len(args): - parser.print_help() - quit() - - managerDir = args[1][0] - version = args[1][1] - - NDK_path = '/opt/android-ndk-r8c' - print '\nUsing Android NDK from "%s"' % NDK_path - - SDK_path = '~/NVPACK/android-sdk-linux' - print '\nUsing Android SDK from "%s"' % SDK_path - - headers = FindHeaders() - - includes = FindIncludes() - - libraries = FindLibraries() - - gcc_options = FilterGCCOptions() - - WriteXml(version, headers, includes, libraries) diff --git a/android/scripts/build.cmd b/android/scripts/build.cmd deleted file mode 100644 index 45bd643501..0000000000 --- a/android/scripts/build.cmd +++ /dev/null @@ -1,90 +0,0 @@ -@ECHO OFF - -:: enable command extensions -VERIFY BADVALUE 2>NUL -SETLOCAL ENABLEEXTENSIONS || (ECHO Unable to enable command extensions. & EXIT \B) - -:: build environment -SET SOURCE_DIR=%cd% -IF EXIST .\android.toolchain.cmake (SET BUILD_OPENCV=1) ELSE (SET BUILD_OPENCV=0) -IF EXIST .\jni\nul (SET BUILD_JAVA_PART=1) ELSE (SET BUILD_JAVA_PART=0) - -:: load configuration -PUSHD %~dp0 -SET SCRIPTS_DIR=%cd% -IF EXIST .\wincfg.cmd CALL .\wincfg.cmd -POPD - -:: inherit old names -IF NOT DEFINED CMAKE SET CMAKE=%CMAKE_EXE% -IF NOT DEFINED MAKE SET MAKE=%MAKE_EXE% - -:: defaults -IF NOT DEFINED BUILD_DIR SET BUILD_DIR=build -IF NOT DEFINED ANDROID_ABI SET ANDROID_ABI=armeabi-v7a -SET OPENCV_BUILD_DIR=%SCRIPTS_DIR%\..\%BUILD_DIR% - -:: check that all required variables defined -PUSHD . -IF NOT DEFINED ANDROID_NDK (ECHO. & ECHO You should set an environment variable ANDROID_NDK to the full path to your copy of Android NDK & GOTO end) -(CD "%ANDROID_NDK%") || (ECHO. & ECHO Directory "%ANDROID_NDK%" specified by ANDROID_NDK variable does not exist & GOTO end) - -IF NOT EXIST "%CMAKE%" (ECHO. & ECHO You should set an environment variable CMAKE to the full path to cmake executable & GOTO end) -IF NOT EXIST "%MAKE%" (ECHO. & ECHO You should set an environment variable MAKE to the full path to native port of make executable & GOTO end) - -IF NOT %BUILD_JAVA_PART%==1 GOTO required_variables_checked - -IF NOT DEFINED ANDROID_SDK (ECHO. & ECHO You should set an environment variable ANDROID_SDK to the full path to your copy of Android SDK & GOTO end) -(CD "%ANDROID_SDK%" 2>NUL) || (ECHO. & ECHO Directory "%ANDROID_SDK%" specified by ANDROID_SDK variable does not exist & GOTO end) - -IF NOT DEFINED ANT_DIR (ECHO. & ECHO You should set an environment variable ANT_DIR to the full path to Apache Ant root & GOTO end) -(CD "%ANT_DIR%" 2>NUL) || (ECHO. & ECHO Directory "%ANT_DIR%" specified by ANT_DIR variable does not exist & GOTO end) - -IF NOT DEFINED JAVA_HOME (ECHO. & ECHO You should set an environment variable JAVA_HOME to the full path to JDK & GOTO end) -(CD "%JAVA_HOME%" 2>NUL) || (ECHO. & ECHO Directory "%JAVA_HOME%" specified by JAVA_HOME variable does not exist & GOTO end) - -:required_variables_checked -POPD - -:: check for ninja -echo "%MAKE%"|findstr /i ninja >nul: -IF %errorlevel%==1 (SET BUILD_WITH_NINJA=0) ELSE (SET BUILD_WITH_NINJA=1) -IF %BUILD_WITH_NINJA%==1 (SET CMAKE_GENERATOR=Ninja) ELSE (SET CMAKE_GENERATOR=MinGW Makefiles) - -:: create build dir -IF DEFINED REBUILD rmdir /S /Q "%BUILD_DIR%" 2>NUL -MKDIR "%BUILD_DIR%" 2>NUL -PUSHD "%BUILD_DIR%" || (ECHO. & ECHO Directory "%BUILD_DIR%" is not found & GOTO end) - -:: run cmake -ECHO. & ECHO Runnning cmake... -ECHO ANDROID_ABI=%ANDROID_ABI% -ECHO. -IF NOT %BUILD_OPENCV%==1 GOTO other-cmake -:opencv-cmake -("%CMAKE%" -G"%CMAKE_GENERATOR%" -DANDROID_ABI="%ANDROID_ABI%" -DCMAKE_TOOLCHAIN_FILE="%SOURCE_DIR%"\android.toolchain.cmake -DCMAKE_MAKE_PROGRAM="%MAKE%" %* "%SOURCE_DIR%\..") && GOTO cmakefin -ECHO. & ECHO cmake failed & GOTO end -:other-cmake -("%CMAKE%" -G"%CMAKE_GENERATOR%" -DANDROID_ABI="%ANDROID_ABI%" -DOpenCV_DIR="%OPENCV_BUILD_DIR%" -DCMAKE_TOOLCHAIN_FILE="%OPENCV_BUILD_DIR%\..\android.toolchain.cmake" -DCMAKE_MAKE_PROGRAM="%MAKE%" %* "%SOURCE_DIR%") && GOTO cmakefin -ECHO. & ECHO cmake failed & GOTO end -:cmakefin - -:: run make -ECHO. & ECHO Building native libs... -IF %BUILD_WITH_NINJA%==0 ("%MAKE%" -j %NUMBER_OF_PROCESSORS% VERBOSE=%VERBOSE%) || (ECHO. & ECHO make failed & GOTO end) -IF %BUILD_WITH_NINJA%==1 ("%MAKE%") || (ECHO. & ECHO ninja failed & GOTO end) - -IF NOT %BUILD_JAVA_PART%==1 GOTO end -POPD && PUSHD %SOURCE_DIR% - -:: configure java part -ECHO. & ECHO Updating Android project... -(CALL "%ANDROID_SDK%\tools\android" update project --name %PROJECT_NAME% --path .) || (ECHO. & ECHO failed to update android project & GOTO end) - -:: compile java part -ECHO. & ECHO Compiling Android project... -(CALL "%ANT_DIR%\bin\ant" debug) || (ECHO. & ECHO failed to compile android project & GOTO end) - -:end -POPD -ENDLOCAL diff --git a/android/scripts/camera_build.conf b/android/scripts/camera_build.conf deleted file mode 100644 index cd172b4fde..0000000000 --- a/android/scripts/camera_build.conf +++ /dev/null @@ -1,23 +0,0 @@ -# make target; arch; API level; Android Source Code Root -native_camera_r2.2.0; armeabi; 8; $ANDROID_STUB_ROOT/2.2.2 -native_camera_r2.2.0; armeabi-v7a; 8; $ANDROID_STUB_ROOT/2.2.2 -native_camera_r2.3.3; armeabi; 9; $ANDROID_STUB_ROOT/2.3.3 -native_camera_r2.3.3; armeabi-v7a; 9; $ANDROID_STUB_ROOT/2.3.3 -native_camera_r2.3.3; x86; 9; $ANDROID_STUB_ROOT/2.3.3 -native_camera_r3.0.1; armeabi; 9; $ANDROID_STUB_ROOT/3.0.1 -native_camera_r3.0.1; armeabi-v7a; 9; $ANDROID_STUB_ROOT/3.0.1 -native_camera_r3.0.1; x86; 9; $ANDROID_STUB_ROOT/3.0.1 -native_camera_r4.0.3; armeabi; 14; $ANDROID_STUB_ROOT/4.0.3 -native_camera_r4.0.3; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.0.3 -native_camera_r4.0.3; x86; 14; $ANDROID_STUB_ROOT/4.0.3 -native_camera_r4.0.3; mips; 14; $ANDROID_STUB_ROOT/4.0.3_mips -native_camera_r4.0.0; armeabi; 14; $ANDROID_STUB_ROOT/4.0.0 -native_camera_r4.0.0; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.0.0 -native_camera_r4.1.1; armeabi; 14; $ANDROID_STUB_ROOT/4.1.1 -native_camera_r4.1.1; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.1.1 -native_camera_r4.1.1; x86; 14; $ANDROID_STUB_ROOT/4.1.1 -native_camera_r4.1.1; mips; 14; $ANDROID_STUB_ROOT/4.1.1 -native_camera_r4.2.0; armeabi-v7a; 14; $ANDROID_STUB_ROOT/4.2.0 -native_camera_r4.2.0; armeabi; 14; $ANDROID_STUB_ROOT/4.2.0 -native_camera_r4.2.0; x86; 14; $ANDROID_STUB_ROOT/4.2.0 -native_camera_r4.2.0; mips; 14; $ANDROID_STUB_ROOT/4.2.0 diff --git a/android/scripts/cmake_android.cmd b/android/scripts/cmake_android.cmd deleted file mode 100644 index 3e6d923953..0000000000 --- a/android/scripts/cmake_android.cmd +++ /dev/null @@ -1,5 +0,0 @@ -@ECHO OFF - -PUSHD %~dp0.. -CALL .\scripts\build.cmd %* -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -POPD \ No newline at end of file diff --git a/android/scripts/cmake_android.sh b/android/scripts/cmake_android.sh deleted file mode 100755 index 101ba3cee8..0000000000 --- a/android/scripts/cmake_android.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -cd `dirname $0`/.. - -mkdir -p build -cd build - -cmake -DCMAKE_BUILD_WITH_INSTALL_RPATH=ON -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. - diff --git a/android/scripts/cmake_android_all_cameras.py b/android/scripts/cmake_android_all_cameras.py deleted file mode 100755 index 0ef430a3d4..0000000000 --- a/android/scripts/cmake_android_all_cameras.py +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/python - -import os -import sys -import shutil - -ScriptHome = os.path.split(sys.argv[0])[0] -ConfFile = open(os.path.join(ScriptHome, "camera_build.conf"), "rt") -HomeDir = os.getcwd() - -stub = "" -try: - stub = os.environ["ANDROID_STUB_ROOT"] -except: - None - -if (stub == ""): - print("Warning: ANDROID_STUB_ROOT environment variable is not set") - -for s in ConfFile.readlines(): - s = s[0:s.find("#")] - if (not s): - continue - keys = s.split(";") - if (len(keys) < 4): - print("Error: invalid config line: \"%s\"" % s) - continue - MakeTarget = str.strip(keys[0]) - Arch = str.strip(keys[1]) - NativeApiLevel = str.strip(keys[2]) - AndroidTreeRoot = str.strip(keys[3]) - AndroidTreeRoot = str.strip(AndroidTreeRoot, "\n") - AndroidTreeRoot = os.path.expandvars(AndroidTreeRoot) - print("Building %s for %s" % (MakeTarget, Arch)) - BuildDir = os.path.join(HomeDir, MakeTarget + "_" + Arch) - - if (os.path.exists(BuildDir)): - shutil.rmtree(BuildDir) - - try: - os.mkdir(BuildDir) - except: - print("Error: cannot create direcotry \"%s\"" % BuildDir) - continue - - shutil.rmtree(os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system"), ignore_errors=True) - - LinkerLibs = os.path.join(AndroidTreeRoot, "bin_arm", "system") - if (Arch == "x86"): - LinkerLibs = os.path.join(AndroidTreeRoot, "bin_x86", "system") - elif (Arch == "mips"): - LinkerLibs = os.path.join(AndroidTreeRoot, "bin_mips", "system") - - if (not os.path.exists(LinkerLibs)): - print("Error: Paltform libs for linker in path \"%s\" not found" % LinkerLibs) - print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)) - continue - - shutil.copytree(LinkerLibs, os.path.join(AndroidTreeRoot, "out", "target", "product", "generic", "system")) - - os.chdir(BuildDir) - BuildLog = os.path.join(BuildDir, "build.log") - CmakeCmdLine = "cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_SOURCE_TREE=\"%s\" -DANDROID_NATIVE_API_LEVEL=\"%s\" -DANDROID_ABI=\"%s\" -DANDROID_STL=stlport_static ../../ > \"%s\" 2>&1" % (AndroidTreeRoot, NativeApiLevel, Arch, BuildLog) - MakeCmdLine = "make %s >> \"%s\" 2>&1" % (MakeTarget, BuildLog); - #print(CmakeCmdLine) - os.system(CmakeCmdLine) - #print(MakeCmdLine) - os.system(MakeCmdLine) - os.chdir(HomeDir) - CameraLib = os.path.join(BuildDir, "lib", Arch, "lib" + MakeTarget + ".so") - if (os.path.exists(CameraLib)): - try: - shutil.copyfile(CameraLib, os.path.join("..", "3rdparty", "lib", Arch, "lib" + MakeTarget + ".so")) - print("Building %s for %s\t[\033[92mOK\033[0m]" % (MakeTarget, Arch)); - except: - print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)); - else: - print("Building %s for %s\t[\033[91mFAILED\033[0m]" % (MakeTarget, Arch)); - -ConfFile.close() diff --git a/android/scripts/cmake_android_armeabi.sh b/android/scripts/cmake_android_armeabi.sh index cab1800174..14e6cd25fb 100755 --- a/android/scripts/cmake_android_armeabi.sh +++ b/android/scripts/cmake_android_armeabi.sh @@ -1,10 +1,15 @@ #!/bin/sh -cd `dirname $0`/.. +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +OUTPUT=$DIR/.. +cd $OUTPUT + +mkdir -p $OUTPUT/dist mkdir -p build_armeabi cd build_armeabi -cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/armv5 $@ ../.. +cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv5 $@ ../.. make make install @@ -13,7 +18,7 @@ cd ../ mkdir -p build_armeabi-v7a cd build_armeabi-v7a -cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/armv7a $@ ../.. +cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv7a $@ ../.. make make install @@ -21,7 +26,7 @@ cd ../ mkdir -p build_x86 cd build_x86 -cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install/android/x86 $@ ../.. +cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/x86 $@ ../.. make make install diff --git a/android/scripts/cmake_android_mips.sh b/android/scripts/cmake_android_mips.sh deleted file mode 100755 index 17d2ff937e..0000000000 --- a/android/scripts/cmake_android_mips.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -cd `dirname $0`/.. - -mkdir -p build_mips -cd build_mips - -cmake -DANDROID_ABI=mips -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. - diff --git a/android/scripts/cmake_android_neon.sh b/android/scripts/cmake_android_neon.sh deleted file mode 100755 index 5e85605b56..0000000000 --- a/android/scripts/cmake_android_neon.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -cd `dirname $0`/.. - -mkdir -p build_neon -cd build_neon - -cmake -DANDROID_ABI="armeabi-v7a with NEON" -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. - diff --git a/android/scripts/cmake_android_service.sh b/android/scripts/cmake_android_service.sh deleted file mode 100755 index 0dbd482520..0000000000 --- a/android/scripts/cmake_android_service.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -cd `dirname $0`/.. - -mkdir -p build_service -cd build_service - -cmake -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DANDROID_TOOLCHAIN_NAME="arm-linux-androideabi-4.4.3" -DANDROID_STL=stlport_static -DANDROID_STL_FORCE_FEATURES=OFF -DBUILD_ANDROID_SERVICE=ON -DANDROID_SOURCE_TREE=~/Projects/AndroidSource/ServiceStub/ $@ ../.. diff --git a/android/scripts/cmake_android_x86.sh b/android/scripts/cmake_android_x86.sh deleted file mode 100755 index a01df2e668..0000000000 --- a/android/scripts/cmake_android_x86.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -cd `dirname $0`/.. - -mkdir -p build_x86 -cd build_x86 - -cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake $@ ../.. - diff --git a/android/scripts/wincfg.cmd.tmpl b/android/scripts/wincfg.cmd.tmpl deleted file mode 100644 index f4168f5b11..0000000000 --- a/android/scripts/wincfg.cmd.tmpl +++ /dev/null @@ -1,30 +0,0 @@ -:: variables required for OpenCV build :: -:: Note: all pathes should be specified without tailing slashes! -SET ANDROID_NDK=C:\full\path\to\your\copy\of\android\NDK\android-ndk-r7b -SET CMAKE_EXE=C:\full\path\to\cmake\utility\cmake.exe -SET MAKE_EXE=%ANDROID_NDK%\prebuilt\windows\bin\make.exe - -:: variables required for android-opencv build :: -SET ANDROID_SDK=C:\full\path\to\your\copy\of\android\SDK\android-sdk-windows -SET ANT_DIR=C:\full\path\to\ant\directory\apache-ant-1.8.2 -SET JAVA_HOME=C:\full\path\to\JDK\jdk1.6.0_25 - -:: configuration options :: -:::: general ARM-V7 settings -SET ANDROID_ABI=armeabi-v7a -SET BUILD_DIR=build - -:::: uncomment following lines to compile for old emulator or old device -::SET ANDROID_ABI=armeabi -::SET BUILD_DIR=build_armeabi - -:::: uncomment following lines to compile for ARM-V7 with NEON support -::SET ANDROID_ABI=armeabi-v7a with NEON -::SET BUILD_DIR=build_neon - -:::: uncomment following lines to compile for x86 -::SET ANDROID_ABI=x86 -::SET BUILD_DIR=build_x86 - -:::: other options -::SET ANDROID_NATIVE_API_LEVEL=8 &:: android-3 is enough for native part of OpenCV but android-8 is required for Java API From b0bb0a7385c230671cd9c7aeae3ee5bd83892e94 Mon Sep 17 00:00:00 2001 From: u0u0 Date: Wed, 28 Aug 2013 10:04:16 +0800 Subject: [PATCH 06/10] sync to master of libwebsockets, commit : 6c58228577306c023b072b5c7c7a2b044a94f12a And fix client header of Origin, add prefix "http://" to make a valid URI, to make golang websocket server happy. --- lib/client-handshake.c | 2 +- lib/client.c | 4 ++-- lib/libwebsockets.c | 6 ++++++ lib/libwebsockets.h | 3 +++ lib/output.c | 15 +++++++-------- lib/sha-1.c | 2 +- 6 files changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/client-handshake.c b/lib/client-handshake.c index de99a208eb..2dd810c561 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -85,7 +85,7 @@ struct libwebsocket *__libwebsocket_client_connect_2( if (context->http_proxy_port) { - n = send(wsi->sock, context->service_buffer, plen, 0); + n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL); if (n < 0) { compatible_close(wsi->sock); lwsl_debug("ERROR writing to proxy socket\n"); diff --git a/lib/client.c b/lib/client.c index dc6dacb94f..45e83af51d 100644 --- a/lib/client.c +++ b/lib/client.c @@ -229,7 +229,7 @@ int lws_client_socket_service(struct libwebsocket_context *context, else #endif n = send(wsi->sock, context->service_buffer, - p - (char *)context->service_buffer, 0); + p - (char *)context->service_buffer, MSG_NOSIGNAL); lws_latency(context, wsi, "send or SSL_write LWS_CONNMODE...HANDSHAKE", n, n >= 0); @@ -774,7 +774,7 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, p += strlen(key_b64); p += sprintf(p, "\x0d\x0a"); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)) - p += sprintf(p, "Origin: %s\x0d\x0a", + p += sprintf(p, "Origin: http://%s\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_ORIGIN)); if (lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_SENT_PROTOCOLS)) diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index ce6bae0bba..21377aba26 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -210,6 +210,8 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, lwsl_debug("closing http fd %d\n", wsi->u.http.fd); close(wsi->u.http.fd); wsi->u.http.fd = 0; + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); } #ifndef LWS_NO_EXTENSIONS @@ -368,6 +370,10 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, lwsl_debug("calling back CLOSED\n"); wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED, wsi->user_space, NULL, 0); + } else if ( wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED ) { + lwsl_debug("calling back CLOSED_HTTP\n"); + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 ); } else lwsl_debug("not calling back closed\n"); diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 65bae14534..9f686bd879 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -138,6 +138,7 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH, LWS_CALLBACK_CLIENT_ESTABLISHED, LWS_CALLBACK_CLOSED, + LWS_CALLBACK_CLOSED_HTTP, LWS_CALLBACK_RECEIVE, LWS_CALLBACK_CLIENT_RECEIVE, LWS_CALLBACK_CLIENT_RECEIVE_PONG, @@ -419,6 +420,8 @@ struct libwebsocket_extension; * * LWS_CALLBACK_CLOSED: when the websocket session ends * + * LWS_CALLBACK_CLOSED_HTTP: when a HTTP (non-websocket) session ends + * * LWS_CALLBACK_RECEIVE: data has appeared for this server endpoint from a * remote client, it can be found at *in and is * len bytes long diff --git a/lib/output.c b/lib/output.c index ef823c3e9f..224a0f27a5 100644 --- a/lib/output.c +++ b/lib/output.c @@ -311,7 +311,7 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, int m; #endif - if (len == 0 && protocol != LWS_WRITE_CLOSE) { + if (len == 0 && protocol != LWS_WRITE_CLOSE && protocol != LWS_WRITE_PING && protocol != LWS_WRITE_PONG) { lwsl_warn("zero length libwebsocket_write attempt\n"); return 0; } @@ -504,7 +504,6 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, LWS_VISIBLE int libwebsockets_serve_http_file_fragment( struct libwebsocket_context *context, struct libwebsocket *wsi) { - int ret = 0; int n, m; while (!lws_send_pipe_choked(wsi)) { @@ -516,7 +515,7 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment( if (m < 0) return -1; - wsi->u.http.filepos += n; + wsi->u.http.filepos += m; if (m != n) /* adjust for what was not sent */ lseek(wsi->u.http.fd, m - n, SEEK_CUR); @@ -525,23 +524,23 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment( if (n < 0) return -1; /* caller will close */ - if (n < sizeof(context->service_buffer) || - wsi->u.http.filepos == wsi->u.http.filelen) { + if (wsi->u.http.filepos == wsi->u.http.filelen) { wsi->state = WSI_STATE_HTTP; if (wsi->protocol->callback) - ret = user_callback_handle_rxflow( + /* ignore callback returned value */ + user_callback_handle_rxflow( wsi->protocol->callback, context, wsi, LWS_CALLBACK_HTTP_FILE_COMPLETION, wsi->user_space, NULL, 0); - return ret; + return 1; /* >0 indicates completed */ } } lwsl_notice("choked before able to send whole file (post)\n"); libwebsocket_callback_on_writable(context, wsi); - return ret; + return 0; /* indicates further processing must be done */ } /** diff --git a/lib/sha-1.c b/lib/sha-1.c index 67b17e1eb2..b1cb96fe46 100644 --- a/lib/sha-1.c +++ b/lib/sha-1.c @@ -215,7 +215,7 @@ sha1_step(struct sha1_ctxt *ctxt) /*------------------------------------------------------------*/ -void +static void sha1_init(struct sha1_ctxt *ctxt) { bzero(ctxt, sizeof(struct sha1_ctxt)); From cbe79de6c516bd1c0adf1d4b62388eb01c1cdb57 Mon Sep 17 00:00:00 2001 From: u0u0 Date: Sun, 22 Sep 2013 15:16:50 +0800 Subject: [PATCH 07/10] roll back commit "trac 40 client connection properly nonblocking", this commit can't connect to golang websocket server. --- README.coding | 20 ------------ changelog | 4 --- lib/client-handshake.c | 65 ++++++++++--------------------------- lib/client.c | 16 --------- lib/libwebsockets.c | 17 ++-------- lib/libwebsockets.h | 1 - lib/private-libwebsockets.h | 1 - test-server/test-client.c | 61 ++++++++++++++-------------------- test-server/test-ping.c | 10 +++--- 9 files changed, 49 insertions(+), 146 deletions(-) diff --git a/README.coding b/README.coding index a34211ec40..d26c21dbb3 100644 --- a/README.coding +++ b/README.coding @@ -231,23 +231,3 @@ is too computationally expensive. To use it, point it to a string like if left NULL, then the "DEFAULT" set of ciphers are all possible to select. - -Async nature of client connections ----------------------------------- - -When you call libwebsocket_client_connect(..) and get a wsi back, it does not -mean your connection is active. It just mean it started trying to connect. - -Your client connection is actually active only when you receive -LWS_CALLBACK_CLIENT_ESTABLISHED for it. - -There's a 5 second timeout for the connection, and it may give up or die for -other reasons, if any of that happens you'll get a -LWS_CALLBACK_CLIENT_CONNECTION_ERROR callback on protocol 0 instead for the -wsi. - -After attempting the connection and getting back a non-NULL wsi you should -loop calling libwebsocket_service() until one of the above callbacks occurs. - -As usual, see test-client.c for example code. - diff --git a/changelog b/changelog index 31cc570c7c..21ecb0560e 100644 --- a/changelog +++ b/changelog @@ -37,10 +37,6 @@ User api additions move the protocol name to the "in" parameter. The docs for this callback are also updated to reflect how to check headers in there. - - libwebsocket_client_connect() is now properly nonblocking and async. See - README.coding and test-client.c for information on the callbacks you - can rely on controlling the async connection period with. - User api changes ---------------- diff --git a/lib/client-handshake.c b/lib/client-handshake.c index a501565690..2dd810c561 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -52,28 +52,11 @@ struct libwebsocket *__libwebsocket_client_connect_2( goto oom4; } - if (wsi->sock < 0) { - - wsi->sock = socket(AF_INET, SOCK_STREAM, 0); - - if (wsi->sock < 0) { - lwsl_warn("Unable to open socket\n"); - goto oom4; - } + wsi->sock = socket(AF_INET, SOCK_STREAM, 0); - if (lws_set_socket_options(context, wsi->sock)) { - lwsl_err("Failed to set wsi socket options\n"); - compatible_close(wsi->sock); - goto oom4; - } - - wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT; - - insert_wsi_socket_into_fds(context, wsi); - - libwebsocket_set_timeout(wsi, - PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, - AWAITING_TIMEOUT); + if (wsi->sock < 0) { + lwsl_warn("Unable to open socket\n"); + goto oom4; } server_addr.sin_family = AF_INET; @@ -83,30 +66,20 @@ struct libwebsocket *__libwebsocket_client_connect_2( if (connect(wsi->sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) { + lwsl_debug("Connect failed\n"); + compatible_close(wsi->sock); + goto oom4; + } - if (errno == EALREADY || errno == EINPROGRESS) { - lwsl_client("nonblocking connect retry\n"); - - /* - * must do specifically a POLLOUT poll to hear - * about the connect completion - */ - - context->fds[wsi->position_in_fds_table].events |= POLLOUT; - - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_SET_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLOUT); - - return wsi; - } + lwsl_client("connected\n"); - lwsl_debug("Connect failed errno=%d\n", errno); - goto failed; + if (lws_set_socket_options(context, wsi->sock)) { + lwsl_err("Failed to set wsi socket options\n"); + compatible_close(wsi->sock); + goto oom4; } - lwsl_client("connected\n"); + insert_wsi_socket_into_fds(context, wsi); /* we are connected to server, or proxy */ @@ -114,8 +87,9 @@ struct libwebsocket *__libwebsocket_client_connect_2( n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL); if (n < 0) { + compatible_close(wsi->sock); lwsl_debug("ERROR writing to proxy socket\n"); - goto failed; + goto oom4; } libwebsocket_set_timeout(wsi, @@ -147,7 +121,7 @@ struct libwebsocket *__libwebsocket_client_connect_2( n = libwebsocket_service_fd(context, &pfd); if (n < 0) - goto failed; + goto oom4; if (n) /* returns 1 on failure after closing wsi */ return NULL; @@ -157,11 +131,7 @@ struct libwebsocket *__libwebsocket_client_connect_2( oom4: free(wsi->u.hdr.ah); free(wsi); - return NULL; -failed: - libwebsocket_close_and_free_session(context, wsi, - LWS_CLOSE_STATUS_NOSTATUS); return NULL; } @@ -215,7 +185,6 @@ libwebsocket_client_connect(struct libwebsocket_context *context, goto bail; memset(wsi, 0, sizeof(*wsi)); - wsi->sock = -1; /* -1 means just use latest supported */ diff --git a/lib/client.c b/lib/client.c index 73809b0078..68b04824ef 100644 --- a/lib/client.c +++ b/lib/client.c @@ -45,22 +45,6 @@ int lws_client_socket_service(struct libwebsocket_context *context, switch (wsi->mode) { - case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT: - - /* - * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE - * timeout protection set in client-handshake.c - */ - - if (__libwebsocket_client_connect_2(context, wsi) == NULL) { - /* closed */ - lwsl_client("closed\n"); - return -1; - } - - /* either still pending connection, or changed mode */ - return 0; - case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index d4fa58cf7c..82cd99da02 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -206,17 +206,6 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, wsi->u.ws.close_reason = reason; - if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT || - wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE) { - - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL, NULL, 0); - - free(wsi->u.hdr.ah); - goto just_kill_connection; - } - - if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED && wsi->u.http.fd) { lwsl_debug("closing http fd %d\n", wsi->u.http.fd); close(wsi->u.http.fd); @@ -915,10 +904,12 @@ libwebsocket_service_fd(struct libwebsocket_context *context, return 0; /* just here for timeout management? */ + if (pollfd == NULL) return 0; /* no, here to service a socket descriptor */ + wsi = context->lws_lookup[pollfd->fd]; if (wsi == NULL) /* not lws connection ... leave revents alone and return */ @@ -1295,10 +1286,8 @@ libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) /* wait for something to need service */ n = poll(context->fds, context->fds_count, timeout_ms); - if (n == 0) /* poll timeout */ { - libwebsocket_service_fd(context, NULL); + if (n == 0) /* poll timeout */ return 0; - } if (n < 0) return -1; diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index 2d62107f60..b3cfa154f9 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -838,7 +838,6 @@ libwebsocket_context_user(struct libwebsocket_context *context); enum pending_timeout { NO_PENDING_TIMEOUT = 0, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, - PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, PENDING_TIMEOUT_AWAITING_PING, diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 2e25e3454c..d7b6d0b813 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -221,7 +221,6 @@ enum connection_mode { LWS_CONNMODE_SSL_ACK_PENDING, /* transient modes */ - LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT, LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY, LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE, LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY, diff --git a/test-server/test-client.c b/test-server/test-client.c index 949d398815..7b32cb1f67 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -39,7 +39,6 @@ static int deny_mux; static struct libwebsocket *wsi_mirror; static int mirror_lifetime = 0; static int force_exit = 0; -static int longlived = 0; /* * This demo shows how to connect multiple websockets simultaneously to a @@ -74,15 +73,6 @@ callback_dumb_increment(struct libwebsocket_context *this, { switch (reason) { - case LWS_CALLBACK_CLIENT_ESTABLISHED: - fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_CLIENT_ESTABLISHED\n"); - break; - - case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: - fprintf(stderr, "LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n"); - was_closed = 1; - break; - case LWS_CALLBACK_CLOSED: fprintf(stderr, "LWS_CALLBACK_CLOSED\n"); was_closed = 1; @@ -135,24 +125,12 @@ callback_lws_mirror(struct libwebsocket_context *context, switch (reason) { - case LWS_CALLBACK_CLIENT_ESTABLISHED: - - fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n"); - - mirror_lifetime = 10 + (random() & 1023); - /* useful to test single connection stability */ - if (longlived) - mirror_lifetime += 50000; - - fprintf(stderr, "opened mirror connection with " - "%d lifetime\n", mirror_lifetime); + case LWS_CALLBACK_CLOSED: + fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime); + wsi_mirror = NULL; + break; - /* - * mirror_lifetime is decremented each send, when it reaches - * zero the connection is closed in the send callback. - * When the close callback comes, wsi_mirror is set to NULL - * so a new connection will be opened - */ + case LWS_CALLBACK_CLIENT_ESTABLISHED: /* * start the ball rolling, @@ -162,11 +140,6 @@ callback_lws_mirror(struct libwebsocket_context *context, libwebsocket_callback_on_writable(context, wsi); break; - case LWS_CALLBACK_CLOSED: - fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime); - wsi_mirror = NULL; - break; - case LWS_CALLBACK_CLIENT_RECEIVE: /* fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */ break; @@ -254,6 +227,7 @@ int main(int argc, char **argv) const char *address; struct libwebsocket *wsi_dumb; int ietf_version = -1; /* latest */ + int longlived = 0; struct lws_context_creation_info info; memset(&info, 0, sizeof info); @@ -332,18 +306,16 @@ int main(int argc, char **argv) protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version); if (wsi_dumb == NULL) { - fprintf(stderr, "libwebsocket connect failed\n"); + fprintf(stderr, "libwebsocket dumb connect failed\n"); ret = 1; goto bail; } - fprintf(stderr, "Waiting for connect...\n"); + fprintf(stderr, "Websocket connections opened\n"); /* * sit there servicing the websocket context to handle incoming * packets, and drawing random circles on the mirror protocol websocket - * nothing happens until the client websocket connection is - * asynchronously established */ n = 0; @@ -365,10 +337,25 @@ int main(int argc, char **argv) if (wsi_mirror == NULL) { fprintf(stderr, "libwebsocket " - "mirror connect failed\n"); + "dumb connect failed\n"); ret = 1; goto bail; } + + mirror_lifetime = 10 + (random() & 1023); + /* useful to test single connection stability */ + if (longlived) + mirror_lifetime += 50000; + + fprintf(stderr, "opened mirror connection with " + "%d lifetime\n", mirror_lifetime); + + /* + * mirror_lifetime is decremented each send, when it reaches + * zero the connection is closed in the send callback. + * When the close callback comes, wsi_mirror is set to NULL + * so a new connection will be opened + */ } bail: diff --git a/test-server/test-ping.c b/test-server/test-ping.c index 155ac7dd30..098981cf7a 100644 --- a/test-server/test-ping.c +++ b/test-server/test-ping.c @@ -338,7 +338,7 @@ int main(int argc, char **argv) char ip[30]; #ifndef WIN32 struct sigaction sa; - // struct winsize w; + struct winsize w; #endif struct timeval tv; unsigned long oldus = 0; @@ -420,10 +420,10 @@ int main(int argc, char **argv) } #ifndef WIN32 - // if (isatty(STDOUT_FILENO)) - // if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) - // if (w.ws_col > 0) - // screen_width = w.ws_col; + if (isatty(STDOUT_FILENO)) + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + if (w.ws_col > 0) + screen_width = w.ws_col; #endif info.port = CONTEXT_PORT_NO_LISTEN; From 067c91305078a021d992c64f6882821f811b9c8c Mon Sep 17 00:00:00 2001 From: u0u0 Date: Thu, 10 Oct 2013 11:07:50 +0800 Subject: [PATCH 08/10] make android build happy. --- CMakeLists.txt | 2 +- README | 24 ++++++++++-------------- test-server/test-ping.c | 10 +++++----- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fed8704f6b..1b98469e59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -309,7 +309,7 @@ endif(UNIX) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) - set( CMAKE_C_FLAGS "-Wall -Werror -O4 -fvisibility=hidden ${CMAKE_C_FLAGS}" ) + set( CMAKE_C_FLAGS "-Wall -O4 -fvisibility=hidden ${CMAKE_C_FLAGS}" ) endif () source_group("Headers Private" FILES ${HDR_PRIVATE}) diff --git a/README b/README index 37436e83f9..9416920d20 100644 --- a/README +++ b/README @@ -1,20 +1,16 @@ -This is the libwebsockets C library for lightweight websocket clients and -servers. For support, visit +TO build lib, need install cmake. - http://libwebsockets.org +iOS: +$cd libwebsockets/ios +$./build_framework.py buildname -and consider joining the project mailing list at - http://ml.libwebsockets.org/mailman/listinfo/libwebsockets +Android: +$export ANDROID_NDK=/absolute/path/to/the/android-ndk +$cd libwebsockets/android +$./scripts/cmake_android_armeabi.sh -You can get the latest version of the library from git +If you Android NDK is x86_64, you may need put below code to android.toolchain.cmake -http://git.libwebsockets.org -https://github.com/warmcat/libwebsockets - -for more information: - -README.build - information on building the library -README.coding - information for writing code using the library -README.test-apps - information about the test apps built with the library +set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) diff --git a/test-server/test-ping.c b/test-server/test-ping.c index 098981cf7a..b209d059f8 100644 --- a/test-server/test-ping.c +++ b/test-server/test-ping.c @@ -338,7 +338,7 @@ int main(int argc, char **argv) char ip[30]; #ifndef WIN32 struct sigaction sa; - struct winsize w; + //struct winsize w; #endif struct timeval tv; unsigned long oldus = 0; @@ -420,10 +420,10 @@ int main(int argc, char **argv) } #ifndef WIN32 - if (isatty(STDOUT_FILENO)) - if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) - if (w.ws_col > 0) - screen_width = w.ws_col; + //if (isatty(STDOUT_FILENO)) + // if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + // if (w.ws_col > 0) + // screen_width = w.ws_col; #endif info.port = CONTEXT_PORT_NO_LISTEN; From a90c2b690092831a38eb3934e128197acb60f9fd Mon Sep 17 00:00:00 2001 From: u0u0 Date: Mon, 28 Jul 2014 21:57:47 +0800 Subject: [PATCH 09/10] updata to latest libwebsocket --- .gitignore | 1 - CMakeLists.txt | 514 ++-- README | 1 + README.build | 35 +- README.coding | 34 + android/scripts/cmake_android_armeabi.sh | 6 +- changelog | 195 +- cmake/LibwebsocketsConfig.cmake.in | 17 + cmake/LibwebsocketsConfigVersion.cmake.in | 11 + config.h.cmake | 18 + cross-ming.cmake | 31 + cross-openwrt-makefile | 91 + ios/build_framework.py | 8 +- .../Toolchains/Toolchain-iPhoneOS_Xcode.cmake | 6 +- .../Toolchain-iPhoneSimulator_Xcode.cmake | 6 +- lib/client-handshake.c | 275 +- lib/client-parser.c | 58 +- lib/client.c | 251 +- lib/context.c | 341 +++ lib/extension-deflate-frame.c | 2 +- lib/extension.c | 178 ++ lib/getifaddrs.h | 4 +- lib/handshake.c | 319 +-- lib/lextable.h | 338 +++ lib/libev.c | 175 ++ lib/libwebsockets.c | 2215 +++-------------- lib/libwebsockets.h | 270 +- lib/lws-plat-unix.c | 404 +++ lib/lws-plat-win.c | 358 +++ lib/minilex.c | 530 ++-- lib/output.c | 464 ++-- lib/parsers.c | 692 +++-- lib/pollfd.c | 239 ++ lib/private-libwebsockets.h | 520 +++- lib/server-handshake.c | 276 +- lib/server.c | 919 +++++-- lib/service.c | 517 ++++ lib/sha-1.c | 38 +- lib/ssl-http2.c | 78 + lib/ssl.c | 571 +++++ test-server/attack.sh | 101 +- test-server/test-client.c | 74 +- test-server/test-echo.c | 17 +- test-server/test-fraggle.c | 7 - test-server/test-ping.c | 22 +- test-server/test-server.c | 330 ++- test-server/test.html | 4 +- win32port/win32helpers/gettimeofday.h | 7 +- win32port/zlib/gzio.c | 3 +- 49 files changed, 7427 insertions(+), 4144 deletions(-) create mode 100755 cmake/LibwebsocketsConfig.cmake.in create mode 100755 cmake/LibwebsocketsConfigVersion.cmake.in create mode 100755 cross-ming.cmake create mode 100755 cross-openwrt-makefile create mode 100755 lib/context.c create mode 100755 lib/lextable.h create mode 100755 lib/libev.c create mode 100755 lib/lws-plat-unix.c create mode 100755 lib/lws-plat-win.c create mode 100755 lib/pollfd.c create mode 100755 lib/service.c create mode 100755 lib/ssl-http2.c create mode 100755 lib/ssl.c diff --git a/.gitignore b/.gitignore index fe97715580..aab9e8419d 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ install-sh configure compile config.guess -*.in *~ *.orig autom4te.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b98469e59..ff9a24e046 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ message(STATUS "CMAKE_TOOLCHAIN_FILE='${CMAKE_TOOLCHAIN_FILE}'") find_package(Git) if(GIT_EXECUTABLE) execute_process( + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND "${GIT_EXECUTABLE}" log -n 1 --pretty=%h OUTPUT_VARIABLE GIT_HASH OUTPUT_STRIP_TRAILING_WHITESPACE @@ -32,91 +33,113 @@ if(GIT_EXECUTABLE) message("Git commit hash: ${LWS_BUILD_HASH}") endif() -option(WITH_SSL "Include SSL support (default OpenSSL, CyaSSL if USE_CYASSL is set)" ON) -option(USE_EXTERNAL_ZLIB "Search the system for ZLib instead of using the included one (on Windows)" OFF) -option(USE_CYASSL "Use CyaSSL replacement for OpenSSL. When settings this, you also need to specify CYASSL_LIB and CYASSL_INCLUDE_DIRS" OFF) -option(WITHOUT_BUILTIN_GETIFADDRS "Don't use BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... Default is your libc provides it. On some systems such as uclibc it doesn't exist." OFF) -option(WITHOUT_CLIENT "Don't build the client part of the library" OFF) -option(WITHOUT_SERVER "Don't build the server part of the library" OFF) -#option(WITH_LIBCRYPTO "Use libcrypto MD5 and SHA1 implementations" ON) -option(LINK_TESTAPPS_DYNAMIC "Link the test apps to the shared version of the library. Default is to link statically" OFF) -option(WITHOUT_TESTAPPS "Don't build the libwebsocket-test-apps" OFF) -option(WITHOUT_TEST_SERVER "Don't build the test server" OFF) -option(WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF) -option(WITHOUT_TEST_PING "Don't build the ping test application" OFF) -option(WITHOUT_TEST_CLIENT "Don't build the client test application" OFF) -option(WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF) -option(WITHOUT_DEBUG "Don't compile debug related code" OFF) -option(WITHOUT_EXTENSIONS "Don't compile with extensions" OFF) -option(WITH_LATENCY "Build latency measuring code into the library" OFF) -option(WITHOUT_DAEMONIZE "Don't build the daemonization api" OFF) - -if (WITHOUT_CLIENT AND WITHOUT_SERVER) +option(LWS_WITH_SSL "Include SSL support (default OpenSSL, CyaSSL if LWS_USE_CYASSL is set)" ON) +option(LWS_SSL_CLIENT_USE_OS_CA_CERTS "SSL support should make use of OS installed CA root certs" ON) +option(LWS_USE_EXTERNAL_ZLIB "Search the system for ZLib instead of using the included one (on Windows)" OFF) +option(LWS_USE_CYASSL "Use CyaSSL replacement for OpenSSL. When settings this, you also need to specify LWS_CYASSL_LIB and LWS_CYASSL_INCLUDE_DIRS" OFF) +option(LWS_WITHOUT_BUILTIN_GETIFADDRS "Don't use BSD getifaddrs implementation from libwebsockets if it is missing (this will result in a compilation error) ... Default is your libc provides it. On some systems such as uclibc it doesn't exist." OFF) +option(LWS_WITHOUT_CLIENT "Don't build the client part of the library" OFF) +option(LWS_WITHOUT_SERVER "Don't build the server part of the library" OFF) +option(LWS_LINK_TESTAPPS_DYNAMIC "Link the test apps to the shared version of the library. Default is to link statically" OFF) +option(LWS_WITHOUT_TESTAPPS "Don't build the libwebsocket-test-apps" OFF) +option(LWS_WITHOUT_TEST_SERVER "Don't build the test server" OFF) +option(LWS_WITHOUT_TEST_SERVER_EXTPOLL "Don't build the test server version that uses external poll" OFF) +option(LWS_WITHOUT_TEST_PING "Don't build the ping test application" OFF) +option(LWS_WITHOUT_TEST_CLIENT "Don't build the client test application" OFF) +option(LWS_WITHOUT_TEST_FRAGGLE "Don't build the ping test application" OFF) +option(LWS_WITHOUT_DEBUG "Don't compile debug related code" OFF) +option(LWS_WITHOUT_EXTENSIONS "Don't compile with extensions" OFF) +option(LWS_WITH_LATENCY "Build latency measuring code into the library" OFF) +option(LWS_WITHOUT_DAEMONIZE "Don't build the daemonization api" OFF) +option(LWS_WITH_LIBEV "Compile with support for libev" OFF) +option(LWS_IPV6 "Compile with support for ipv6" ON) +option(LWS_WITH_HTTP2 "Compile with support for http2" OFF) + +# Allow the user to override installation directories. +set(LWS_INSTALL_LIB_DIR lib CACHE PATH "Installation directory for libraries") +set(LWS_INSTALL_BIN_DIR bin CACHE PATH "Installation directory for executables") +set(LWS_INSTALL_INCLUDE_DIR include CACHE PATH "Installation directory for header files") +set(LWS_INSTALL_EXAMPLES_DIR bin CACHE PATH "Installation directory for example files") + +if (LWS_WITHOUT_CLIENT AND LWS_WITHOUT_SERVER) message(FATAL_ERROR "Makes no sense to compile without both client or server.") endif() # The base dir where the test-apps look for the SSL certs. -set(SSL_CERT_DIR CACHE STRING "") -set(SSL_CLIENT_CERT_DIR CACHE STRING "") - -if ("${SSL_CERT_DIR}" STREQUAL "") - set(SSL_CERT_DIR "../share") -endif() +set(LWS_OPENSSL_CLIENT_CERTS ../share CACHE PATH "Server SSL certificate directory") +if (WIN32) + set(LWS_OPENSSL_CLIENT_CERTS . CACHE PATH "Client SSL certificate directory") -if ("${SSL_CLIENT_CERT_DIR}" STREQUAL "") - if (WIN32) - set(LWS_OPENSSL_CLIENT_CERTS ".") - else() - set(LWS_OPENSSL_CLIENT_CERTS "/etc/pki/tls/certs/") + if (LWS_IPV6) + set(LWS_IPV6 OFF) + message(WARNING "IPv6 does currently not work on Windows!") endif() else() - set(LWS_OPENSSL_CLIENT_CERTS "${SSL_CLIENT_CERT_DIR}") + set(LWS_OPENSSL_CLIENT_CERTS /etc/pki/tls/certs/ CACHE PATH "Client SSL certificate directory") endif() -set(CYASSL_LIB CACHE STRING "") -set(CYASSL_INCLUDE_DIRS CACHE STRING "") +set(LWS_CYASSL_LIB CACHE PATH "Path to the CyaSSL library") +set(LWS_CYASSL_INCLUDE_DIRS CACHE PATH "Path to the CyaSSL include directory") -if (USE_CYASSL) - if ("${CYASSL_LIB}" STREQUAL "" OR "${CYASSL_INCLUDE_DIRS}" STREQUAL "") - message(FATAL_ERROR "You must set CYASSL_LIB and CYASSL_INCLUDE_DIRS when USE_CYASSL is turned on") +if (LWS_USE_CYASSL) + if ("${LWS_CYASSL_LIB}" STREQUAL "" OR "${LWS_CYASSL_INCLUDE_DIRS}" STREQUAL "") + message(FATAL_ERROR "You must set LWS_CYASSL_LIB and LWS_CYASSL_INCLUDE_DIRS when LWS_USE_CYASSL is turned on") endif() + set(USE_CYASSL 1) endif() -if (WITHOUT_EXTENSIONS) +if (LWS_WITHOUT_EXTENSIONS) set(LWS_NO_EXTENSIONS 1) endif() -if (WITH_SSL) +if (LWS_WITH_SSL) set(LWS_OPENSSL_SUPPORT 1) endif() -if (WITH_LATENCY) +if (LWS_SSL_CLIENT_USE_OS_CA_CERTS) + set(LWS_SSL_CLIENT_USE_OS_CA_CERTS 1) +endif() + +if (LWS_WITH_LATENCY) set(LWS_LATENCY 1) endif() -if (WITHOUT_DAEMONIZE) +if (LWS_WITHOUT_DAEMONIZE OR WIN32) set(LWS_NO_DAEMONIZE 1) endif() -if (WITHOUT_SERVER) +if (LWS_WITHOUT_SERVER) set(LWS_NO_SERVER 1) endif() -if (WITHOUT_CLIENT) +if (LWS_WITHOUT_CLIENT) set(LWS_NO_CLIENT 1) endif() -if (WITHOUT_DEBUG) +if (LWS_WITHOUT_DEBUG) set(_DEBUG 0) else() set(_DEBUG 1) endif() +if (LWS_WITH_LIBEV) + set(LWS_USE_LIBEV 1) + set(LWS_NO_EXTERNAL_POLL 1) +endif() + +if (LWS_IPV6) + set(LWS_USE_IPV6 1) +endif() + +if (LWS_WITH_HTTP2) + set(LWS_USE_HTTP2 1) +endif() + if (MINGW) set(LWS_MINGW_SUPPORT 1) endif() -include_directories(${PROJECT_BINARY_DIR}) +include_directories("${PROJECT_BINARY_DIR}") include(CheckCSourceCompiles) @@ -141,13 +164,13 @@ endif() # Put the libaries and binaries that get built into directories at the # top of the build tree rather than in hard-to-find leaf directories. -SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) -SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) -SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin") +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib") # Put absolute path of dynamic libraries into the object code. Some # architectures, notably Mac OS X, need this. -SET(CMAKE_INSTALL_NAME_DIR ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}) +SET(CMAKE_INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}") # So we can include the CMake generated config file only when # building with CMAKE. @@ -157,9 +180,11 @@ include(CheckFunctionExists) include(CheckIncludeFile) include(CheckIncludeFiles) include(CheckLibraryExists) +include(CheckTypeSize) CHECK_FUNCTION_EXISTS(bzero HAVE_BZERO) CHECK_FUNCTION_EXISTS(fork HAVE_FORK) +CHECK_FUNCTION_EXISTS(getenv HAVE_GETENV) CHECK_FUNCTION_EXISTS(malloc HAVE_MALLOC) CHECK_FUNCTION_EXISTS(memset HAVE_MEMSET) CHECK_FUNCTION_EXISTS(realloc HAVE_REALLOC) @@ -169,8 +194,8 @@ CHECK_FUNCTION_EXISTS(vfork HAVE_VFORK) CHECK_FUNCTION_EXISTS(getifaddrs HAVE_GETIFADDRS) if (NOT HAVE_GETIFADDRS) - if (WITHOUT_BUILTIN_GETIFADDRS) - message(FATAL_ERROR "No getifaddrs was found on the system. Turn off the WITHOUT_BUILTIN_GETIFADDRS compile option to use the supplied BSD version.") + if (LWS_WITHOUT_BUILTIN_GETIFADDRS) + message(FATAL_ERROR "No getifaddrs was found on the system. Turn off the LWS_WITHOUT_BUILTIN_GETIFADDRS compile option to use the supplied BSD version.") endif() set(LWS_BUILTIN_GETIFADDRS 1) @@ -178,6 +203,7 @@ endif() CHECK_INCLUDE_FILE(dlfcn.h HAVE_DLFCN_H) CHECK_INCLUDE_FILE(fcntl.h HAVE_FCNTL_H) +CHECK_INCLUDE_FILE(in6addr.h HAVE_IN6ADDR_H) CHECK_INCLUDE_FILE(inttypes.h HAVE_INTTYPES_H) CHECK_INCLUDE_FILE(memory.h HAVE_MEMORY_H) CHECK_INCLUDE_FILE(netinet/in.h HAVE_NETINET_IN_H) @@ -199,8 +225,14 @@ set(HAVE_WORKING_VFORK HAVE_VFORK) CHECK_INCLUDE_FILES("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS) -if (NOT HAVE_SYS_TYPES_H) +CHECK_TYPE_SIZE(pid_t PID_T_SIZE) +CHECK_TYPE_SIZE(size_t SIZE_T_SIZE) + +if (NOT PID_T_SIZE) set(pid_t int) +endif() + +if (NOT SIZE_T_SIZE) set(size_t "unsigned int") endif() @@ -214,37 +246,40 @@ endif() # Generate the config.h that includes all the compilation settings. configure_file( - ${PROJECT_SOURCE_DIR}/config.h.cmake - ${PROJECT_BINARY_DIR}/lws_config.h) + "${PROJECT_SOURCE_DIR}/config.h.cmake" + "${PROJECT_BINARY_DIR}/lws_config.h") if (MSVC) # Turn off stupid microsoft security warnings. add_definitions(-D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE) endif(MSVC) -include_directories(${PROJECT_SOURCE_DIR}/lib) +include_directories("${PROJECT_SOURCE_DIR}/lib") # Group headers and sources. # Some IDEs use this for nicer file structure. set(HDR_PRIVATE lib/private-libwebsockets.h - ${PROJECT_BINARY_DIR}/lws_config.h + "${PROJECT_BINARY_DIR}/lws_config.h" ) set(HDR_PUBLIC - ${PROJECT_SOURCE_DIR}/lib/libwebsockets.h + "${PROJECT_SOURCE_DIR}/lib/libwebsockets.h" ) set(SOURCES lib/base64-decode.c lib/handshake.c lib/libwebsockets.c + lib/service.c + lib/pollfd.c lib/output.c lib/parsers.c + lib/context.c lib/sha-1.c ) -if (NOT WITHOUT_CLIENT) +if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES lib/client.c lib/client-handshake.c @@ -252,14 +287,37 @@ if (NOT WITHOUT_CLIENT) ) endif() -if (NOT WITHOUT_SERVER) +if (LWS_WITH_SSL) + list(APPEND SOURCES + lib/ssl.c + ) +endif() + +if (LWS_WITH_HTTP2) + list(APPEND SOURCES + lib/ssl-http2.c + ) +endif() +# select the active platform files + +if (WIN32) + list(APPEND SOURCES + lib/lws-plat-win.c + ) +else() + list(APPEND SOURCES + lib/lws-plat-unix.c + ) +endif() + +if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES lib/server.c lib/server-handshake.c ) endif() -if (NOT WITHOUT_EXTENSIONS) +if (NOT LWS_WITHOUT_EXTENSIONS) list(APPEND HDR_PRIVATE lib/extension-deflate-frame.h lib/extension-deflate-stream.h @@ -272,28 +330,19 @@ if (NOT WITHOUT_EXTENSIONS) ) endif() +if (LWS_WITH_LIBEV) + list(APPEND SOURCES + lib/libev.c + ) +endif(LWS_WITH_LIBEV) + # Add helper files for Windows. if (WIN32) set(WIN32_HELPERS_PATH win32port/win32helpers) - - list(APPEND HDR_PUBLIC - ${WIN32_HELPERS_PATH}/websock-w32.h - ${WIN32_HELPERS_PATH}/gettimeofday.h - ) - if (MINGW) - list(APPEND SOURCES - ${WIN32_HELPERS_PATH}/gettimeofday.c - ) - else(MINGW) - list(APPEND SOURCES - ${WIN32_HELPERS_PATH}/websock-w32.c - ${WIN32_HELPERS_PATH}/gettimeofday.c - ) - endif(MINGW) include_directories(${WIN32_HELPERS_PATH}) else(WIN32) # Unix. - if (NOT WITHOUT_DAEMONIZE) + if (NOT LWS_WITHOUT_DAEMONIZE) list(APPEND SOURCES lib/daemonize.c ) @@ -309,7 +358,11 @@ endif(UNIX) if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) + if (UNIX) + set( CMAKE_C_FLAGS "-Wall -Werror -O4 -fvisibility=hidden ${CMAKE_C_FLAGS}" ) + else(UNIX) set( CMAKE_C_FLAGS "-Wall -O4 -fvisibility=hidden ${CMAKE_C_FLAGS}" ) + endif(UNIX) endif () source_group("Headers Private" FILES ${HDR_PRIVATE}) @@ -370,8 +423,8 @@ set(LIB_LIST) # # ZLIB (Only needed for deflate extensions). # -if (NOT WITHOUT_EXTENSIONS) - if (WIN32 AND NOT USE_EXTERNAL_ZLIB) +if (NOT LWS_WITHOUT_EXTENSIONS) + if (WIN32 AND NOT LWS_USE_EXTERNAL_ZLIB) message("Using included Zlib version") # Compile ZLib if needed. @@ -415,28 +468,28 @@ if (NOT WITHOUT_EXTENSIONS) message("ZLib libraries: ${ZLIB_LIBRARIES}") include_directories(${ZLIB_INCLUDE_DIRS}) list(APPEND LIB_LIST ${ZLIB_LIBRARIES}) -endif(NOT WITHOUT_EXTENSIONS) +endif(NOT LWS_WITHOUT_EXTENSIONS) # # OpenSSL # -if (WITH_SSL) +if (LWS_WITH_SSL) message("Compiling with SSL support") - if (USE_CYASSL) + if (LWS_USE_CYASSL) # Use CyaSSL as OpenSSL replacement. # TODO: Add a find_package command for this also. - message("CyaSSL include dir: ${CYASSL_INCLUDE_DIRS}") - message("CyaSSL libraries: ${CYASSL_LIB}") + message("CyaSSL include dir: ${LWS_CYASSL_INCLUDE_DIRS}") + message("CyaSSL libraries: ${LWS_CYASSL_LIB}") # Additional to the root directory we need to include # the cyassl/ subdirectory which contains the OpenSSL # compatability layer headers. - foreach(inc ${CYASSL_INCLUDE_DIRS}) - include_directories(${inc} ${inc}/cyassl) + foreach(inc ${LWS_CYASSL_INCLUDE_DIRS}) + include_directories("${inc}" "${inc}/cyassl") endforeach() - list(APPEND LIB_LIST ${CYASSL_LIB}) + list(APPEND LIB_LIST "${LWS_CYASSL_LIB}") else() # TODO: Add support for STATIC also. find_package(OpenSSL REQUIRED) @@ -444,15 +497,25 @@ if (WITH_SSL) message("OpenSSL include dir: ${OPENSSL_INCLUDE_DIR}") message("OpenSSL libraries: ${OPENSSL_LIBRARIES}") - include_directories(${OPENSSL_INCLUDE_DIR}) + include_directories("${OPENSSL_INCLUDE_DIR}") list(APPEND LIB_LIST ${OPENSSL_LIBRARIES}) + + # Link against dynamic linking functions. + # (Don't link directly to libdl since it is not needed on all platforms, it's now a part of libc). + list(APPEND LIB_LIST ${CMAKE_DL_LIBS}) endif() -endif(WITH_SSL) +endif(LWS_WITH_SSL) + +if (LWS_WITH_LIBEV) + list(APPEND LIB_LIST "ev") +endif(LWS_WITH_LIBEV) # # Platform specific libs. # -if (WIN32) +if (WINCE) + list(APPEND LIB_LIST ws2.lib) +elseif (WIN32) list(APPEND LIB_LIST ws2_32.lib) endif() @@ -469,11 +532,11 @@ endforeach() # Test applications # set(TEST_APP_LIST) -if (NOT WITHOUT_TESTAPPS) +if (NOT LWS_WITHOUT_TESTAPPS) # # Helper function for adding a test app. # - macro(create_test_app TEST_NAME MAIN_SRC WIN32_SRCS WIN32_HDRS) + macro(create_test_app TEST_NAME MAIN_SRC) set(TEST_SRCS ${MAIN_SRC}) set(TEST_HDR) @@ -483,25 +546,25 @@ if (NOT WITHOUT_TESTAPPS) ${WIN32_HELPERS_PATH}/getopt.c ${WIN32_HELPERS_PATH}/getopt_long.c ${WIN32_HELPERS_PATH}/gettimeofday.c - ${WIN32_SRCS}) + ) list(APPEND TEST_HDR ${WIN32_HELPERS_PATH}/getopt.h ${WIN32_HELPERS_PATH}/gettimeofday.h - ${WIN32_HDRS}) + ) endif(WIN32) source_group("Headers Private" FILES ${TEST_HDR}) source_group("Sources" FILES ${TEST_SRCS}) add_executable(${TEST_NAME} ${TEST_SRCS} ${TEST_HDR}) - if (LINK_TESTAPPS_DYNAMIC) + if (LWS_LINK_TESTAPPS_DYNAMIC) target_link_libraries(${TEST_NAME} websockets_shared) add_dependencies(${TEST_NAME} websockets_shared) - else(LINK_TESTAPPS_DYNAMIC) + else(LWS_LINK_TESTAPPS_DYNAMIC) target_link_libraries(${TEST_NAME} websockets) add_dependencies(${TEST_NAME} websockets) - endif(LINK_TESTAPPS_DYNAMIC) + endif(LWS_LINK_TESTAPPS_DYNAMIC) # Set test app specific defines. set_property(TARGET ${TEST_NAME} @@ -518,31 +581,25 @@ if (NOT WITHOUT_TESTAPPS) list(APPEND TEST_APP_LIST ${TEST_NAME}) endmacro() - if (WITH_SSL AND NOT USE_CYASSL) + if (LWS_WITH_SSL AND NOT LWS_USE_CYASSL) message("Searching for OpenSSL executable and dlls") find_package(OpenSSLbins) message("OpenSSL executable: ${OPENSSL_EXECUTABLE}") endif() - if (NOT WITHOUT_SERVER) + if (NOT LWS_WITHOUT_SERVER) # # test-server # - if (NOT WITHOUT_TEST_SERVER) - create_test_app(test-server - "test-server/test-server.c" - "" - "${WIN32_HELPERS_PATH}/netdb.h;${WIN32_HELPERS_PATH}/strings.h;${WIN32_HELPERS_PATH}/unistd.h;${WIN32_HELPERS_PATH}/websock-w32.h") + if (NOT LWS_WITHOUT_TEST_SERVER) + create_test_app(test-server "test-server/test-server.c") endif() # # test-server-extpoll # - if (NOT WITHOUT_TEST_SERVER_EXTPOLL) - create_test_app(test-server-extpoll - "test-server/test-server.c" - "win32port/win32helpers/websock-w32.c" - "${WIN32_HELPERS_PATH}/netdb.h;${WIN32_HELPERS_PATH}/strings.h;${WIN32_HELPERS_PATH}/unistd.h;${WIN32_HELPERS_PATH}/websock-w32.h") + if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL) + create_test_app(test-server-extpoll "test-server/test-server.c") # Set defines for this executable only. set_property( TARGET test-server-extpoll @@ -559,20 +616,20 @@ if (NOT WITHOUT_TESTAPPS) # Data files for running the test server. set(TEST_SERVER_DATA - ${PROJECT_SOURCE_DIR}/test-server/favicon.ico - ${PROJECT_SOURCE_DIR}/test-server/leaf.jpg - ${PROJECT_SOURCE_DIR}/test-server/libwebsockets.org-logo.png - ${PROJECT_SOURCE_DIR}/test-server/test.html) + "${PROJECT_SOURCE_DIR}/test-server/favicon.ico" + "${PROJECT_SOURCE_DIR}/test-server/leaf.jpg" + "${PROJECT_SOURCE_DIR}/test-server/libwebsockets.org-logo.png" + "${PROJECT_SOURCE_DIR}/test-server/test.html") # Generate self-signed SSL certs for the test-server. - if (WITH_SSL AND OPENSSL_EXECUTABLE) + if (LWS_WITH_SSL AND OPENSSL_EXECUTABLE AND NOT LWS_WITHOUT_TEST_SERVER) message("Generating SSL Certificates for the test-server...") - set(TEST_SERVER_SSL_KEY ${PROJECT_BINARY_DIR}/libwebsockets-test-server.key.pem) - set(TEST_SERVER_SSL_CERT ${PROJECT_BINARY_DIR}/libwebsockets-test-server.pem) + set(TEST_SERVER_SSL_KEY "${PROJECT_BINARY_DIR}/libwebsockets-test-server.key.pem") + set(TEST_SERVER_SSL_CERT "${PROJECT_BINARY_DIR}/libwebsockets-test-server.pem") if (WIN32) - file(WRITE ${PROJECT_BINARY_DIR}/openssl_input.txt + file(WRITE "${PROJECT_BINARY_DIR}/openssl_input.txt" "GB\n" "Erewhon\n" "All around\n" @@ -580,90 +637,94 @@ if (NOT WITHOUT_TESTAPPS) "localhost\n" "none@invalid.org\n\n" ) - + # The "type" command is a bit picky with paths. file(TO_NATIVE_PATH "${PROJECT_BINARY_DIR}/openssl_input.txt" OPENSSL_INPUT_WIN_PATH) + message("OPENSSL_INPUT_WIN_PATH = ${OPENSSL_INPUT_WIN_PATH}") + message("cmd = \"${OPENSSL_EXECUTABLE}\" req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout \"${TEST_SERVER_SSL_KEY}\" -out \"${TEST_SERVER_SSL_CERT}\"") execute_process( COMMAND cmd /c type "${OPENSSL_INPUT_WIN_PATH}" COMMAND "${OPENSSL_EXECUTABLE}" req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout "${TEST_SERVER_SSL_KEY}" -out "${TEST_SERVER_SSL_CERT}" RESULT_VARIABLE OPENSSL_RETURN_CODE) - + message("\n") if (OPENSSL_RETURN_CODE) - message("!!! Failed to generate SSL certificate:\n${OPENSSL_RETURN_CODE} !!!") + message(WARNING "!!! Failed to generate SSL certificate for Test Server using cmd.exe !!!:\nOpenSSL return code = ${OPENSSL_RETURN_CODE}") + else() + message("SUCCSESFULLY generated SSL certificate") endif() else() + # Unix. execute_process( COMMAND printf "GB\\nErewhon\\nAll around\\nlibwebsockets-test\\n\\nlocalhost\\nnone@invalid.org\\n" - COMMAND ${OPENSSL_EXECUTABLE} - req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout ${TEST_SERVER_SSL_KEY} -out ${TEST_SERVER_SSL_CERT} - ) + COMMAND "${OPENSSL_EXECUTABLE}" + req -new -newkey rsa:1024 -days 10000 -nodes -x509 -keyout "${TEST_SERVER_SSL_KEY}" -out "${TEST_SERVER_SSL_CERT}" + RESULT_VARIABLE OPENSSL_RETURN_CODE) + + if (OPENSSL_RETURN_CODE) + message(WARNING "!!! Failed to generate SSL certificate for Test Server!!!:\nOpenSSL return code = ${OPENSSL_RETURN_CODE}") + else() + message("SUCCSESFULLY generated SSL certificate") + endif() endif() list(APPEND TEST_SERVER_DATA - ${TEST_SERVER_SSL_KEY} - ${TEST_SERVER_SSL_CERT}) + "${TEST_SERVER_SSL_KEY}" + "${TEST_SERVER_SSL_CERT}") endif() + add_custom_command(TARGET test-server + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E make_directory "$/../share/libwebsockets-test-server") + # Copy the file needed to run the server so that the test apps can # reach them from their default output location foreach (TEST_FILE ${TEST_SERVER_DATA}) - add_custom_command(TARGET test-server - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E make_directory "$/../share/libwebsockets-test-server" - COMMAND ${CMAKE_COMMAND} -E copy ${TEST_FILE} "$/../share/libwebsockets-test-server" VERBATIM) + if (EXISTS ${TEST_FILE}) + add_custom_command(TARGET test-server + POST_BUILD + COMMAND "${CMAKE_COMMAND}" -E copy "${TEST_FILE}" "$/../share/libwebsockets-test-server" VERBATIM) + endif() endforeach() - endif(NOT WITHOUT_SERVER) + endif(NOT LWS_WITHOUT_SERVER) - if (NOT WITHOUT_CLIENT) + if (NOT LWS_WITHOUT_CLIENT) # # test-client # - if (NOT WITHOUT_TEST_CLIENT) - create_test_app(test-client - "test-server/test-client.c" - "" - "") + if (NOT LWS_WITHOUT_TEST_CLIENT) + create_test_app(test-client "test-server/test-client.c") endif() # # test-fraggle # - if (NOT WITHOUT_TEST_FRAGGLE) - create_test_app(test-fraggle - "test-server/test-fraggle.c" - "" - "${WIN32_HELPERS_PATH}/unistd.h;${WIN32_HELPERS_PATH}/sys/time.h") + if (NOT LWS_WITHOUT_TEST_FRAGGLE) + create_test_app(test-fraggle "test-server/test-fraggle.c") endif() # # test-ping # - if (NOT WITHOUT_TEST_PING) - create_test_app(test-ping - "test-server/test-ping.c" - "" - "${WIN32_HELPERS_PATH}/unistd.h;${WIN32_HELPERS_PATH}/sys/time.h") + if (NOT LWS_WITHOUT_TEST_PING) + create_test_app(test-ping "test-server/test-ping.c") endif() # # test-echo # if (NOT WITHOUT_TEST_ECHO) - create_test_app(test-echo - "test-server/test-echo.c" - "" - "${WIN32_HELPERS_PATH}/unistd.h;${WIN32_HELPERS_PATH}/sys/time.h") + create_test_app(test-echo "test-server/test-echo.c") endif() - endif(NOT WITHOUT_CLIENT) + endif(NOT LWS_WITHOUT_CLIENT) # # Copy OpenSSL dlls to the output directory on Windows. # (Otherwise we'll get an error when trying to run) # - if (WIN32 AND WITH_SSL AND NOT USE_CYASSL) + if (WIN32 AND LWS_WITH_SSL AND NOT LWS_USE_CYASSL) if(OPENSSL_BIN_FOUND) message("OpenSSL dlls found:") message(" Libeay: ${LIBEAY_BIN}") @@ -672,91 +733,182 @@ if (NOT WITHOUT_TESTAPPS) foreach(TARGET_BIN ${TEST_APP_LIST}) add_custom_command(TARGET ${TARGET_BIN} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${LIBEAY_BIN} $ VERBATIM) + COMMAND "${CMAKE_COMMAND}" -E copy "${LIBEAY_BIN}" "$" VERBATIM) add_custom_command(TARGET ${TARGET_BIN} POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${SSLEAY_BIN} $ VERBATIM) + COMMAND "${CMAKE_COMMAND}" -E copy "${SSLEAY_BIN}" "$" VERBATIM) endforeach() endif() endif() -endif(NOT WITHOUT_TESTAPPS) +endif(NOT LWS_WITHOUT_TESTAPPS) if (UNIX) # Generate documentation. # TODO: Fix this on Windows. message("Generating API documentation") - file(GLOB C_FILES ${PROJECT_SOURCE_DIR}/lib/*.c) - execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/doc/) + file(GLOB C_FILES "${PROJECT_SOURCE_DIR}/lib/*.c") + execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${PROJECT_BINARY_DIR}/doc/") execute_process( - COMMAND ${PROJECT_SOURCE_DIR}/scripts/kernel-doc -html ${C_FILES} ${HDR_PUBLIC} - OUTPUT_FILE ${PROJECT_BINARY_DIR}/doc/libwebsockets-api-doc.html + COMMAND "${PROJECT_SOURCE_DIR}/scripts/kernel-doc" -html ${C_FILES} ${HDR_PUBLIC} + OUTPUT_FILE "${PROJECT_BINARY_DIR}/doc/libwebsockets-api-doc.html" ERROR_QUIET) execute_process( - COMMAND ${PROJECT_SOURCE_DIR}/scripts/kernel-doc -text ${C_FILES} ${HDR_PUBLIC} - OUTPUT_FILE ${PROJECT_BINARY_DIR}/doc/libwebsockets-api-doc.txt + COMMAND "${PROJECT_SOURCE_DIR}/scripts/kernel-doc" -text ${C_FILES} ${HDR_PUBLIC} + OUTPUT_FILE "${PROJECT_BINARY_DIR}/doc/libwebsockets-api-doc.txt" ERROR_QUIET) # Generate and install pkgconfig. # (This is not indented, because the tabs will be part of the output) -file(WRITE ${PROJECT_BINARY_DIR}/libwebsockets.pc -"prefix="${CMAKE_INSTALL_PREFIX}" +file(WRITE "${PROJECT_BINARY_DIR}/libwebsockets.pc" +"prefix=\"${CMAKE_INSTALL_PREFIX}\" exec_prefix=\${prefix} libdir=\${exec_prefix}/lib${LIB_SUFFIX} includedir=\${prefix}/include Name: libwebsockets Description: Websockets server and client library -Version: ${PACKAGE_VERSION} +Version: ${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR} Libs: -L\${libdir} -lwebsockets Cflags: -I\${includedir}" ) - install(FILES ${PROJECT_BINARY_DIR}/libwebsockets.pc + install(FILES "${PROJECT_BINARY_DIR}/libwebsockets.pc" DESTINATION lib${LIB_SUFFIX}/pkgconfig) endif(UNIX) -# Install headers. -install(FILES ${HDR_PUBLIC} - DESTINATION include - COMPONENT headers) -set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Header files") +# +# Installation preparations. +# + +if(WIN32 AND NOT CYGWIN) + set(DEF_INSTALL_CMAKE_DIR cmake) +else() + set(DEF_INSTALL_CMAKE_DIR lib/cmake/libwebsockets) +endif() -# Install libs. +set(LWS_INSTALL_CMAKE_DIR ${DEF_INSTALL_CMAKE_DIR} CACHE PATH "Installation directory for CMake files") + +# Export targets (This is used for other CMake projects to easily find the libraries and include files). +export(TARGETS websockets websockets_shared + FILE "${PROJECT_BINARY_DIR}/LibwebsocketsTargets.cmake") +export(PACKAGE libwebsockets) + +# Generate the config file for the build-tree. +set(LWS__INCLUDE_DIRS + "${PROJECT_SOURCE_DIR}/lib" + "${PROJECT_BINARY_DIR}") +set(LIBWEBSOCKETS_INCLUDE_DIRS ${LWS__INCLUDE_DIRS} CACHE PATH "Libwebsockets include directories") +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibwebsocketsConfig.cmake.in + ${PROJECT_BINARY_DIR}/LibwebsocketsConfig.cmake + @ONLY) + +# Generate the config file for the installation tree. +get_filename_component(LWS_ABSOLUTE_INSTALL_CMAKE_DIR ${LWS_INSTALL_CMAKE_DIR} ABSOLUTE) +get_filename_component(LWS_ABSOLUTE_INSTALL_INCLUDE_DIR ${LWS_INSTALL_INCLUDE_DIR} ABSOLUTE) +file(RELATIVE_PATH + REL_INCLUDE_DIR + "${LWS_ABSOLUTE_INSTALL_CMAKE_DIR}" + "${LWS_ABSOLUTE_INSTALL_INCLUDE_DIR}") # Calculate the relative directory from the cmake dir. + +# Note the EVENT_CMAKE_DIR is defined in JanssonConfig.cmake.in, +# we escape it here so it's evaluated when it is included instead +# so that the include dirs are given relative to where the +# config file is located. +set(LWS__INCLUDE_DIRS + "\${LWS_CMAKE_DIR}/${REL_INCLUDE_DIR}") +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibwebsocketsConfig.cmake.in + ${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/LibwebsocketsConfig.cmake + @ONLY) + +# Generate version info for both build-tree and install-tree. +configure_file(${PROJECT_SOURCE_DIR}/cmake/LibwebsocketsConfigVersion.cmake.in + ${PROJECT_BINARY_DIR}/LibwebsocketsConfigVersion.cmake + @ONLY) + +set_target_properties(websockets websockets_shared + PROPERTIES PUBLIC_HEADER "${HDR_PUBLIC}") + +# +# Installation. +# + +# Install libs and headers. install(TARGETS websockets websockets_shared - LIBRARY DESTINATION lib${LIB_SUFFIX} - ARCHIVE DESTINATION lib${LIB_SUFFIX} - COMPONENT libraries) + EXPORT LibwebsocketsTargets + LIBRARY DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT libraries + ARCHIVE DESTINATION "${LWS_INSTALL_LIB_DIR}${LIB_SUFFIX}" COMPONENT libraries + RUNTIME DESTINATION "${LWS_INSTALL_BIN_DIR}" COMPONENT libraries # Windows DLLs + PUBLIC_HEADER DESTINATION "${LWS_INSTALL_INCLUDE_DIR}" COMPONENT dev) set(CPACK_COMPONENT_LIBRARIES_DISPLAY_NAME "Libraries") +set(CPACK_COMPONENT_DEV_DISPLAY_NAME "Development files") # Install test apps. -if (NOT WITHOUT_TESTAPPS) +if (NOT LWS_WITHOUT_TESTAPPS AND NOT LWS_WITHOUT_CLIENT) install(TARGETS test-client ${TEST_APP_LIST} - RUNTIME DESTINATION bin + RUNTIME DESTINATION ${LWS_INSTALL_EXAMPLES_DIR} COMPONENT examples) - set(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Example Install") + set(CPACK_COMPONENT_EXAMPLES_DISPLAY_NAME "Example files") endif() # Programs shared files used by the test-server. -if (NOT WITHOUT_TESTAPPS AND NOT WITHOUT_SERVER) +if (NOT LWS_WITHOUT_TESTAPPS AND NOT LWS_WITHOUT_SERVER) install(FILES ${TEST_SERVER_DATA} DESTINATION share/libwebsockets-test-server COMPONENT examples) endif() +# Install exports for the install-tree. +install(EXPORT LibwebsocketsTargets + DESTINATION "${LWS_INSTALL_CMAKE_DIR}" COMPONENT dev) + # build subdir is not part of sources set(CPACK_SOURCE_IGNORE_FILES $(CPACK_SOURCE_IGNORE_FILES) ".git" "build" "tgz" "tar.gz") # Most people are more used to "make dist" compared to "make package_source" -add_custom_target(dist COMMAND ${CMAKE_MAKE_PROGRAM} package_source) +add_custom_target(dist COMMAND "${CMAKE_MAKE_PROGRAM}" package_source) -INCLUDE(UseRPMTools) -IF(RPMTools_FOUND) +include(UseRPMTools) +if (RPMTools_FOUND) RPMTools_ADD_RPM_TARGETS(libwebsockets libwebsockets.spec) -ENDIF(RPMTools_FOUND) +endif() + +message("---------------------------------------------------------------------") +message(" Settings: (For more help do cmake -LH ") +message("---------------------------------------------------------------------") +message(" LWS_WITH_SSL = ${LWS_WITH_SSL} (SSL Support)") +message(" LWS_SSL_CLIENT_USE_OS_CA_CERTS = ${LWS_SSL_CLIENT_USE_OS_CA_CERTS}") +message(" LWS_USE_CYASSL = ${LWS_USE_CYASSL} (CyaSSL replacement for OpenSSL)") +if (LWS_USE_CYASSL) + message(" LWS_CYASSL_LIB = ${LWS_CYASSL_LIB}") + message(" LWS_CYASSL_INCLUDE_DIRS = ${LWS_CYASSL_INCLUDE_DIRS}") +endif() +message(" LWS_WITHOUT_BUILTIN_GETIFADDRS = ${LWS_WITHOUT_BUILTIN_GETIFADDRS}") +message(" LWS_WITHOUT_CLIENT = ${LWS_WITHOUT_CLIENT}") +message(" LWS_WITHOUT_SERVER = ${LWS_WITHOUT_SERVER}") +message(" LWS_LINK_TESTAPPS_DYNAMIC = ${LWS_LINK_TESTAPPS_DYNAMIC}") +message(" LWS_WITHOUT_TESTAPPS = ${LWS_WITHOUT_TESTAPPS}") +message(" LWS_WITHOUT_TEST_SERVER = ${LWS_WITHOUT_TEST_SERVER}") +message(" LWS_WITHOUT_TEST_SERVER_EXTPOLL = ${LWS_WITHOUT_TEST_SERVER_EXTPOLL}") +message(" LWS_WITHOUT_TEST_PING = ${LWS_WITHOUT_TEST_PING}") +message(" LWS_WITHOUT_TEST_CLIENT = ${LWS_WITHOUT_TEST_CLIENT}") +message(" LWS_WITHOUT_TEST_FRAGGLE = ${LWS_WITHOUT_TEST_FRAGGLE}") +message(" LWS_WITHOUT_DEBUG = ${LWS_WITHOUT_DEBUG}") +message(" LWS_WITHOUT_EXTENSIONS = ${LWS_WITHOUT_EXTENSIONS}") +message(" LWS_WITH_LATENCY = ${LWS_WITH_LATENCY}") +message(" LWS_WITHOUT_DAEMONIZE = ${LWS_WITHOUT_DAEMONIZE}") +message(" LWS_USE_LIBEV = ${LWS_USE_LIBEV}") +message(" LWS_IPV6 = ${LWS_IPV6}") +message(" LWS_WITH_HTTP2 = ${LWS_WITH_HTTP2}") +message("---------------------------------------------------------------------") + +# These will be available to parent projects including libwebsockets using add_subdirectory() +set(LIBWEBSOCKETS_LIBRARIES websocket websockets_shared CACHE STRING "Libwebsocket libraries") +set(LIBWEBSOCKETS_LIBRARIES_STATIC websocket CACHE STRING "Libwebsocket static library") +set(LIBWEBSOCKETS_LIBRARIES_SHARED websockets_shared CACHE STRING "Libwebsocket shared library") # This must always be last! include(CPack) diff --git a/README b/README index 9416920d20..33a75e4490 100644 --- a/README +++ b/README @@ -14,3 +14,4 @@ If you Android NDK is x86_64, you may need put below code to android.toolchain.c set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) +如何Android编译找不到NDK,可能需要手动打开上面的条件。 \ No newline at end of file diff --git a/README.build b/README.build index d62e6cb278..4620661d7a 100644 --- a/README.build +++ b/README.build @@ -60,11 +60,27 @@ Building on Unix: following to the cmake line -DLIB_SUFFIX=64 + + NOTE4 + If you are building against a non-distro OpenSSL (eg, in order to get + access to ALPN support only in newer OpenSSL versions) the nice way to + express that in one cmake command is eg, + + -DOPENSSL_ROOT_DIR=/usr/local/ssl 4. Finally you can build using the generated Makefile: make + +Quirk of cmake +-------------- + +When changing cmake options, for some reason the only way to get it to see the +changes sometimes is delete the contents of your build directory and do the +cmake from scratch. + + Building on Windows (Visual Studio) ----------------------------------- 1. Install CMake 2.6 or greater: http://cmake.org/cmake/resources/software.html @@ -98,9 +114,9 @@ To list avaialable options (ommit the H if you don't want the help text): Then to set an option and build (for example turn off SSL support): - cmake -DWITH_SSL=0 .. + cmake -DLWS_WITH_SSL=0 .. or - cmake -DWITH_SSL:BOOL=OFF .. + cmake -DLWS_WITH_SSL:BOOL=OFF .. Unix GUI -------- @@ -122,22 +138,17 @@ http://www.yassl.com/yaSSL/Products-cyassl.html It contains a OpenSSL compatability layer which makes it possible to pretty much link to it instead of OpenSSL, giving a much smaller footprint. -NOTE: At the time of writing this the current release of CyaSSL contains a -crash bug due to some APIs libwebsocket uses. To be able to use this you will -need to use the current HEAD in their official repository: - https://github.com/cyassl/cyassl - -NOTE: cyassl needs to be compiled using the --enable-opensslExtra flag for +NOTE: cyassl needs to be compiled using the --enable-opensslextra flag for this to work. Compiling libwebsockets with CyaSSL ----------------------------------- -cmake -DUSE_CYASSL=1 - -DCYASSL_INCLUDE_DIRS=/path/to/cyassl - -DCYASSL_LIB=/path/to/cyassl/cyassl.a .. +cmake .. -DLWS_USE_CYASSL=1 \ + -DLWS_CYASSL_INCLUDE_DIRS=/path/to/cyassl \ + -DLWS_CYASSL_LIB=/path/to/cyassl/cyassl.a .. -NOTE: On windows use the .lib file extension for CYASSL_LIB instead. +NOTE: On windows use the .lib file extension for LWS_CYASSL_LIB instead. Cross compiling --------------- diff --git a/README.coding b/README.coding index d26c21dbb3..333011caed 100644 --- a/README.coding +++ b/README.coding @@ -67,6 +67,20 @@ in the ...WRITEABLE callback. See the test server code for an example of how to do this. +Do not rely on only your own WRITEABLE requests appearing +--------------------------------------------------------- + +Libwebsockets may generate additional LWS_CALLBACK_CLIENT_WRITEABLE events +if it met network conditions where it had to buffer your send data internally. + +So your code for LWS_CALLBACK_CLIENT_WRITEABLE needs to own the decision +about what to send, it can't assume that just because the writeable callback +came it really is time to send something. + +It's quite possible you get an 'extra' writeable callback at any time and +just need to return 0 and wait for the expected callback later. + + Closing connections from the user side -------------------------------------- @@ -231,3 +245,23 @@ is too computationally expensive. To use it, point it to a string like if left NULL, then the "DEFAULT" set of ciphers are all possible to select. + +Async nature of client connections +---------------------------------- + +When you call libwebsocket_client_connect(..) and get a wsi back, it does not +mean your connection is active. It just mean it started trying to connect. + +Your client connection is actually active only when you receive +LWS_CALLBACK_CLIENT_ESTABLISHED for it. + +There's a 5 second timeout for the connection, and it may give up or die for +other reasons, if any of that happens you'll get a +LWS_CALLBACK_CLIENT_CONNECTION_ERROR callback on protocol 0 instead for the +wsi. + +After attempting the connection and getting back a non-NULL wsi you should +loop calling libwebsocket_service() until one of the above callbacks occurs. + +As usual, see test-client.c for example code. + diff --git a/android/scripts/cmake_android_armeabi.sh b/android/scripts/cmake_android_armeabi.sh index 14e6cd25fb..0f47ef2978 100755 --- a/android/scripts/cmake_android_armeabi.sh +++ b/android/scripts/cmake_android_armeabi.sh @@ -9,7 +9,7 @@ mkdir -p $OUTPUT/dist mkdir -p build_armeabi cd build_armeabi -cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv5 $@ ../.. +cmake -DANDROID_ABI=armeabi -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DLWS_WITH_SSL=OFF -DLWS_WITHOUT_SERVER=ON -DLWS_WITHOUT_TESTAPPS=ON -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv5 $@ ../.. make make install @@ -18,7 +18,7 @@ cd ../ mkdir -p build_armeabi-v7a cd build_armeabi-v7a -cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv7a $@ ../.. +cmake -DANDROID_ABI=armeabi-v7a -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DLWS_WITH_SSL=OFF -DLWS_WITHOUT_SERVER=ON -DLWS_WITHOUT_TESTAPPS=ON -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/armv7a $@ ../.. make make install @@ -26,7 +26,7 @@ cd ../ mkdir -p build_x86 cd build_x86 -cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DWITH_SSL=0 -DWITHOUT_SERVER=1 -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/x86 $@ ../.. +cmake -DANDROID_ABI=x86 -DCMAKE_TOOLCHAIN_FILE=../android.toolchain.cmake -DLWS_WITH_SSL=OFF -DLWS_WITHOUT_SERVER=ON -DLWS_WITHOUT_TESTAPPS=ON -DCMAKE_INSTALL_PREFIX:PATH=$OUTPUT/dist/x86 $@ ../.. make make install diff --git a/changelog b/changelog index 21ecb0560e..a945fc24ab 100644 --- a/changelog +++ b/changelog @@ -1,7 +1,192 @@ Changelog --------- -(development since 1.22) +v1.3-chrome37-firefox30 +======================= + + .gitignore | 1 - + CMakeLists.txt | 447 +++-- + README.build | 35 +- + README.coding | 14 + + changelog | 66 + + cmake/LibwebsocketsConfig.cmake.in | 17 + + cmake/LibwebsocketsConfigVersion.cmake.in | 11 + + config.h.cmake | 18 + + cross-ming.cmake | 31 + + cross-openwrt-makefile | 91 + + lib/client-handshake.c | 205 ++- + lib/client-parser.c | 58 +- + lib/client.c | 158 +- + lib/context.c | 341 ++++ + lib/extension-deflate-frame.c | 2 +- + lib/extension.c | 178 ++ + lib/handshake.c | 287 +--- + lib/lextable.h | 338 ++++ + lib/libev.c | 175 ++ + lib/libwebsockets.c | 2089 +++-------------------- + lib/libwebsockets.h | 253 ++- + lib/lws-plat-unix.c | 404 +++++ + lib/lws-plat-win.c | 358 ++++ + lib/minilex.c | 530 +++--- + lib/output.c | 445 ++--- + lib/parsers.c | 682 ++++---- + lib/pollfd.c | 239 +++ + lib/private-libwebsockets.h | 501 +++++- + lib/server-handshake.c | 274 +-- + lib/server.c | 858 ++++++++-- + lib/service.c | 517 ++++++ + lib/sha-1.c | 38 +- + lib/ssl-http2.c | 78 + + lib/ssl.c | 571 +++++++ + test-server/attack.sh | 101 +- + test-server/test-client.c | 9 +- + test-server/test-echo.c | 17 +- + test-server/test-fraggle.c | 7 - + test-server/test-ping.c | 12 +- + test-server/test-server.c | 330 ++-- + test-server/test.html | 4 +- + win32port/client/client.vcxproj | 259 --- + win32port/client/client.vcxproj.filters | 39 - + .../libwebsocketswin32.vcxproj.filters | 93 - + win32port/server/server.vcxproj | 276 --- + win32port/server/server.vcxproj.filters | 51 - + win32port/win32helpers/gettimeofday.h | 59 +- + win32port/win32helpers/netdb.h | 1 - + win32port/win32helpers/strings.h | 0 + win32port/win32helpers/sys/time.h | 1 - + win32port/win32helpers/unistd.h | 0 + win32port/win32helpers/websock-w32.c | 104 -- + win32port/win32helpers/websock-w32.h | 62 - + win32port/win32port.sln | 100 -- + win32port/zlib/gzio.c | 3 +- + 55 files changed, 6779 insertions(+), 5059 deletions(-) + + +User api additions +------------------ + +POST method is supported + +The protocol 0 / HTTP callback can now get two new kinds of callback, +LWS_CALLBACK_HTTP_BODY (in and len are a chunk of the body of the HTTP request) +and LWS_CALLBACK_HTTP_BODY_COMPLETION (the expected amount of body has arrived +and been passed to the user code already). These callbacks are used with the +post method (see the test server for details). + +The period between the HTTP header completion and the completion of the body +processing is protected by a 5s timeout. + +The chunks are stored in a malloc'd buffer of size protocols[0].rx_buffer_size. + + +New server option you can enable from user code +LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT allows non-SSL connections to +also be accepted on an SSL listening port. It's disabled unless you enable +it explicitly. + + +Two new callbacks are added in protocols[0] that are optional for allowing +limited thread access to libwebsockets, LWS_CALLBACK_LOCK_POLL and +LWS_CALLBACK_UNLOCK_POLL. + +If you use them, they protect internal and external poll list changes, but if +you want to use external thread access to libwebsocket_callback_on_writable() +you have to implement your locking here even if you don't use external +poll support. + +If you will use another thread for this, take a lot of care about managing +your list of live wsi by doing it from ESTABLISHED and CLOSED callbacks +(with your own locking). + +If you configure cmake with -DLWS_WITH_LIBEV=1 then the code allowing the libev +eventloop instead of the default poll() one will also be compiled in. But to +use it, you must also set the LWS_SERVER_OPTION_LIBEV flag on the context +creation info struct options member. + +IPV6 is supported and enabled by default except for Windows, you can disable +the support at build-time by giving -DLWS_IPV6=, and disable use of it even if +compiled in by making sure the flag LWS_SERVER_OPTION_DISABLE_IPV6 is set on +the context creation info struct options member. + +You can give LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS option flag to +guarantee the OS CAs will not be used, even if that support was selected at +build-time. + +Optional "token limits" may be enforced by setting the member "token_limits" +in struct lws_context_creation_info to point to a struct lws_token_limits. +NULL means no token limits used for compatibility. + + +User api changes +---------------- + +Extra optional argument to libwebsockets_serve_http_file() allows injecion +of HTTP headers into the canned response. Eg, cookies may be added like +that without getting involved in having to send the header by hand. + +A new info member http_proxy_address may be used at context creation time to +set the http proxy. If non-NULL, it overrides http_proxy environment var. + +Cmake supports LWS_SSL_CLIENT_USE_OS_CA_CERTS defaulting to on, which gets +the client to use the OS CA Roots. If you're worried somebody with the +ability to forge for force creation of a client cert from the root CA in +your OS, you should disable this since your selfsigned $0 cert is a lot safer +then... + + +v1.23-chrome32-firefox24 +======================== + + Android.mk | 29 + + CMakeLists.txt | 573 ++++++++---- + COPYING | 503 ----------- + INSTALL | 365 -------- + Makefile.am | 13 - + README.build | 371 ++------ + README.coding | 63 ++ + autogen.sh | 1578 --------------------------------- + changelog | 69 ++ + cmake/FindGit.cmake | 163 ++++ + cmake/FindOpenSSLbins.cmake | 15 +- + cmake/UseRPMTools.cmake | 176 ++++ + config.h.cmake | 25 +- + configure.ac | 226 ----- + cross-arm-linux-gnueabihf.cmake | 28 + + lib/Makefile.am | 89 -- + lib/base64-decode.c | 98 +- + lib/client-handshake.c | 123 ++- + lib/client-parser.c | 19 +- + lib/client.c | 145 ++- + lib/daemonize.c | 4 +- + lib/extension.c | 2 +- + lib/getifaddrs.h | 4 +- + lib/handshake.c | 76 +- + lib/libwebsockets.c | 491 ++++++---- + lib/libwebsockets.h | 164 ++-- + lib/output.c | 214 ++++- + lib/parsers.c | 102 +-- + lib/private-libwebsockets.h | 66 +- + lib/server-handshake.c | 5 +- + lib/server.c | 29 +- + lib/sha-1.c | 2 +- + libwebsockets-api-doc.html | 249 +++--- + libwebsockets.pc.in | 11 - + libwebsockets.spec | 14 +- + m4/ignore-me | 2 - + scripts/FindLibWebSockets.cmake | 33 + + scripts/kernel-doc | 1 + + test-server/Makefile.am | 131 --- + test-server/leaf.jpg | Bin 0 -> 2477518 bytes + test-server/test-client.c | 78 +- + test-server/test-echo.c | 33 +- + test-server/test-fraggle.c | 26 +- + test-server/test-ping.c | 15 +- + test-server/test-server.c | 197 +++- + test-server/test.html | 5 +- + win32port/win32helpers/gettimeofday.c | 74 +- + win32port/win32helpers/websock-w32.h | 6 +- + 48 files changed, 2493 insertions(+), 4212 deletions(-) + User api additions ------------------ @@ -37,6 +222,14 @@ User api additions move the protocol name to the "in" parameter. The docs for this callback are also updated to reflect how to check headers in there. + - libwebsocket_client_connect() is now properly nonblocking and async. See + README.coding and test-client.c for information on the callbacks you + can rely on controlling the async connection period with. + + - if your OS does not support the http_proxy environment variable convention + (eg, reportedly OSX), you can use a new api libwebsocket_set_proxy() + to set the proxy details inbetween context creation and the connection + action. For OSes that support http_proxy, that's used automatically. User api changes ---------------- diff --git a/cmake/LibwebsocketsConfig.cmake.in b/cmake/LibwebsocketsConfig.cmake.in new file mode 100755 index 0000000000..bea82b58e0 --- /dev/null +++ b/cmake/LibwebsocketsConfig.cmake.in @@ -0,0 +1,17 @@ +# - Config file for the Libevent package +# It defines the following variables +# LIBWEBSOCKETS_INCLUDE_DIRS - include directories for FooBar +# LIBWEBSOCKETS_LIBRARIES - libraries to link against + +# Get the path of the current file. +get_filename_component(LWS_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +# Set the include directories. +set(LIBWEBSOCKETS_INCLUDE_DIRS "@LWS__INCLUDE_DIRS@") + +# Include the project Targets file, this contains definitions for IMPORTED targets. +include(${LWS_CMAKE_DIR}/LibwebsocketsTargets.cmake) + +# IMPORTED targets from LibwebsocketsTargets.cmake +set(LIBWEBSOCKETS_LIBRARIES websockets websockets_shared) + diff --git a/cmake/LibwebsocketsConfigVersion.cmake.in b/cmake/LibwebsocketsConfigVersion.cmake.in new file mode 100755 index 0000000000..e7bc6eebcf --- /dev/null +++ b/cmake/LibwebsocketsConfigVersion.cmake.in @@ -0,0 +1,11 @@ +set(PACKAGE_VERSION "@CPACK_PACKAGE_VERSION@") + +# Check whether the requested PACKAGE_FIND_VERSION is compatible +if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_COMPATIBLE FALSE) +else() + set(PACKAGE_VERSION_COMPATIBLE TRUE) + if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}") + set(PACKAGE_VERSION_EXACT TRUE) + endif() +endif() diff --git a/config.h.cmake b/config.h.cmake index e55d7154bb..2b883961bf 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -17,12 +17,24 @@ /* Build with OpenSSL support */ #cmakedefine LWS_OPENSSL_SUPPORT +/* The client should load and trust CA root certs it finds in the OS */ +#cmakedefine LWS_SSL_CLIENT_USE_OS_CA_CERTS + /* Sets the path where the client certs should be installed. */ #cmakedefine LWS_OPENSSL_CLIENT_CERTS "${LWS_OPENSSL_CLIENT_CERTS}" /* Turn off websocket extensions */ #cmakedefine LWS_NO_EXTENSIONS +/* Enable libev io loop */ +#cmakedefine LWS_USE_LIBEV + +/* Build with support for ipv6 */ +#cmakedefine LWS_USE_IPV6 + +/* Build with support for HTTP2 */ +#cmakedefine LWS_USE_HTTP2 + /* Turn on latency measuring code */ #cmakedefine LWS_LATENCY @@ -53,6 +65,12 @@ /* Define to 1 if you have the `fork' function. */ #cmakedefine HAVE_FORK +/* Define to 1 if you have the `getenv’ function. */ +#cmakedefine HAVE_GETENV + +/* Define to 1 if you have the header file. */ +#cmakedefine HAVE_IN6ADDR_H + /* Define to 1 if you have the header file. */ #cmakedefine HAVE_INTTYPES_H diff --git a/cross-ming.cmake b/cross-ming.cmake new file mode 100755 index 0000000000..94989f2300 --- /dev/null +++ b/cross-ming.cmake @@ -0,0 +1,31 @@ +# +# CMake Toolchain file for crosscompiling on MingW. +# +# This can be used when running cmake in the following way: +# cd build/ +# cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-ming.cmake +# + +set(CROSS_PATH /usr/bin) + +# Target operating system name. +set(CMAKE_SYSTEM_NAME Windows) +set(BUILD_SHARED_LIBS OFF) + +# Name of C compiler. +set(CMAKE_C_COMPILER "${CROSS_PATH}/x86_64-w64-mingw32-gcc") +#set(CMAKE_CXX_COMPILER "${CROSS_PATH}/x86_64-w64-mingw32-g++") +set(CMAKE_RC_COMPILER "${CROSS_PATH}/x86_64-w64-mingw32-windres") +set(CMAKE_C_FLAGS "-Wno-error") + +# Where to look for the target environment. (More paths can be added here) +set(CMAKE_FIND_ROOT_PATH "${CROSS_PATH}") + +# Adjust the default behavior of the FIND_XXX() commands: +# search programs in the host environment only. +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + +# Search headers and libraries in the target environment only. +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + diff --git a/cross-openwrt-makefile b/cross-openwrt-makefile new file mode 100755 index 0000000000..9f1a0fdb08 --- /dev/null +++ b/cross-openwrt-makefile @@ -0,0 +1,91 @@ +# +# libwebsockets makefile for openwrt +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=libwebsockets +PKG_VERSION:=2014-03-01 +PKG_RELEASE=$(PKG_SOURCE_VERSION) + +PKG_SOURCE_PROTO:=git +PKG_SOURCE_URL:=https://github.com/warmcat/libwebsockets.git +PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION) +PKG_SOURCE_VERSION:=388dc7d201d8d123841869fb49ec4d94d6dd7f54 +PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION)-$(PKG_SOURCE_VERSION).tar.gz +CMAKE_INSTALL:=1 + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +CMAKE_OPTIONS += -DLWS_OPENSSL_CLIENT_CERTS=/etc/ssl/certs +CMAKE_OPTIONS += -DLWS_OPENSSL_SUPPORT=ON +CMAKE_OPTIONS += -DLWS_WITH_SSL=ON +CMAKE_OPTIONS += -DLWS_WITHOUT_TESTAPPS=$(if $(CONFIG_PACKAGE_libwebsockets-examples),"OFF","ON") + +# for cyassl, define these in addition to LWS_OPENSSL_SUPPORT and +# edit package/libs/cyassl/Makefile to include --enable-opensslextra +# CMAKE_OPTIONS += -DLWS_USE_CYASSL=ON +# CMAKE_OPTIONS += -DLWS_CYASSL_LIB=$(STAGING_DIR)/usr/lib/libcyassl.so +# CMAKE_OPTIONS += -DLWS_CYASSL_INCLUDE_DIRS=$(STAGING_DIR)/usr/include + +# other options worth noting +# CMAKE_OPTIONS += -DLWS_WITHOUT_EXTENSIONS=ON +# CMAKE_OPTIONS += -DLWS_WITHOUT_DAEMONIZE=ON +# CMAKE_OPTIONS += -DLWS_WITHOUT_SERVER=ON +# CMAKE_OPTIONS += -DLWS_WITHOUT_DEBUG=ON + + +define Package/libwebsockets/Default + SECTION:=libs + CATEGORY:=Libraries + TITLE:=libwebsockets + DEPENDS:=+zlib +libopenssl +endef + +define Package/libwebsockets + $(call Package/libwebsockets/Default) + TITLE+= (libraries) +endef + +define Package/libwebsockets/description + libwebsockets + This package contains libwebsocket libraries +endef + +define Package/libwebsockets-examples + $(call Package/libwebsockets/Default) + DEPENDS:=libwebsockets + TITLE+= (examples) +endef + +define Package/libwebsockets-examples/description + libwebsockets examples + This package contains libwebsockets examples +endef + +define Package/libwebsockets/install + $(INSTALL_DIR) $(1)/usr/lib + $(CP) $(PKG_INSTALL_DIR)/usr/lib/libwebsockets.so* $(1)/usr/lib/ +endef + +define Package/libwebsockets-examples/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-client $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-echo $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-fraggle $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-ping $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-server $(1)/usr/bin/ + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/libwebsockets-test-server-extpoll $(1)/usr/bin/ + + $(INSTALL_DIR) $(1)/usr/share/libwebsockets-test-server + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/favicon.ico $(1)/usr/share/libwebsockets-test-server/ + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/leaf.jpg $(1)/usr/share/libwebsockets-test-server/ + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/libwebsockets.org-logo.png $(1)/usr/share/libwebsockets-test-server/ + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/libwebsockets-test-server.key.pem $(1)/usr/share/libwebsockets-test-server/ + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/libwebsockets-test-server.pem $(1)/usr/share/libwebsockets-test-server/ + $(INSTALL_DATA) $(PKG_INSTALL_DIR)/usr/share/libwebsockets-test-server/test.html $(1)/usr/share/libwebsockets-test-server/ +endef + +$(eval $(call BuildPackage,libwebsockets)) +$(eval $(call BuildPackage,libwebsockets-examples)) diff --git a/ios/build_framework.py b/ios/build_framework.py index 352c503078..b9b980bdf2 100755 --- a/ios/build_framework.py +++ b/ios/build_framework.py @@ -38,9 +38,9 @@ def build_opencv(srcroot, buildroot, target, arch): # for some reason, if you do not specify CMAKE_BUILD_TYPE, it puts libs to "RELEASE" rather than "Release" cmakeargs = ("-GXcode " + "-DCMAKE_TOOLCHAIN_FILE=%s/ios/cmake/Toolchains/Toolchain-%s_Xcode.cmake " + - "-DWITH_SSL=0 " + - "-DWITHOUT_SERVER=1 " + - "-DWITHOUT_TESTAPPS=1 " + + "-DLWS_WITH_SSL=OFF " + + "-DLWS_WITHOUT_SERVER=ON " + + "-DLWS_WITHOUT_TESTAPPS=ON " + "-DCMAKE_INSTALL_PREFIX:PATH=/Users/james/Project/libwebsockets/install") % (srcroot, target) # if cmake cache exists, just rerun cmake to update OpenCV.xproj if necessary if os.path.isfile(os.path.join(builddir, "CMakeCache.txt")): @@ -137,4 +137,4 @@ def build_framework(srcroot, dstroot): print "Usage:\n\t./build_framework.py \n\n" sys.exit(0) - build_framework(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")), os.path.abspath(sys.argv[1])) \ No newline at end of file + build_framework(os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "..")), os.path.abspath(sys.argv[1])) diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake index 67343253bd..92e83f2e8c 100644 --- a/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake +++ b/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake @@ -8,8 +8,8 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cma # Force the compilers to gcc for iOS include (CMakeForceCompiler) -#CMAKE_FORCE_C_COMPILER (gcc gcc) -#CMAKE_FORCE_CXX_COMPILER (g++ g++) +CMAKE_FORCE_C_COMPILER (gcc gcc) +CMAKE_FORCE_CXX_COMPILER (g++ g++) set (CMAKE_C_SIZEOF_DATA_PTR 4) set (CMAKE_C_HAS_ISYSROOT 1) @@ -28,4 +28,4 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -message (STATUS "iPhoneOS toolchain loaded") \ No newline at end of file +message (STATUS "iPhoneOS toolchain loaded") diff --git a/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake index 7ef8113edb..02bcfb5063 100644 --- a/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake +++ b/ios/cmake/Toolchains/Toolchain-iPhoneSimulator_Xcode.cmake @@ -8,8 +8,8 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/ios/cma # Force the compilers to gcc for iOS include (CMakeForceCompiler) -#CMAKE_FORCE_C_COMPILER (gcc gcc) -#CMAKE_FORCE_CXX_COMPILER (g++ g++) +CMAKE_FORCE_C_COMPILER (gcc gcc) +CMAKE_FORCE_CXX_COMPILER (g++ g++) set (CMAKE_C_SIZEOF_DATA_PTR 4) set (CMAKE_C_HAS_ISYSROOT 1) @@ -28,4 +28,4 @@ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -message (STATUS "iPhoneSimulator toolchain loaded") \ No newline at end of file +message (STATUS "iPhoneSimulator toolchain loaded") diff --git a/lib/client-handshake.c b/lib/client-handshake.c index 2dd810c561..e25c368cf3 100644 --- a/lib/client-handshake.c +++ b/lib/client-handshake.c @@ -1,17 +1,25 @@ #include "private-libwebsockets.h" -struct libwebsocket *__libwebsocket_client_connect_2( +struct libwebsocket *libwebsocket_client_connect_2( struct libwebsocket_context *context, struct libwebsocket *wsi ) { - struct pollfd pfd; + struct libwebsocket_pollfd pfd; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 server_addr6; + struct sockaddr_in6 client_addr6; + struct addrinfo hints, *result; +#endif + struct sockaddr_in server_addr4; + struct sockaddr_in client_addr4; struct hostent *server_hostent; - struct sockaddr_in server_addr; + + struct sockaddr *v; int n; int plen = 0; const char *ads; - lwsl_client("__libwebsocket_client_connect_2\n"); + lwsl_client("libwebsocket_client_connect_2\n"); /* * proxy? @@ -25,71 +33,196 @@ struct libwebsocket *__libwebsocket_client_connect_2( "\x0d\x0a", lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS), wsi->u.hdr.ah->c_port); + ads = context->http_proxy_address; - /* OK from now on we talk via the proxy, so connect to that */ - - /* - * (will overwrite existing pointer, - * leaving old string/frag there but unreferenced) - */ - if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, - context->http_proxy_address)) - goto oom4; - wsi->u.hdr.ah->c_port = context->http_proxy_port; +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) + server_addr6.sin6_port = htons(context->http_proxy_port); + else +#endif + server_addr4.sin_port = htons(context->http_proxy_port); + + } else { + ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) + server_addr6.sin6_port = htons(wsi->u.hdr.ah->c_port); + else +#endif + server_addr4.sin_port = htons(wsi->u.hdr.ah->c_port); } /* * prepare the actual connection (to the proxy, if any) */ + lwsl_client("libwebsocket_client_connect_2: address %s\n", ads); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + memset(&hints, 0, sizeof(struct addrinfo)); + n = getaddrinfo(ads, NULL, &hints, &result); + if (n) { +#ifdef _WIN32 + lwsl_err("getaddrinfo: %ls\n", gai_strerrorW(n)); +#else + lwsl_err("getaddrinfo: %s\n", gai_strerror(n)); +#endif + goto oom4; + } - ads = lws_hdr_simple_ptr(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS); + server_addr6.sin6_family = AF_INET6; + switch (result->ai_family) { + case AF_INET: + /* map IPv4 to IPv6 */ + bzero((char *)&server_addr6.sin6_addr, + sizeof(struct in6_addr)); + server_addr6.sin6_addr.s6_addr[10] = 0xff; + server_addr6.sin6_addr.s6_addr[11] = 0xff; + memcpy(&server_addr6.sin6_addr.s6_addr[12], + &((struct sockaddr_in *)result->ai_addr)->sin_addr, + sizeof(struct in_addr)); + break; + case AF_INET6: + memcpy(&server_addr6.sin6_addr, + &((struct sockaddr_in6 *)result->ai_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; + default: + lwsl_err("Unknown address family\n"); + freeaddrinfo(result); + goto oom4; + } - lwsl_client("__libwebsocket_client_connect_2: address %s\n", ads); + freeaddrinfo(result); + } else +#endif + { + server_hostent = gethostbyname(ads); + if (!server_hostent) { + lwsl_err("Unable to get host name from %s\n", ads); + goto oom4; + } - server_hostent = gethostbyname(ads); - if (server_hostent == NULL) { - lwsl_err("Unable to get host name from %s\n", ads); - goto oom4; + server_addr4.sin_family = AF_INET; + server_addr4.sin_addr = + *((struct in_addr *)server_hostent->h_addr); + bzero(&server_addr4.sin_zero, 8); } - wsi->sock = socket(AF_INET, SOCK_STREAM, 0); - if (wsi->sock < 0) { - lwsl_warn("Unable to open socket\n"); - goto oom4; - } - server_addr.sin_family = AF_INET; - server_addr.sin_port = htons(wsi->u.hdr.ah->c_port); - server_addr.sin_addr = *((struct in_addr *)server_hostent->h_addr); - bzero(&server_addr.sin_zero, 8); +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) + wsi->sock = socket(AF_INET6, SOCK_STREAM, 0); + else +#endif + wsi->sock = socket(AF_INET, SOCK_STREAM, 0); + + if (wsi->sock < 0) { + lwsl_warn("Unable to open socket\n"); + goto oom4; + } + + if (lws_plat_set_socket_options(context, wsi->sock)) { + lwsl_err("Failed to set wsi socket options\n"); + compatible_close(wsi->sock); + goto oom4; + } - if (connect(wsi->sock, (struct sockaddr *)&server_addr, - sizeof(struct sockaddr)) == -1) { - lwsl_debug("Connect failed\n"); - compatible_close(wsi->sock); - goto oom4; + wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT; + + insert_wsi_socket_into_fds(context, wsi); + + libwebsocket_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, + AWAITING_TIMEOUT); +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&client_addr6; + n = sizeof(client_addr6); + bzero((char *)v, n); + client_addr6.sin6_family = AF_INET6; + } else +#endif + { + v = (struct sockaddr *)&client_addr4; + n = sizeof(client_addr4); + bzero((char *)v, n); + client_addr4.sin_family = AF_INET; + } + + if (context->iface) { + if (interface_to_sa(context, context->iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", + context->iface); + compatible_close(wsi->sock); + goto failed; + } + + if (bind(wsi->sock, v, n) < 0) { + lwsl_err("Error binding to interface %s", + context->iface); + compatible_close(wsi->sock); + goto failed; + } + } } - lwsl_client("connected\n"); +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&server_addr6; + n = sizeof(struct sockaddr_in6); + } else +#endif + { + v = (struct sockaddr *)&server_addr4; + n = sizeof(struct sockaddr); + } + + if (connect(wsi->sock, v, n) == -1 || LWS_ERRNO == LWS_EISCONN) { + + if (LWS_ERRNO == LWS_EALREADY || LWS_ERRNO == LWS_EINPROGRESS + || LWS_ERRNO == LWS_EWOULDBLOCK) { + lwsl_client("nonblocking connect retry\n"); + + /* + * must do specifically a POLLOUT poll to hear + * about the connect completion + */ + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + goto oom4; + + return wsi; + } - if (lws_set_socket_options(context, wsi->sock)) { - lwsl_err("Failed to set wsi socket options\n"); - compatible_close(wsi->sock); - goto oom4; + if (LWS_ERRNO != LWS_EISCONN) { + lwsl_debug("Connect failed errno=%d\n", LWS_ERRNO); + goto failed; + } } - insert_wsi_socket_into_fds(context, wsi); + lwsl_client("connected\n"); /* we are connected to server, or proxy */ if (context->http_proxy_port) { + /* OK from now on we talk via the proxy, so connect to that */ + + /* + * (will overwrite existing pointer, + * leaving old string/frag there but unreferenced) + */ + if (lws_hdr_simple_create(wsi, _WSI_TOKEN_CLIENT_PEER_ADDRESS, + context->http_proxy_address)) + goto failed; + wsi->u.hdr.ah->c_port = context->http_proxy_port; + n = send(wsi->sock, context->service_buffer, plen, MSG_NOSIGNAL); if (n < 0) { - compatible_close(wsi->sock); lwsl_debug("ERROR writing to proxy socket\n"); - goto oom4; + goto failed; } libwebsocket_set_timeout(wsi, @@ -116,12 +249,12 @@ struct libwebsocket *__libwebsocket_client_connect_2( wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE; pfd.fd = wsi->sock; - pfd.revents = POLLIN; + pfd.revents = LWS_POLLIN; n = libwebsocket_service_fd(context, &pfd); if (n < 0) - goto oom4; + goto failed; if (n) /* returns 1 on failure after closing wsi */ return NULL; @@ -131,7 +264,11 @@ struct libwebsocket *__libwebsocket_client_connect_2( oom4: free(wsi->u.hdr.ah); free(wsi); + return NULL; +failed: + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); return NULL; } @@ -166,25 +303,13 @@ libwebsocket_client_connect(struct libwebsocket_context *context, int ietf_version_or_minus_one) { struct libwebsocket *wsi; -#ifndef LWS_NO_EXTENSIONS - int n; - int m; - struct libwebsocket_extension *ext; - int handled; -#endif - -#ifndef LWS_OPENSSL_SUPPORT - if (ssl_connection) { - lwsl_err("libwebsockets not configured for ssl\n"); - return NULL; - } -#endif wsi = (struct libwebsocket *) malloc(sizeof(struct libwebsocket)); if (wsi == NULL) goto bail; memset(wsi, 0, sizeof(*wsi)); + wsi->sock = -1; /* -1 means just use latest supported */ @@ -196,11 +321,14 @@ libwebsocket_client_connect(struct libwebsocket_context *context, wsi->state = WSI_STATE_CLIENT_UNCONNECTED; wsi->protocol = NULL; wsi->pending_timeout = NO_PENDING_TIMEOUT; -#ifndef LWS_NO_EXTENSIONS - wsi->count_active_extensions = 0; -#endif + #ifdef LWS_OPENSSL_SUPPORT wsi->use_ssl = ssl_connection; +#else + if (ssl_connection) { + lwsl_err("libwebsockets not configured for ssl\n"); + goto bail; + } #endif if (lws_allocate_header_table(wsi)) @@ -237,30 +365,16 @@ libwebsocket_client_connect(struct libwebsocket_context *context, wsi->protocol = &context->protocols[0]; -#ifndef LWS_NO_EXTENSIONS /* * Check with each extension if it is able to route and proxy this * connection for us. For example, an extension like x-google-mux * can handle this and then we don't need an actual socket for this * connection. */ - - handled = 0; - ext = context->extensions; - n = 0; - - while (ext && ext->callback && !handled) { - m = ext->callback(context, ext, wsi, + + if (lws_ext_callback_for_each_extension_type(context, wsi, LWS_EXT_CALLBACK_CAN_PROXY_CLIENT_CONNECTION, - (void *)(long)n, (void *)address, port); - if (m) - handled = 1; - - ext++; - n++; - } - - if (handled) { + (void *)address, port) > 0) { lwsl_client("libwebsocket_client_connect: ext handling conn\n"); libwebsocket_set_timeout(wsi, @@ -270,10 +384,9 @@ libwebsocket_client_connect(struct libwebsocket_context *context, wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT; return wsi; } -#endif lwsl_client("libwebsocket_client_connect: direct conn\n"); - return __libwebsocket_client_connect_2(context, wsi); + return libwebsocket_client_connect_2(context, wsi); bail1: free(wsi->u.hdr.ah); @@ -321,8 +434,10 @@ libwebsocket_client_connect_extended(struct libwebsocket_context *context, ssl_connection, path, host, origin, protocol, ietf_version_or_minus_one); - if (ws && !ws->user_space && userdata) + if (ws && !ws->user_space && userdata) { + ws->user_space_externally_allocated = 1; ws->user_space = userdata ; + } return ws ; } diff --git a/lib/client-parser.c b/lib/client-parser.c index e548b4f0e4..d7b97cc3ed 100644 --- a/lib/client-parser.c +++ b/lib/client-parser.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2013 Andy Green + * Copyright (C) 2010-2014 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -26,10 +26,7 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) int callback_action = LWS_CALLBACK_CLIENT_RECEIVE; int handled; struct lws_tokens eff_buf; -#ifndef LWS_NO_EXTENSIONS - int n; int m; -#endif switch (wsi->lws_rx_parse_state) { case LWS_RXPS_NEW: @@ -319,7 +316,7 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) default: lwsl_parser("Reserved opc 0x%2X\n", wsi->u.ws.opcode); -#ifndef LWS_NO_EXTENSIONS + /* * It's something special we can't understand here. * Pass the payload up to the extension's parsing @@ -329,29 +326,18 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) eff_buf.token = &wsi->u.ws.rx_user_buffer[ LWS_SEND_BUFFER_PRE_PADDING]; eff_buf.token_len = wsi->u.ws.rx_user_buffer_head; + + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) { /* not handle or fail */ - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX, - wsi->active_extensions_user[n], - &eff_buf, 0); - if (m) - handled = 1; - } - - if (!handled) { -#else - { -#endif lwsl_ext("Unhandled ext opc 0x%x\n", wsi->u.ws.opcode); wsi->u.ws.rx_user_buffer_head = 0; return 0; } - + handled = 1; break; } @@ -366,23 +352,14 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) eff_buf.token = &wsi->u.ws.rx_user_buffer[ LWS_SEND_BUFFER_PRE_PADDING]; eff_buf.token_len = wsi->u.ws.rx_user_buffer_head; -#ifndef LWS_NO_EXTENSIONS - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, + + if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_PAYLOAD_RX, - wsi->active_extensions_user[n], - &eff_buf, 0); - if (m < 0) { - lwsl_ext( - "Ext '%s' failed to handle payload!\n", - wsi->active_extensions[n]->name); - return -1; - } - } -#endif - if (eff_buf.token_len <= 0) + &eff_buf, 0) < 0) /* fail */ + return -1; + + if (eff_buf.token_len <= 0 && + callback_action != LWS_CALLBACK_CLIENT_RECEIVE_PONG) goto already_done; eff_buf.token[eff_buf.token_len] = '\0'; @@ -393,7 +370,7 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) if (callback_action == LWS_CALLBACK_CLIENT_RECEIVE_PONG) lwsl_info("Client doing pong callback\n"); - wsi->protocol->callback( + m = wsi->protocol->callback( wsi->protocol->owning_server, wsi, (enum libwebsocket_callback_reasons)callback_action, @@ -401,6 +378,10 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) eff_buf.token, eff_buf.token_len); + /* if user code wants to close, let caller know */ + if (m) + return 1; + already_done: wsi->u.ws.rx_user_buffer_head = 0; break; @@ -416,7 +397,6 @@ int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c) lwsl_warn("Control frame asking for extended length is illegal\n"); /* kill the connection */ return -1; - } diff --git a/lib/client.c b/lib/client.c index 68b04824ef..3d19b29779 100644 --- a/lib/client.c +++ b/lib/client.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2013 Andy Green + * Copyright (C) 2010-2014 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,35 +21,59 @@ #include "private-libwebsockets.h" -#ifdef WIN32 -#include -#include -#else -#ifdef LWS_BUILTIN_GETIFADDRS -#include -#else -#include -#endif -#include -#include -#include -#endif +int lws_handshake_client(struct libwebsocket *wsi, unsigned char **buf, size_t len) +{ + int n; + + switch (wsi->mode) { + case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: + case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE: + case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY: + case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT: + case LWS_CONNMODE_WS_CLIENT: + for (n = 0; n < len; n++) + if (libwebsocket_client_rx_sm(wsi, *(*buf)++)) { + lwsl_debug("client_rx_sm failed\n"); + return 1; + } + return 0; + default: + break; + } + return 0; +} int lws_client_socket_service(struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd) + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { int n; char *p = (char *)&context->service_buffer[0]; int len; - char c; + unsigned char c; switch (wsi->mode) { + case LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT: + + /* + * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE + * timeout protection set in client-handshake.c + */ + + if (libwebsocket_client_connect_2(context, wsi) == NULL) { + /* closed */ + lwsl_client("closed\n"); + return -1; + } + + /* either still pending connection, or changed mode */ + return 0; + case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: /* handle proxy hung up on us */ - if (pollfd->revents & (POLLERR | POLLHUP)) { + if (pollfd->revents & LWS_POLLHUP) { lwsl_warn("Proxy connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); @@ -62,6 +86,13 @@ int lws_client_socket_service(struct libwebsocket_context *context, n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), 0); if (n < 0) { + + if (LWS_ERRNO == LWS_EAGAIN) { + lwsl_debug( + "Proxy read returned EAGAIN... retrying\n"); + return 0; + } + libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_err("ERROR reading from proxy socket\n"); @@ -89,25 +120,41 @@ int lws_client_socket_service(struct libwebsocket_context *context, * timeout protection set in client-handshake.c */ - #ifdef LWS_OPENSSL_SUPPORT - /* * take care of our libwebsocket_callback_on_writable * happening at a time when there's no real connection yet */ + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return -1; - pollfd->events &= ~POLLOUT; - - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLEAR_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLOUT); - +#ifdef LWS_OPENSSL_SUPPORT /* we can retry this... just cook the SSL BIO the first time */ if (wsi->use_ssl && !wsi->ssl) { +#if defined(CYASSL_SNI_HOST_NAME) || defined(SSL_CTRL_SET_TLSEXT_HOSTNAME) + const char *hostname = lws_hdr_simple_ptr(wsi, + _WSI_TOKEN_CLIENT_PEER_ADDRESS); +#endif wsi->ssl = SSL_new(context->ssl_client_ctx); +#ifndef USE_CYASSL + SSL_set_mode(wsi->ssl, + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); +#endif + /* + * use server name indication (SNI), if supported, + * when establishing connection + */ +#ifdef USE_CYASSL +#ifdef CYASSL_SNI_HOST_NAME + CyaSSL_UseSNI(wsi->ssl, CYASSL_SNI_HOST_NAME, + hostname, strlen(hostname)); +#endif +#else +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_set_tlsext_host_name(wsi->ssl, hostname); +#endif +#endif #ifdef USE_CYASSL /* @@ -167,7 +214,9 @@ int lws_client_socket_service(struct libwebsocket_context *context, "SSL_connect WANT_... retrying\n"); libwebsocket_callback_on_writable( context, wsi); - + + wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL; + return 0; /* no error */ } n = -1; @@ -178,14 +227,78 @@ int lws_client_socket_service(struct libwebsocket_context *context, * retry if new data comes until we * run into the connection timeout or win */ - - lwsl_err("SSL connect error %lu: %s\n", - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - return 0; + + n = ERR_get_error(); + if (n != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + n, + ERR_error_string(n, + (char *)context->service_buffer)); + return 0; + } } + } else + wsi->ssl = NULL; + /* fallthru */ + + case LWS_CONNMODE_WS_CLIENT_WAITING_SSL: + + if (wsi->use_ssl) { + + if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_SSL) { + lws_latency_pre(context, wsi); + n = SSL_connect(wsi->ssl); + lws_latency(context, wsi, + "SSL_connect LWS_CONNMODE_WS_CLIENT_WAITING_SSL", + n, n > 0); + + if (n < 0) { + n = SSL_get_error(wsi->ssl, n); + + if (n == SSL_ERROR_WANT_READ || + n == SSL_ERROR_WANT_WRITE) { + /* + * wants us to retry connect due to + * state of the underlying ssl layer... + * but since it may be stalled on + * blocked write, no incoming data may + * arrive to trigger the retry. + * Force (possibly many times if the SSL + * state persists in returning the + * condition code, but other sockets + * are getting serviced inbetweentimes) + * us to get called back when writable. + */ + + lwsl_info( + "SSL_connect WANT_... retrying\n"); + libwebsocket_callback_on_writable( + context, wsi); + + wsi->mode = LWS_CONNMODE_WS_CLIENT_WAITING_SSL; + + return 0; /* no error */ + } + n = -1; + } + + if (n <= 0) { + /* + * retry if new data comes until we + * run into the connection timeout or win + */ + n = ERR_get_error(); + if (n != SSL_ERROR_NONE) { + lwsl_err("SSL connect error %lu: %s\n", + n, + ERR_error_string(n, + (char *)context->service_buffer)); + return 0; + } + } + } + #ifndef USE_CYASSL /* * See comment above about CyaSSL certificate @@ -210,7 +323,15 @@ int lws_client_socket_service(struct libwebsocket_context *context, } else wsi->ssl = NULL; #endif + + wsi->mode = LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2; + libwebsocket_set_timeout(wsi, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, + AWAITING_TIMEOUT); + + /* fallthru */ + case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2: p = libwebsockets_generate_client_handshake(context, wsi, p); if (p == NULL) { lwsl_err("Failed to generate handshake for client\n"); @@ -222,23 +343,18 @@ int lws_client_socket_service(struct libwebsocket_context *context, /* send our request to the server */ lws_latency_pre(context, wsi); -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->use_ssl) - n = SSL_write(wsi->ssl, context->service_buffer, - p - (char *)context->service_buffer); - else -#endif - n = send(wsi->sock, context->service_buffer, - p - (char *)context->service_buffer, MSG_NOSIGNAL); - lws_latency(context, wsi, - "send or SSL_write LWS_CONNMODE...HANDSHAKE", - n, n >= 0); - if (n < 0) { + n = lws_ssl_capable_write(wsi, context->service_buffer, p - (char *)context->service_buffer); + lws_latency(context, wsi, "send lws_issue_raw", n, n == p - (char *)context->service_buffer); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: lwsl_debug("ERROR writing to client socket\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + libwebsocket_callback_on_writable(context, wsi); + break; } wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; @@ -253,7 +369,7 @@ int lws_client_socket_service(struct libwebsocket_context *context, /* handle server hung up on us */ - if (pollfd->revents & (POLLERR | POLLHUP)) { + if (pollfd->revents & LWS_POLLHUP) { lwsl_debug("Server connection %p (fd=%d) dead\n", (void *)wsi, pollfd->fd); @@ -261,7 +377,7 @@ int lws_client_socket_service(struct libwebsocket_context *context, goto bail3; } - if (!(pollfd->revents & POLLIN)) + if (!(pollfd->revents & LWS_POLLIN)) break; /* interpret the server response */ @@ -283,29 +399,19 @@ int lws_client_socket_service(struct libwebsocket_context *context, * in one packet, since at that point the connection is * definitively ready from browser pov. */ - len = 1; while (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE && len > 0) { -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->use_ssl) { - len = SSL_read(wsi->ssl, &c, 1); - if (len < 0) { - n = SSL_get_error(wsi->ssl, len); - if (n == SSL_ERROR_WANT_READ || - n == SSL_ERROR_WANT_WRITE) - return 0; - } - } else -#endif - len = recv(wsi->sock, &c, 1, 0); - - if (len < 0) { - lwsl_warn("error on parsing recv\n"); + n = lws_ssl_capable_read(wsi, &c, 1); + lws_latency(context, wsi, "send lws_issue_raw", n, n == 1); + switch (n) { + case LWS_SSL_CAPABLE_ERROR: goto bail3; + case LWS_SSL_CAPABLE_MORE_SERVICE: + return 0; } - if (libwebsocket_parse(wsi, c)) { + if (libwebsocket_parse(context, wsi, c)) { lwsl_warn("problems parsing header\n"); goto bail3; } @@ -646,7 +752,7 @@ lws_client_interpret_server_handshake(struct libwebsocket_context *context, } lwsl_info("Allocating client RX buffer %d\n", n); - if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, &n, sizeof n)) { + if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) { lwsl_warn("Failed to set SNDBUF to %d", n); goto bail3; } @@ -715,7 +821,6 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, int n; #ifndef LWS_NO_EXTENSIONS struct libwebsocket_extension *ext; - struct libwebsocket_extension *ext1; int ext_count = 0; #endif @@ -788,17 +893,9 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, ext = context->extensions; while (ext && ext->callback) { - n = 0; - ext1 = context->extensions; - - while (ext1 && ext1->callback) { - n |= ext1->callback(context, ext1, wsi, - LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION, - NULL, (char *)ext->name, 0); - - ext1++; - } - + n = lws_ext_callback_for_each_extension_type(context, wsi, + LWS_EXT_CALLBACK_CHECK_OK_TO_PROPOSE_EXTENSION, + (char *)ext->name, 0); if (n) { /* an extension vetos us */ lwsl_ext("ext %s vetoed\n", (char *)ext->name); ext++; diff --git a/lib/context.c b/lib/context.c new file mode 100755 index 0000000000..f2f30ade04 --- /dev/null +++ b/lib/context.c @@ -0,0 +1,341 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + */ + +#include "private-libwebsockets.h" + +#ifndef LWS_BUILD_HASH +#define LWS_BUILD_HASH "unknown-build-hash" +#endif + +static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; + +/** + * lws_get_library_version: get version and git hash library built from + * + * returns a const char * to a string like "1.1 178d78c" + * representing the library version followed by the git head hash it + * was built from + */ + +LWS_VISIBLE const char * +lws_get_library_version(void) +{ + return library_version; +} + +/** + * libwebsocket_create_context() - Create the websocket handler + * @info: pointer to struct with parameters + * + * This function creates the listening socket (if serving) and takes care + * of all initialization in one step. + * + * After initialization, it returns a struct libwebsocket_context * that + * represents this server. After calling, user code needs to take care + * of calling libwebsocket_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 callack 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 struct libwebsocket_context * +libwebsocket_create_context(struct lws_context_creation_info *info) +{ + struct libwebsocket_context *context = NULL; + char *p; + + int pid_daemon = get_daemonize_pid(); + + lwsl_notice("Initial logging level %d\n", log_level); + lwsl_notice("Library version: %s\n", library_version); +#ifdef LWS_USE_IPV6 + if (!(info->options & LWS_SERVER_OPTION_DISABLE_IPV6)) + lwsl_notice("IPV6 compiled in and enabled\n"); + else + lwsl_notice("IPV6 compiled in but disabled\n"); +#else + lwsl_notice("IPV6 not compiled in\n"); +#endif + lws_feature_status_libev(info); + lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN); + lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS); + + lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED); + lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT); + lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); + lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER); + + if (lws_plat_context_early_init()) + return NULL; + + context = (struct libwebsocket_context *) + malloc(sizeof(struct libwebsocket_context)); + if (!context) { + lwsl_err("No memory for websocket context\n"); + return NULL; + } + memset(context, 0, sizeof(*context)); + + if (pid_daemon) { + context->started_with_parent = pid_daemon; + lwsl_notice(" Started with daemon pid %d\n", pid_daemon); + } + + context->listen_service_extraseen = 0; + context->protocols = info->protocols; + context->token_limits = info->token_limits; + context->listen_port = info->port; + context->http_proxy_port = 0; + context->http_proxy_address[0] = '\0'; + context->options = info->options; + context->iface = info->iface; + /* to reduce this allocation, */ + context->max_fds = getdtablesize(); + lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n", + sizeof(struct libwebsocket_context), + sizeof(struct libwebsocket_pollfd) + + sizeof(struct libwebsocket *), + context->max_fds, + sizeof(struct libwebsocket_context) + + ((sizeof(struct libwebsocket_pollfd) + + sizeof(struct libwebsocket *)) * + context->max_fds)); + + context->fds = (struct libwebsocket_pollfd *) + malloc(sizeof(struct libwebsocket_pollfd) * + context->max_fds); + if (context->fds == NULL) { + lwsl_err("Unable to allocate fds array for %d connections\n", + context->max_fds); + free(context); + return NULL; + } + + context->lws_lookup = (struct libwebsocket **) + malloc(sizeof(struct libwebsocket *) * context->max_fds); + if (context->lws_lookup == NULL) { + lwsl_err( + "Unable to allocate lws_lookup array for %d connections\n", + context->max_fds); + free(context->fds); + free(context); + return NULL; + } + memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) * + context->max_fds); + + if (lws_plat_init_fd_tables(context)) { + free(context->lws_lookup); + free(context->fds); + free(context); + return NULL; + } + + lws_context_init_extensions(info, context); + + context->user_space = info->user; + + strcpy(context->canonical_hostname, "unknown"); + + lws_server_get_canonical_hostname(context, info); + + /* split the proxy ads:port if given */ + + if (info->http_proxy_address) { + strncpy(context->http_proxy_address, info->http_proxy_address, + sizeof(context->http_proxy_address) - 1); + context->http_proxy_address[ + sizeof(context->http_proxy_address) - 1] = '\0'; + context->http_proxy_port = info->http_proxy_port; + } else { +#ifdef HAVE_GETENV + p = getenv("http_proxy"); + if (p) { + strncpy(context->http_proxy_address, p, + sizeof(context->http_proxy_address) - 1); + context->http_proxy_address[ + sizeof(context->http_proxy_address) - 1] = '\0'; + + p = strchr(context->http_proxy_address, ':'); + if (p == NULL) { + lwsl_err("http_proxy needs to be ads:port\n"); + goto bail; + } + *p = '\0'; + context->http_proxy_port = atoi(p + 1); + } +#endif + } + + if (context->http_proxy_address[0]) + lwsl_notice(" Proxy %s:%u\n", + context->http_proxy_address, + context->http_proxy_port); + + lwsl_notice( + " per-conn mem: %u + %u headers + protocol rx buf\n", + sizeof(struct libwebsocket), + sizeof(struct allocated_headers)); + + if (lws_context_init_server_ssl(info, context)) + goto bail; + + if (lws_context_init_client_ssl(info, context)) + goto bail; + + if (lws_context_init_server(info, context)) + goto bail; + + /* + * drop any root privs for this process + * to listen on port < 1023 we would have needed root, but now we are + * listening, we don't want the power for anything else + */ + lws_plat_drop_app_privileges(info); + + /* initialize supported protocols */ + + for (context->count_protocols = 0; + info->protocols[context->count_protocols].callback; + context->count_protocols++) { + + lwsl_parser(" Protocol: %s\n", + info->protocols[context->count_protocols].name); + + info->protocols[context->count_protocols].owning_server = + context; + info->protocols[context->count_protocols].protocol_index = + context->count_protocols; + + /* + * inform all the protocols that they are doing their one-time + * initialization if they want to + */ + info->protocols[context->count_protocols].callback(context, + NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); + } + + /* + * give all extensions a chance to create any per-context + * allocations they need + */ + + if (info->port != CONTEXT_PORT_NO_LISTEN) { + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, + NULL, 0) < 0) + goto bail; + } else + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, + NULL, 0) < 0) + goto bail; + + return context; + +bail: + libwebsocket_context_destroy(context); + return NULL; +} + +/** + * libwebsocket_context_destroy() - Destroy the websocket context + * @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 void +libwebsocket_context_destroy(struct libwebsocket_context *context) +{ + int n; + struct libwebsocket_protocols *protocol = context->protocols; + + lwsl_notice("%s\n", __func__); + +#ifdef LWS_LATENCY + if (context->worst_latency_info[0]) + lwsl_notice("Worst latency: %s\n", context->worst_latency_info); +#endif + + for (n = 0; n < context->fds_count; n++) { + struct libwebsocket *wsi = + context->lws_lookup[context->fds[n].fd]; + if (!wsi) + continue; + libwebsocket_close_and_free_session(context, + wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */); + n--; + } + + /* + * give all extensions a chance to clean up any per-context + * allocations they might have made + */ + if (context->listen_port) { + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT, NULL, 0) < 0) + return; + } else + if (lws_ext_callback_for_each_extension_type(context, NULL, + LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT, NULL, 0) < 0) + return; + + /* + * inform all the protocols that they are done and will have no more + * callbacks + */ + + while (protocol->callback) { + protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY, + NULL, NULL, 0); + protocol++; + } + + lws_plat_context_early_destroy(context); + + lws_ssl_context_destroy(context); + + if (context->fds) + free(context->fds); + if (context->lws_lookup) + free(context->lws_lookup); + + lws_plat_context_late_destroy(context); + + free(context); +} diff --git a/lib/extension-deflate-frame.c b/lib/extension-deflate-frame.c index b206476bcd..6cf6ac0161 100644 --- a/lib/extension-deflate-frame.c +++ b/lib/extension-deflate-frame.c @@ -163,7 +163,7 @@ int lws_extension_callback_deflate_frame( * we will get a destroy callback to take care * of closing nicely */ - lwsl_err("zlib error inflate %d: %s\n", + lwsl_info("zlib error inflate %d: %s\n", n, conn->zs_in.msg); return -1; } diff --git a/lib/extension.c b/lib/extension.c index d4660c052d..1e8370c51f 100644 --- a/lib/extension.c +++ b/lib/extension.c @@ -27,7 +27,185 @@ struct libwebsocket_extension libwebsocket_internal_extensions[] = { } }; +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct libwebsocket_context *context) +{ + context->extensions = info->extensions; + lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", LWS_MAX_EXTENSIONS_ACTIVE); +} + LWS_VISIBLE struct libwebsocket_extension *libwebsocket_get_internal_extensions() { return libwebsocket_internal_extensions; } + + +/* 0 = nobody had nonzero return, 1 = somebody had positive return, -1 = fail */ + +int lws_ext_callback_for_each_active(struct libwebsocket *wsi, int reason, + void *arg, int len) +{ + int n, m, handled = 0; + + for (n = 0; n < wsi->count_active_extensions; n++) { + m = wsi->active_extensions[n]->callback( + wsi->protocol->owning_server, + wsi->active_extensions[n], wsi, + reason, + wsi->active_extensions_user[n], + arg, len); + if (m < 0) { + lwsl_ext( + "Extension '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + if (m > handled) + handled = m; + } + + return handled; +} + +int lws_ext_callback_for_each_extension_type( + struct libwebsocket_context *context, struct libwebsocket *wsi, + int reason, void *arg, int len) +{ + int n = 0, m, handled = 0; + struct libwebsocket_extension *ext = context->extensions; + + while (ext && ext->callback && !handled) { + m = ext->callback(context, ext, wsi, reason, + (void *)(long)n, arg, len); + if (m < 0) { + lwsl_ext( + "Extension '%s' failed to handle callback %d!\n", + wsi->active_extensions[n]->name, reason); + return -1; + } + if (m) + handled = 1; + + ext++; + n++; + } + + return 0; +} + +int +lws_issue_raw_ext_access(struct libwebsocket *wsi, + unsigned char *buf, size_t len) +{ + int ret; + struct lws_tokens eff_buf; + int m; + int n = 0; + + eff_buf.token = (char *)buf; + eff_buf.token_len = len; + + /* + * while we have original buf to spill ourselves, or extensions report + * more in their pipeline + */ + + ret = 1; + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + + /* show every extension the new incoming data */ + m = lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PACKET_TX_PRESEND, &eff_buf, 0); + if (m < 0) + return -1; + if (m) /* handled */ + ret = 1; + + if ((char *)buf != eff_buf.token) + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + + /* assuming they left us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from ext access\n"); + return -1; + } + + /* always either sent it all or privately buffered */ + if (wsi->u.ws.clean_buffer) { + eff_buf.token_len = n; + len = n; + } + + } + + lwsl_parser("written %d bytes to client\n", n); + + /* no extension has more to spill? Then we can go */ + + if (!ret) + break; + + /* we used up what we had */ + + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* + * Did that leave the pipe choked? + * Or we had to hold on to some of it? + */ + + if (!lws_send_pipe_choked(wsi) && !wsi->truncated_send_len) + /* no we could add more, lets's do that */ + continue; + + lwsl_debug("choked\n"); + + /* + * Yes, he's choked. Don't spill the rest now get a callback + * when he is ready to send and take care of it there + */ + libwebsocket_callback_on_writable( + wsi->protocol->owning_server, wsi); + wsi->extension_data_pending = 1; + ret = 0; + } + + return len; +} + +int +lws_any_extension_handled(struct libwebsocket_context *context, + struct libwebsocket *wsi, + enum libwebsocket_extension_callback_reasons r, + void *v, size_t len) +{ + int n; + int handled = 0; + + /* maybe an extension will take care of it for us */ + + for (n = 0; n < wsi->count_active_extensions && !handled; n++) { + if (!wsi->active_extensions[n]->callback) + continue; + + handled |= wsi->active_extensions[n]->callback(context, + wsi->active_extensions[n], wsi, + r, wsi->active_extensions_user[n], v, len); + } + + return handled; +} diff --git a/lib/getifaddrs.h b/lib/getifaddrs.h index 7871932334..da69b50e6a 100644 --- a/lib/getifaddrs.h +++ b/lib/getifaddrs.h @@ -40,8 +40,8 @@ extern "C" { /* $KTH: ifaddrs.hin,v 1.3 2000/12/11 00:01:13 assar Exp $ */ -#ifndef __ifaddrs_h__ -#define __ifaddrs_h__ +#ifndef ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 +#define ifaddrs_h_7467027A95AD4B5C8DDD40FE7D973791 /* * the interface is defined in terms of the fields below, and this is diff --git a/lib/handshake.c b/lib/handshake.c index 3007426b92..421494a251 100644 --- a/lib/handshake.c +++ b/lib/handshake.c @@ -46,6 +46,10 @@ * Sec-WebSocket-Protocol: chat */ +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + /* * We have to take care about parsing because the headers may be split * into multiple fragments. They may contain unknown headers with arbitrary @@ -58,260 +62,135 @@ libwebsocket_read(struct libwebsocket_context *context, struct libwebsocket *wsi, unsigned char *buf, size_t len) { size_t n; - struct allocated_headers *ah; - char *uri_ptr; - int uri_len; + int body_chunk_len; + unsigned char *last_char; switch (wsi->state) { - case WSI_STATE_HTTP_ISSUING_FILE: +http_new: case WSI_STATE_HTTP: + case WSI_STATE_HTTP_ISSUING_FILE: wsi->state = WSI_STATE_HTTP_HEADERS; wsi->u.hdr.parser_state = WSI_TOKEN_NAME_PART; wsi->u.hdr.lextable_pos = 0; /* fallthru */ case WSI_STATE_HTTP_HEADERS: - lwsl_parser("issuing %d bytes to parser\n", (int)len); -#ifndef LWS_NO_CLIENT - switch (wsi->mode) { - case LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY: - case LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE: - case LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY: - case LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT: - case LWS_CONNMODE_WS_CLIENT: - for (n = 0; n < len; n++) - if (libwebsocket_client_rx_sm(wsi, *buf++)) { - lwsl_info("client_rx_sm failed\n"); - goto bail; - } - return 0; - default: - break; - } -#endif -#ifndef LWS_NO_SERVER - /* LWS_CONNMODE_WS_SERVING */ - - for (n = 0; n < len; n++) - if (libwebsocket_parse(wsi, *buf++)) { - lwsl_info("libwebsocket_parse failed\n"); - goto bail_nuke_ah; - } - - if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) - break; - - lwsl_parser("libwebsocket_parse sees parsing complete\n"); - - wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT; - - /* is this websocket protocol or normal http 1.0? */ - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) || - !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { - - /* it's not websocket.... shall we accept it as http? */ - - if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) { - lwsl_warn("Missing URI in HTTP request\n"); - goto bail_nuke_ah; - } - - lwsl_info("HTTP request for '%s'\n", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI)); - - if (libwebsocket_ensure_user_space(wsi)) - goto bail_nuke_ah; - - /* - * Hm we still need the headers so the - * callback can look at leaders like the URI, but we - * need to transition to http union state.... hold a - * copy of u.hdr.ah and deallocate afterwards - */ - - ah = wsi->u.hdr.ah; - uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); - uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); - - /* union transition */ - memset(&wsi->u, 0, sizeof(wsi->u)); - - wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED; - wsi->state = WSI_STATE_HTTP; - n = 0; - if (wsi->protocol->callback) - n = wsi->protocol->callback(context, wsi, - LWS_CALLBACK_HTTP, - wsi->user_space, uri_ptr, uri_len); - - /* now drop the header info we kept a pointer to */ - if (ah) - free(ah); - - if (n) { - lwsl_info("LWS_CALLBACK_HTTP closing\n"); - goto bail; /* struct ah ptr already nuked */ - } - - return 0; - } - - if (!wsi->protocol) - lwsl_err("NULL protocol at libwebsocket_read\n"); - - /* - * It's websocket - * - * Make sure user side is happy about protocol - */ - - while (wsi->protocol->callback) { + if (lws_handshake_client(wsi, &buf, len)) + goto bail; - if (!lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL)) { - if (wsi->protocol->name == NULL) - break; - } else - if (wsi->protocol->name && strcmp( - lws_hdr_simple_ptr(wsi, - WSI_TOKEN_PROTOCOL), - wsi->protocol->name) == 0) - break; + last_char = buf; + if (lws_handshake_server(context, wsi, &buf, len)) + /* Handshake indicates this session is done. */ + goto bail; - wsi->protocol++; + /* It's possible that we've exhausted our data already, but + * lws_handshake_server doesn't update len for us. Figure out how + * much was read, so that we can proceed appropriately: */ + len -= (buf - last_char); + + if (!wsi->hdr_parsing_completed) + /* More header content on the way */ + goto read_ok; + + switch (wsi->state) { + case WSI_STATE_HTTP: + case WSI_STATE_HTTP_HEADERS: + goto http_complete; + case WSI_STATE_HTTP_ISSUING_FILE: + goto read_ok; + case WSI_STATE_HTTP_BODY: + wsi->u.http.content_remain = wsi->u.http.content_length; + goto http_postbody; + default: + break; } + break; - /* we didn't find a protocol he wanted? */ - - if (wsi->protocol->callback == NULL) { - if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) == - NULL) { - lwsl_info("no protocol -> prot 0 handler\n"); - wsi->protocol = &context->protocols[0]; - } else { - lwsl_err("Req protocol %s not supported\n", - lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL)); - goto bail_nuke_ah; + case WSI_STATE_HTTP_BODY: +http_postbody: + while (len && wsi->u.http.content_remain) { + /* Copy as much as possible, up to the limit of: + * what we have in the read buffer (len) + * remaining portion of the POST body (content_remain) + */ + body_chunk_len = min(wsi->u.http.content_remain,len); + wsi->u.http.content_remain -= body_chunk_len; + len -= body_chunk_len; + + if (wsi->protocol->callback) { + n = wsi->protocol->callback( + wsi->protocol->owning_server, wsi, + LWS_CALLBACK_HTTP_BODY, wsi->user_space, + buf, body_chunk_len); + if (n) + goto bail; } - } - - /* allocate wsi->user storage */ - if (libwebsocket_ensure_user_space(wsi)) - goto bail_nuke_ah; - - /* - * Give the user code a chance to study the request and - * have the opportunity to deny it - */ - - if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi, - LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, - wsi->user_space, - lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { - lwsl_warn("User code denied connection\n"); - goto bail_nuke_ah; - } - - - /* - * Perform the handshake according to the protocol version the - * client announced - */ - - switch (wsi->ietf_spec_revision) { - case 13: - lwsl_parser("lws_parse calling handshake_04\n"); - if (handshake_0405(context, wsi)) { - lwsl_info("hs0405 has failed the connection\n"); - goto bail_nuke_ah; + buf += body_chunk_len; + + if (!wsi->u.http.content_remain) { + /* he sent the content in time */ + libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + if (wsi->protocol->callback) { + n = wsi->protocol->callback( + wsi->protocol->owning_server, wsi, + LWS_CALLBACK_HTTP_BODY_COMPLETION, + wsi->user_space, NULL, 0); + if (n) + goto bail; + } + goto http_complete; } - break; - - default: - lwsl_warn("Unknown client spec version %d\n", - wsi->ietf_spec_revision); - goto bail_nuke_ah; - } - - /* drop the header info -- no bail_nuke_ah after this */ - - if (wsi->u.hdr.ah) - free(wsi->u.hdr.ah); - - wsi->mode = LWS_CONNMODE_WS_SERVING; - - /* union transition */ - memset(&wsi->u, 0, sizeof(wsi->u)); - wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* - * create the frame buffer for this connection according to the - * size mentioned in the protocol definition. If 0 there, use - * a big default for compatibility - */ - - n = wsi->protocol->rx_buffer_size; - if (!n) - n = LWS_MAX_SOCKET_IO_BUF; - n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING; - wsi->u.ws.rx_user_buffer = malloc(n); - if (!wsi->u.ws.rx_user_buffer) { - lwsl_err("Out of Mem allocating rx buffer %d\n", n); - goto bail; } - lwsl_info("Allocating RX buffer %d\n", n); - - if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, &n, sizeof n)) { - lwsl_warn("Failed to set SNDBUF to %d", n); - goto bail; - } - - lwsl_parser("accepted v%02d connection\n", - wsi->ietf_spec_revision); -#endif break; - case WSI_STATE_AWAITING_CLOSE_ACK: case WSI_STATE_ESTABLISHED: -#ifndef LWS_NO_CLIENT + case WSI_STATE_AWAITING_CLOSE_ACK: + if (lws_handshake_client(wsi, &buf, len)) + goto bail; switch (wsi->mode) { - case LWS_CONNMODE_WS_CLIENT: - for (n = 0; n < len; n++) - if (libwebsocket_client_rx_sm( - wsi, *buf++) < 0) { - lwsl_info("client rx has bailed\n"); - goto bail; - } + case LWS_CONNMODE_WS_SERVING: - return 0; - default: + if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) { + lwsl_info("interpret_incoming_packet has bailed\n"); + goto bail; + } break; } -#endif -#ifndef LWS_NO_SERVER - /* LWS_CONNMODE_WS_SERVING */ - - if (libwebsocket_interpret_incoming_packet(wsi, buf, len) < 0) { - lwsl_info("interpret_incoming_packet has bailed\n"); - goto bail; - } -#endif break; default: lwsl_err("libwebsocket_read: Unhandled state\n"); break; } +read_ok: + /* Nothing more to do for now. */ + lwsl_debug("libwebsocket_read: read_ok\n"); + return 0; -bail_nuke_ah: - /* drop the header info */ - if (wsi->u.hdr.ah) - free(wsi->u.hdr.ah); +http_complete: + lwsl_debug("libwebsocket_read: http_complete\n"); + /* Handle keep-alives, by preparing for a new request: */ + if (wsi->u.http.connection_type == HTTP_CONNECTION_KEEP_ALIVE) { + lwsl_debug("libwebsocket_read: keep-alive\n"); + wsi->state = WSI_STATE_HTTP; + wsi->mode = LWS_CONNMODE_HTTP_SERVING; + /* We might be streaming HTTP content, so leave the connection open.*/ + libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + if (lws_allocate_header_table(wsi)) + goto bail; + + /* If we have more data, loop back around: */ + if (len) + goto http_new; + + return 0; + } bail: - lwsl_info("closing connection at libwebsocket_read bail:\n"); + lwsl_debug("closing connection at libwebsocket_read bail:\n"); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); diff --git a/lib/lextable.h b/lib/lextable.h new file mode 100755 index 0000000000..9d86577a5f --- /dev/null +++ b/lib/lextable.h @@ -0,0 +1,338 @@ +/* pos 0000: 0 */ 0x67 /* 'g' */, 0x25, 0x00 /* (to 0x0025 state 1) */, + 0x70 /* 'p' */, 0x27, 0x00 /* (to 0x002A state 5) */, + 0x6F /* 'o' */, 0x30, 0x00 /* (to 0x0036 state 10) */, + 0x68 /* 'h' */, 0x3C, 0x00 /* (to 0x0045 state 18) */, + 0x63 /* 'c' */, 0x45, 0x00 /* (to 0x0051 state 23) */, + 0x73 /* 's' */, 0x60, 0x00 /* (to 0x006F state 34) */, + 0x75 /* 'u' */, 0x9F, 0x00 /* (to 0x00B1 state 64) */, + 0x0D /* '.' */, 0xB3, 0x00 /* (to 0x00C8 state 84) */, + 0x61 /* 'a' */, 0xEA, 0x00 /* (to 0x0102 state 134) */, + 0x69 /* 'i' */, 0x1D, 0x01 /* (to 0x0138 state 168) */, + 0x64 /* 'd' */, 0x9C, 0x01 /* (to 0x01BA state 270) */, + 0x72 /* 'r' */, 0x9F, 0x01 /* (to 0x01C0 state 275) */, + 0x08, /* fail */ +/* pos 0025: 1 */ 0xE5 /* 'e' -> */, +/* pos 0026: 2 */ 0xF4 /* 't' -> */, +/* pos 0027: 3 */ 0xA0 /* ' ' -> */, +/* pos 0028: 4 */ 0x00, 0x00 /* - terminal marker 0 - */, +/* pos 002a: 5 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0031 state 6) */, + 0x72 /* 'r' */, 0x4B, 0x01 /* (to 0x0178 state 216) */, + 0x08, /* fail */ +/* pos 0031: 6 */ 0xF3 /* 's' -> */, +/* pos 0032: 7 */ 0xF4 /* 't' -> */, +/* pos 0033: 8 */ 0xA0 /* ' ' -> */, +/* pos 0034: 9 */ 0x00, 0x01 /* - terminal marker 1 - */, +/* pos 0036: 10 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x003D state 11) */, + 0x72 /* 'r' */, 0x81, 0x00 /* (to 0x00BA state 72) */, + 0x08, /* fail */ +/* pos 003d: 11 */ 0xF4 /* 't' -> */, +/* pos 003e: 12 */ 0xE9 /* 'i' -> */, +/* pos 003f: 13 */ 0xEF /* 'o' -> */, +/* pos 0040: 14 */ 0xEE /* 'n' -> */, +/* pos 0041: 15 */ 0xF3 /* 's' -> */, +/* pos 0042: 16 */ 0xA0 /* ' ' -> */, +/* pos 0043: 17 */ 0x00, 0x02 /* - terminal marker 2 - */, +/* pos 0045: 18 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x004C state 19) */, + 0x74 /* 't' */, 0xB1, 0x00 /* (to 0x00F9 state 126) */, + 0x08, /* fail */ +/* pos 004c: 19 */ 0xF3 /* 's' -> */, +/* pos 004d: 20 */ 0xF4 /* 't' -> */, +/* pos 004e: 21 */ 0xBA /* ':' -> */, +/* pos 004f: 22 */ 0x00, 0x03 /* - terminal marker 3 - */, +/* pos 0051: 23 */ 0x6F /* 'o' */, 0x07, 0x00 /* (to 0x0058 state 24) */, + 0x61 /* 'a' */, 0x2B, 0x01 /* (to 0x017F state 222) */, + 0x08, /* fail */ +/* pos 0058: 24 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x005F state 25) */, + 0x6F /* 'o' */, 0x40, 0x01 /* (to 0x019B state 248) */, + 0x08, /* fail */ +/* pos 005f: 25 */ 0x6E /* 'n' */, 0x07, 0x00 /* (to 0x0066 state 26) */, + 0x74 /* 't' */, 0x3F, 0x01 /* (to 0x01A1 state 253) */, + 0x08, /* fail */ +/* pos 0066: 26 */ 0xE5 /* 'e' -> */, +/* pos 0067: 27 */ 0xE3 /* 'c' -> */, +/* pos 0068: 28 */ 0xF4 /* 't' -> */, +/* pos 0069: 29 */ 0xE9 /* 'i' -> */, +/* pos 006a: 30 */ 0xEF /* 'o' -> */, +/* pos 006b: 31 */ 0xEE /* 'n' -> */, +/* pos 006c: 32 */ 0xBA /* ':' -> */, +/* pos 006d: 33 */ 0x00, 0x04 /* - terminal marker 4 - */, +/* pos 006f: 34 */ 0xE5 /* 'e' -> */, +/* pos 0070: 35 */ 0xE3 /* 'c' -> */, +/* pos 0071: 36 */ 0xAD /* '-' -> */, +/* pos 0072: 37 */ 0xF7 /* 'w' -> */, +/* pos 0073: 38 */ 0xE5 /* 'e' -> */, +/* pos 0074: 39 */ 0xE2 /* 'b' -> */, +/* pos 0075: 40 */ 0xF3 /* 's' -> */, +/* pos 0076: 41 */ 0xEF /* 'o' -> */, +/* pos 0077: 42 */ 0xE3 /* 'c' -> */, +/* pos 0078: 43 */ 0xEB /* 'k' -> */, +/* pos 0079: 44 */ 0xE5 /* 'e' -> */, +/* pos 007a: 45 */ 0xF4 /* 't' -> */, +/* pos 007b: 46 */ 0xAD /* '-' -> */, +/* pos 007c: 47 */ 0x6B /* 'k' */, 0x19, 0x00 /* (to 0x0095 state 48) */, + 0x70 /* 'p' */, 0x28, 0x00 /* (to 0x00A7 state 55) */, + 0x64 /* 'd' */, 0x3F, 0x00 /* (to 0x00C1 state 78) */, + 0x76 /* 'v' */, 0x48, 0x00 /* (to 0x00CD state 87) */, + 0x6F /* 'o' */, 0x4E, 0x00 /* (to 0x00D6 state 95) */, + 0x65 /* 'e' */, 0x53, 0x00 /* (to 0x00DE state 102) */, + 0x61 /* 'a' */, 0x5C, 0x00 /* (to 0x00EA state 113) */, + 0x6E /* 'n' */, 0x61, 0x00 /* (to 0x00F2 state 120) */, + 0x08, /* fail */ +/* pos 0095: 48 */ 0xE5 /* 'e' -> */, +/* pos 0096: 49 */ 0xF9 /* 'y' -> */, +/* pos 0097: 50 */ 0x31 /* '1' */, 0x0A, 0x00 /* (to 0x00A1 state 51) */, + 0x32 /* '2' */, 0x0A, 0x00 /* (to 0x00A4 state 53) */, + 0x3A /* ':' */, 0x2E, 0x00 /* (to 0x00CB state 86) */, + 0x08, /* fail */ +/* pos 00a1: 51 */ 0xBA /* ':' -> */, +/* pos 00a2: 52 */ 0x00, 0x05 /* - terminal marker 5 - */, +/* pos 00a4: 53 */ 0xBA /* ':' -> */, +/* pos 00a5: 54 */ 0x00, 0x06 /* - terminal marker 6 - */, +/* pos 00a7: 55 */ 0xF2 /* 'r' -> */, +/* pos 00a8: 56 */ 0xEF /* 'o' -> */, +/* pos 00a9: 57 */ 0xF4 /* 't' -> */, +/* pos 00aa: 58 */ 0xEF /* 'o' -> */, +/* pos 00ab: 59 */ 0xE3 /* 'c' -> */, +/* pos 00ac: 60 */ 0xEF /* 'o' -> */, +/* pos 00ad: 61 */ 0xEC /* 'l' -> */, +/* pos 00ae: 62 */ 0xBA /* ':' -> */, +/* pos 00af: 63 */ 0x00, 0x07 /* - terminal marker 7 - */, +/* pos 00b1: 64 */ 0xF0 /* 'p' -> */, +/* pos 00b2: 65 */ 0xE7 /* 'g' -> */, +/* pos 00b3: 66 */ 0xF2 /* 'r' -> */, +/* pos 00b4: 67 */ 0xE1 /* 'a' -> */, +/* pos 00b5: 68 */ 0xE4 /* 'd' -> */, +/* pos 00b6: 69 */ 0xE5 /* 'e' -> */, +/* pos 00b7: 70 */ 0xBA /* ':' -> */, +/* pos 00b8: 71 */ 0x00, 0x08 /* - terminal marker 8 - */, +/* pos 00ba: 72 */ 0xE9 /* 'i' -> */, +/* pos 00bb: 73 */ 0xE7 /* 'g' -> */, +/* pos 00bc: 74 */ 0xE9 /* 'i' -> */, +/* pos 00bd: 75 */ 0xEE /* 'n' -> */, +/* pos 00be: 76 */ 0xBA /* ':' -> */, +/* pos 00bf: 77 */ 0x00, 0x09 /* - terminal marker 9 - */, +/* pos 00c1: 78 */ 0xF2 /* 'r' -> */, +/* pos 00c2: 79 */ 0xE1 /* 'a' -> */, +/* pos 00c3: 80 */ 0xE6 /* 'f' -> */, +/* pos 00c4: 81 */ 0xF4 /* 't' -> */, +/* pos 00c5: 82 */ 0xBA /* ':' -> */, +/* pos 00c6: 83 */ 0x00, 0x0A /* - terminal marker 10 - */, +/* pos 00c8: 84 */ 0x8A /* '.' -> */, +/* pos 00c9: 85 */ 0x00, 0x0B /* - terminal marker 11 - */, +/* pos 00cb: 86 */ 0x00, 0x0C /* - terminal marker 12 - */, +/* pos 00cd: 87 */ 0xE5 /* 'e' -> */, +/* pos 00ce: 88 */ 0xF2 /* 'r' -> */, +/* pos 00cf: 89 */ 0xF3 /* 's' -> */, +/* pos 00d0: 90 */ 0xE9 /* 'i' -> */, +/* pos 00d1: 91 */ 0xEF /* 'o' -> */, +/* pos 00d2: 92 */ 0xEE /* 'n' -> */, +/* pos 00d3: 93 */ 0xBA /* ':' -> */, +/* pos 00d4: 94 */ 0x00, 0x0D /* - terminal marker 13 - */, +/* pos 00d6: 95 */ 0xF2 /* 'r' -> */, +/* pos 00d7: 96 */ 0xE9 /* 'i' -> */, +/* pos 00d8: 97 */ 0xE7 /* 'g' -> */, +/* pos 00d9: 98 */ 0xE9 /* 'i' -> */, +/* pos 00da: 99 */ 0xEE /* 'n' -> */, +/* pos 00db: 100 */ 0xBA /* ':' -> */, +/* pos 00dc: 101 */ 0x00, 0x0E /* - terminal marker 14 - */, +/* pos 00de: 102 */ 0xF8 /* 'x' -> */, +/* pos 00df: 103 */ 0xF4 /* 't' -> */, +/* pos 00e0: 104 */ 0xE5 /* 'e' -> */, +/* pos 00e1: 105 */ 0xEE /* 'n' -> */, +/* pos 00e2: 106 */ 0xF3 /* 's' -> */, +/* pos 00e3: 107 */ 0xE9 /* 'i' -> */, +/* pos 00e4: 108 */ 0xEF /* 'o' -> */, +/* pos 00e5: 109 */ 0xEE /* 'n' -> */, +/* pos 00e6: 110 */ 0xF3 /* 's' -> */, +/* pos 00e7: 111 */ 0xBA /* ':' -> */, +/* pos 00e8: 112 */ 0x00, 0x0F /* - terminal marker 15 - */, +/* pos 00ea: 113 */ 0xE3 /* 'c' -> */, +/* pos 00eb: 114 */ 0xE3 /* 'c' -> */, +/* pos 00ec: 115 */ 0xE5 /* 'e' -> */, +/* pos 00ed: 116 */ 0xF0 /* 'p' -> */, +/* pos 00ee: 117 */ 0xF4 /* 't' -> */, +/* pos 00ef: 118 */ 0xBA /* ':' -> */, +/* pos 00f0: 119 */ 0x00, 0x10 /* - terminal marker 16 - */, +/* pos 00f2: 120 */ 0xEF /* 'o' -> */, +/* pos 00f3: 121 */ 0xEE /* 'n' -> */, +/* pos 00f4: 122 */ 0xE3 /* 'c' -> */, +/* pos 00f5: 123 */ 0xE5 /* 'e' -> */, +/* pos 00f6: 124 */ 0xBA /* ':' -> */, +/* pos 00f7: 125 */ 0x00, 0x11 /* - terminal marker 17 - */, +/* pos 00f9: 126 */ 0xF4 /* 't' -> */, +/* pos 00fa: 127 */ 0xF0 /* 'p' -> */, +/* pos 00fb: 128 */ 0xAF /* '/' -> */, +/* pos 00fc: 129 */ 0xB1 /* '1' -> */, +/* pos 00fd: 130 */ 0xAE /* '.' -> */, +/* pos 00fe: 131 */ 0xB1 /* '1' -> */, +/* pos 00ff: 132 */ 0xA0 /* ' ' -> */, +/* pos 0100: 133 */ 0x00, 0x12 /* - terminal marker 18 - */, +/* pos 0102: 134 */ 0x63 /* 'c' */, 0x07, 0x00 /* (to 0x0109 state 135) */, + 0x75 /* 'u' */, 0x88, 0x00 /* (to 0x018D state 235) */, + 0x08, /* fail */ +/* pos 0109: 135 */ 0xE3 /* 'c' -> */, +/* pos 010a: 136 */ 0xE5 /* 'e' -> */, +/* pos 010b: 137 */ 0x70 /* 'p' */, 0x07, 0x00 /* (to 0x0112 state 138) */, + 0x73 /* 's' */, 0x0E, 0x00 /* (to 0x011C state 141) */, + 0x08, /* fail */ +/* pos 0112: 138 */ 0xF4 /* 't' -> */, +/* pos 0113: 139 */ 0x3A /* ':' */, 0x07, 0x00 /* (to 0x011A state 140) */, + 0x2D /* '-' */, 0x47, 0x00 /* (to 0x015D state 197) */, + 0x08, /* fail */ +/* pos 011a: 140 */ 0x00, 0x13 /* - terminal marker 19 - */, +/* pos 011c: 141 */ 0xF3 /* 's' -> */, +/* pos 011d: 142 */ 0xAD /* '-' -> */, +/* pos 011e: 143 */ 0xE3 /* 'c' -> */, +/* pos 011f: 144 */ 0xEF /* 'o' -> */, +/* pos 0120: 145 */ 0xEE /* 'n' -> */, +/* pos 0121: 146 */ 0xF4 /* 't' -> */, +/* pos 0122: 147 */ 0xF2 /* 'r' -> */, +/* pos 0123: 148 */ 0xEF /* 'o' -> */, +/* pos 0124: 149 */ 0xEC /* 'l' -> */, +/* pos 0125: 150 */ 0xAD /* '-' -> */, +/* pos 0126: 151 */ 0xF2 /* 'r' -> */, +/* pos 0127: 152 */ 0xE5 /* 'e' -> */, +/* pos 0128: 153 */ 0xF1 /* 'q' -> */, +/* pos 0129: 154 */ 0xF5 /* 'u' -> */, +/* pos 012a: 155 */ 0xE5 /* 'e' -> */, +/* pos 012b: 156 */ 0xF3 /* 's' -> */, +/* pos 012c: 157 */ 0xF4 /* 't' -> */, +/* pos 012d: 158 */ 0xAD /* '-' -> */, +/* pos 012e: 159 */ 0xE8 /* 'h' -> */, +/* pos 012f: 160 */ 0xE5 /* 'e' -> */, +/* pos 0130: 161 */ 0xE1 /* 'a' -> */, +/* pos 0131: 162 */ 0xE4 /* 'd' -> */, +/* pos 0132: 163 */ 0xE5 /* 'e' -> */, +/* pos 0133: 164 */ 0xF2 /* 'r' -> */, +/* pos 0134: 165 */ 0xF3 /* 's' -> */, +/* pos 0135: 166 */ 0xBA /* ':' -> */, +/* pos 0136: 167 */ 0x00, 0x14 /* - terminal marker 20 - */, +/* pos 0138: 168 */ 0xE6 /* 'f' -> */, +/* pos 0139: 169 */ 0xAD /* '-' -> */, +/* pos 013a: 170 */ 0x6D /* 'm' */, 0x07, 0x00 /* (to 0x0141 state 171) */, + 0x6E /* 'n' */, 0x14, 0x00 /* (to 0x0151 state 186) */, + 0x08, /* fail */ +/* pos 0141: 171 */ 0xEF /* 'o' -> */, +/* pos 0142: 172 */ 0xE4 /* 'd' -> */, +/* pos 0143: 173 */ 0xE9 /* 'i' -> */, +/* pos 0144: 174 */ 0xE6 /* 'f' -> */, +/* pos 0145: 175 */ 0xE9 /* 'i' -> */, +/* pos 0146: 176 */ 0xE5 /* 'e' -> */, +/* pos 0147: 177 */ 0xE4 /* 'd' -> */, +/* pos 0148: 178 */ 0xAD /* '-' -> */, +/* pos 0149: 179 */ 0xF3 /* 's' -> */, +/* pos 014a: 180 */ 0xE9 /* 'i' -> */, +/* pos 014b: 181 */ 0xEE /* 'n' -> */, +/* pos 014c: 182 */ 0xE3 /* 'c' -> */, +/* pos 014d: 183 */ 0xE5 /* 'e' -> */, +/* pos 014e: 184 */ 0xBA /* ':' -> */, +/* pos 014f: 185 */ 0x00, 0x15 /* - terminal marker 21 - */, +/* pos 0151: 186 */ 0xEF /* 'o' -> */, +/* pos 0152: 187 */ 0xEE /* 'n' -> */, +/* pos 0153: 188 */ 0xE5 /* 'e' -> */, +/* pos 0154: 189 */ 0xAD /* '-' -> */, +/* pos 0155: 190 */ 0xED /* 'm' -> */, +/* pos 0156: 191 */ 0xE1 /* 'a' -> */, +/* pos 0157: 192 */ 0xF4 /* 't' -> */, +/* pos 0158: 193 */ 0xE3 /* 'c' -> */, +/* pos 0159: 194 */ 0xE8 /* 'h' -> */, +/* pos 015a: 195 */ 0xBA /* ':' -> */, +/* pos 015b: 196 */ 0x00, 0x16 /* - terminal marker 22 - */, +/* pos 015d: 197 */ 0x65 /* 'e' */, 0x07, 0x00 /* (to 0x0164 state 198) */, + 0x6C /* 'l' */, 0x0E, 0x00 /* (to 0x016E state 207) */, + 0x08, /* fail */ +/* pos 0164: 198 */ 0xEE /* 'n' -> */, +/* pos 0165: 199 */ 0xE3 /* 'c' -> */, +/* pos 0166: 200 */ 0xEF /* 'o' -> */, +/* pos 0167: 201 */ 0xE4 /* 'd' -> */, +/* pos 0168: 202 */ 0xE9 /* 'i' -> */, +/* pos 0169: 203 */ 0xEE /* 'n' -> */, +/* pos 016a: 204 */ 0xE7 /* 'g' -> */, +/* pos 016b: 205 */ 0xBA /* ':' -> */, +/* pos 016c: 206 */ 0x00, 0x17 /* - terminal marker 23 - */, +/* pos 016e: 207 */ 0xE1 /* 'a' -> */, +/* pos 016f: 208 */ 0xEE /* 'n' -> */, +/* pos 0170: 209 */ 0xE7 /* 'g' -> */, +/* pos 0171: 210 */ 0xF5 /* 'u' -> */, +/* pos 0172: 211 */ 0xE1 /* 'a' -> */, +/* pos 0173: 212 */ 0xE7 /* 'g' -> */, +/* pos 0174: 213 */ 0xE5 /* 'e' -> */, +/* pos 0175: 214 */ 0xBA /* ':' -> */, +/* pos 0176: 215 */ 0x00, 0x18 /* - terminal marker 24 - */, +/* pos 0178: 216 */ 0xE1 /* 'a' -> */, +/* pos 0179: 217 */ 0xE7 /* 'g' -> */, +/* pos 017a: 218 */ 0xED /* 'm' -> */, +/* pos 017b: 219 */ 0xE1 /* 'a' -> */, +/* pos 017c: 220 */ 0xBA /* ':' -> */, +/* pos 017d: 221 */ 0x00, 0x19 /* - terminal marker 25 - */, +/* pos 017f: 222 */ 0xE3 /* 'c' -> */, +/* pos 0180: 223 */ 0xE8 /* 'h' -> */, +/* pos 0181: 224 */ 0xE5 /* 'e' -> */, +/* pos 0182: 225 */ 0xAD /* '-' -> */, +/* pos 0183: 226 */ 0xE3 /* 'c' -> */, +/* pos 0184: 227 */ 0xEF /* 'o' -> */, +/* pos 0185: 228 */ 0xEE /* 'n' -> */, +/* pos 0186: 229 */ 0xF4 /* 't' -> */, +/* pos 0187: 230 */ 0xF2 /* 'r' -> */, +/* pos 0188: 231 */ 0xEF /* 'o' -> */, +/* pos 0189: 232 */ 0xEC /* 'l' -> */, +/* pos 018a: 233 */ 0xBA /* ':' -> */, +/* pos 018b: 234 */ 0x00, 0x1A /* - terminal marker 26 - */, +/* pos 018d: 235 */ 0xF4 /* 't' -> */, +/* pos 018e: 236 */ 0xE8 /* 'h' -> */, +/* pos 018f: 237 */ 0xEF /* 'o' -> */, +/* pos 0190: 238 */ 0xF2 /* 'r' -> */, +/* pos 0191: 239 */ 0xE9 /* 'i' -> */, +/* pos 0192: 240 */ 0xFA /* 'z' -> */, +/* pos 0193: 241 */ 0xE1 /* 'a' -> */, +/* pos 0194: 242 */ 0xF4 /* 't' -> */, +/* pos 0195: 243 */ 0xE9 /* 'i' -> */, +/* pos 0196: 244 */ 0xEF /* 'o' -> */, +/* pos 0197: 245 */ 0xEE /* 'n' -> */, +/* pos 0198: 246 */ 0xBA /* ':' -> */, +/* pos 0199: 247 */ 0x00, 0x1B /* - terminal marker 27 - */, +/* pos 019b: 248 */ 0xEB /* 'k' -> */, +/* pos 019c: 249 */ 0xE9 /* 'i' -> */, +/* pos 019d: 250 */ 0xE5 /* 'e' -> */, +/* pos 019e: 251 */ 0xBA /* ':' -> */, +/* pos 019f: 252 */ 0x00, 0x1C /* - terminal marker 28 - */, +/* pos 01a1: 253 */ 0xE5 /* 'e' -> */, +/* pos 01a2: 254 */ 0xEE /* 'n' -> */, +/* pos 01a3: 255 */ 0xF4 /* 't' -> */, +/* pos 01a4: 256 */ 0xAD /* '-' -> */, +/* pos 01a5: 257 */ 0x6C /* 'l' */, 0x07, 0x00 /* (to 0x01AC state 258) */, + 0x74 /* 't' */, 0x0C, 0x00 /* (to 0x01B4 state 265) */, + 0x08, /* fail */ +/* pos 01ac: 258 */ 0xE5 /* 'e' -> */, +/* pos 01ad: 259 */ 0xEE /* 'n' -> */, +/* pos 01ae: 260 */ 0xE7 /* 'g' -> */, +/* pos 01af: 261 */ 0xF4 /* 't' -> */, +/* pos 01b0: 262 */ 0xE8 /* 'h' -> */, +/* pos 01b1: 263 */ 0xBA /* ':' -> */, +/* pos 01b2: 264 */ 0x00, 0x1D /* - terminal marker 29 - */, +/* pos 01b4: 265 */ 0xF9 /* 'y' -> */, +/* pos 01b5: 266 */ 0xF0 /* 'p' -> */, +/* pos 01b6: 267 */ 0xE5 /* 'e' -> */, +/* pos 01b7: 268 */ 0xBA /* ':' -> */, +/* pos 01b8: 269 */ 0x00, 0x1E /* - terminal marker 30 - */, +/* pos 01ba: 270 */ 0xE1 /* 'a' -> */, +/* pos 01bb: 271 */ 0xF4 /* 't' -> */, +/* pos 01bc: 272 */ 0xE5 /* 'e' -> */, +/* pos 01bd: 273 */ 0xBA /* ':' -> */, +/* pos 01be: 274 */ 0x00, 0x1F /* - terminal marker 31 - */, +/* pos 01c0: 275 */ 0x61 /* 'a' */, 0x07, 0x00 /* (to 0x01C7 state 276) */, + 0x65 /* 'e' */, 0x0A, 0x00 /* (to 0x01CD state 281) */, + 0x08, /* fail */ +/* pos 01c7: 276 */ 0xEE /* 'n' -> */, +/* pos 01c8: 277 */ 0xE7 /* 'g' -> */, +/* pos 01c9: 278 */ 0xE5 /* 'e' -> */, +/* pos 01ca: 279 */ 0xBA /* ':' -> */, +/* pos 01cb: 280 */ 0x00, 0x20 /* - terminal marker 32 - */, +/* pos 01cd: 281 */ 0xE6 /* 'f' -> */, +/* pos 01ce: 282 */ 0xE5 /* 'e' -> */, +/* pos 01cf: 283 */ 0xF2 /* 'r' -> */, +/* pos 01d0: 284 */ 0xE5 /* 'e' -> */, +/* pos 01d1: 285 */ 0xF2 /* 'r' -> */, +/* pos 01d2: 286 */ 0xBA /* ':' -> */, +/* pos 01d3: 287 */ 0x00, 0x21 /* - terminal marker 33 - */, +/* total size 469 bytes */ diff --git a/lib/libev.c b/lib/libev.c new file mode 100755 index 0000000000..04a4b5be47 --- /dev/null +++ b/lib/libev.c @@ -0,0 +1,175 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + */ + +#include "private-libwebsockets.h" + +void lws_feature_status_libev(struct lws_context_creation_info *info) +{ + if (info->options & LWS_SERVER_OPTION_LIBEV) + lwsl_notice("libev support compiled in and enabled\n"); + else + lwsl_notice("libev support compiled in but disabled\n"); +} + +static void +libwebsocket_accept_cb(struct ev_loop *loop, struct ev_io *watcher, int revents) +{ + struct libwebsocket_pollfd eventfd; + struct lws_io_watcher *lws_io = (struct lws_io_watcher *)watcher; + struct libwebsocket_context *context = lws_io->context; + + if (revents & EV_ERROR) + return; + + eventfd.fd = watcher->fd; + eventfd.revents = EV_NONE; + if (revents & EV_READ) + eventfd.revents |= LWS_POLLIN; + + if (revents & EV_WRITE) + eventfd.revents |= LWS_POLLOUT; + + libwebsocket_service_fd(context, &eventfd); +} + +LWS_VISIBLE void +libwebsocket_sigint_cb(struct ev_loop *loop, + struct ev_signal *watcher, int revents) +{ + ev_break(loop, EVBREAK_ALL); +} + +LWS_VISIBLE int +libwebsocket_initloop( + struct libwebsocket_context *context, + struct ev_loop *loop) +{ + int status = 0; + int backend; + const char * backend_name; + struct ev_io *w_accept = (ev_io *)&context->w_accept; + struct ev_signal *w_sigint = (ev_signal *)&context->w_sigint; + + if (!loop) + loop = ev_default_loop(0); + + context->io_loop = loop; + + /* + * Initialize the accept w_accept with the listening socket + * and register a callback for read operations: + */ + ev_io_init(w_accept, libwebsocket_accept_cb, + context->listen_service_fd, EV_READ); + ev_io_start(context->io_loop,w_accept); + ev_signal_init(w_sigint, libwebsocket_sigint_cb, SIGINT); + ev_signal_start(context->io_loop,w_sigint); + backend = ev_backend(loop); + + switch (backend) { + case EVBACKEND_SELECT: + backend_name = "select"; + break; + case EVBACKEND_POLL: + backend_name = "poll"; + break; + case EVBACKEND_EPOLL: + backend_name = "epoll"; + break; + case EVBACKEND_KQUEUE: + backend_name = "kqueue"; + break; + case EVBACKEND_DEVPOLL: + backend_name = "/dev/poll"; + break; + case EVBACKEND_PORT: + backend_name = "Solaris 10 \"port\""; + break; + default: + backend_name = "Unknown libev backend"; + break; + }; + + lwsl_notice(" libev backend: %s\n", backend_name); + + return status; +} + +LWS_VISIBLE void +lws_libev_accept(struct libwebsocket_context *context, + struct libwebsocket *new_wsi, int accept_fd) +{ + struct ev_io *r = &new_wsi->w_read.watcher; + struct ev_io *w = &new_wsi->w_write.watcher; + + if (!LWS_LIBEV_ENABLED(context)) + return; + + new_wsi->w_read.context = context; + new_wsi->w_write.context = context; + ev_io_init(r, libwebsocket_accept_cb, accept_fd, EV_READ); + ev_io_init(w, libwebsocket_accept_cb, accept_fd, EV_WRITE); +} + +LWS_VISIBLE void +lws_libev_io(struct libwebsocket_context *context, + struct libwebsocket *wsi, int flags) +{ + if (!LWS_LIBEV_ENABLED(context)) + return; + + if (!context->io_loop) + return; + + assert((flags & (LWS_EV_START | LWS_EV_STOP)) && + (flags & (LWS_EV_READ | LWS_EV_WRITE))); + + if (flags & LWS_EV_START) { + if (flags & LWS_EV_WRITE) + ev_io_start(context->io_loop, &wsi->w_write.watcher); + if (flags & LWS_EV_READ) + ev_io_start(context->io_loop, &wsi->w_read.watcher); + } else { + if (flags & LWS_EV_WRITE) + ev_io_stop(context->io_loop, &wsi->w_write.watcher); + if (flags & LWS_EV_READ) + ev_io_stop(context->io_loop, &wsi->w_read.watcher); + } +} + +LWS_VISIBLE int +lws_libev_init_fd_table(struct libwebsocket_context *context) +{ + if (!LWS_LIBEV_ENABLED(context)) + return 0; + + context->w_accept.context = context; + context->w_sigint.context = context; + + return 1; +} + +LWS_VISIBLE void +lws_libev_run(struct libwebsocket_context *context) +{ + if (context->io_loop && LWS_LIBEV_ENABLED(context)) + ev_run(context->io_loop, 0); +} diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 82cd99da02..ee88e2e1b6 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010 Andy Green + * Copyright (C) 2010-2014 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,44 +21,9 @@ #include "private-libwebsockets.h" -#ifdef WIN32 -#include -#include -#include -#else -#ifdef LWS_BUILTIN_GETIFADDRS -#include -#else -#include -#endif -#include -#include -#include -#include -#endif - -#ifdef LWS_OPENSSL_SUPPORT -int openssl_websocket_private_data_index; -#endif - -#ifdef __MINGW32__ -#include "../win32port/win32helpers/websock-w32.c" -#else -#ifdef __MINGW64__ -#include "../win32port/win32helpers/websock-w32.c" -#endif -#endif - -#ifndef LWS_BUILD_HASH -#define LWS_BUILD_HASH "unknown-build-hash" -#endif - -static int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; -static void lwsl_emit_stderr(int level, const char *line); +int log_level = LLL_ERR | LLL_WARN | LLL_NOTICE; static void (*lwsl_emit)(int level, const char *line) = lwsl_emit_stderr; -static const char *library_version = LWS_LIBRARY_VERSION " " LWS_BUILD_HASH; - static const char * const log_level_names[] = { "ERR", "WARN", @@ -72,173 +37,77 @@ static const char * const log_level_names[] = { "LATENCY", }; -#ifndef LWS_NO_CLIENT - extern int lws_client_socket_service( - struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd); -#endif -#ifndef LWS_NO_SERVER - extern int lws_server_socket_service( - struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd); -#endif - -/** - * lws_get_library_version: get version and git hash library built from - * - * returns a const char * to a string like "1.1 178d78c" - * representing the library version followed by the git head hash it - * was built from - */ - -LWS_VISIBLE const char * -lws_get_library_version(void) -{ - return library_version; -} - -int -insert_wsi_socket_into_fds(struct libwebsocket_context *context, - struct libwebsocket *wsi) -{ - if (context->fds_count >= context->max_fds) { - lwsl_err("Too many fds (%d)\n", context->max_fds); - return 1; - } - - if (wsi->sock > context->max_fds) { - lwsl_err("Socket fd %d is too high (%d)\n", - wsi->sock, context->max_fds); - return 1; - } - - assert(wsi); - assert(wsi->sock >= 0); - - lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", - wsi, wsi->sock, context->fds_count); - - context->lws_lookup[wsi->sock] = wsi; - wsi->position_in_fds_table = context->fds_count; - context->fds[context->fds_count].fd = wsi->sock; - context->fds[context->fds_count].events = POLLIN; - context->fds[context->fds_count++].revents = 0; - - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_ADD_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLIN); - - return 0; -} - -static int -remove_wsi_socket_from_fds(struct libwebsocket_context *context, - struct libwebsocket *wsi) -{ - int m; - - if (!--context->fds_count) - goto do_ext; - - if (wsi->sock > context->max_fds) { - lwsl_err("Socket fd %d too high (%d)\n", - wsi->sock, context->max_fds); - return 1; - } - - lwsl_info("remove_wsi_socket_from_fds: wsi=%p, sock=%d, fds pos=%d\n", - wsi, wsi->sock, wsi->position_in_fds_table); - - m = wsi->position_in_fds_table; /* replace the contents for this */ - - /* have the last guy take up the vacant slot */ - context->fds[m] = context->fds[context->fds_count]; - /* - * end guy's fds_lookup entry remains unchanged - * (still same fd pointing to same wsi) - */ - /* end guy's "position in fds table" changed */ - context->lws_lookup[context->fds[context->fds_count].fd]-> - position_in_fds_table = m; - /* deletion guy's lws_lookup entry needs nuking */ - context->lws_lookup[wsi->sock] = NULL; - /* removed wsi has no position any more */ - wsi->position_in_fds_table = -1; - -do_ext: - /* remove also from external POLL support via protocol 0 */ - if (wsi->sock) - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_DEL_POLL_FD, wsi->user_space, - (void *)(long)wsi->sock, 0); - - return 0; -} - void libwebsocket_close_and_free_session(struct libwebsocket_context *context, struct libwebsocket *wsi, enum lws_close_status reason) { - int n; + int n, m, ret; int old_state; unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + 2 + LWS_SEND_BUFFER_POST_PADDING]; -#ifndef LWS_NO_EXTENSIONS - int ret; - int m; struct lws_tokens eff_buf; - struct libwebsocket_extension *ext; -#endif if (!wsi) return; old_state = wsi->state; - if (old_state == WSI_STATE_DEAD_SOCKET) + switch (old_state) { + case WSI_STATE_DEAD_SOCKET: return; /* we tried the polite way... */ - if (old_state == WSI_STATE_AWAITING_CLOSE_ACK) + case WSI_STATE_AWAITING_CLOSE_ACK: + goto just_kill_connection; + + case WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE: + if (wsi->truncated_send_len) { + libwebsocket_callback_on_writable(context, wsi); + return; + } + lwsl_info("wsi %p completed WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); goto just_kill_connection; + default: + if (wsi->truncated_send_len) { + lwsl_info("wsi %p entering WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi); + wsi->state = WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE; + return; + } + break; + } wsi->u.ws.close_reason = reason; - if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED && wsi->u.http.fd) { - lwsl_debug("closing http fd %d\n", wsi->u.http.fd); - close(wsi->u.http.fd); - wsi->u.http.fd = 0; + if (wsi->mode == LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT || + wsi->mode == LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE) { + context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL, NULL, 0); + + free(wsi->u.hdr.ah); + goto just_kill_connection; + } + + if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) { + if (wsi->u.http.fd != LWS_INVALID_FILE) { + lwsl_debug("closing http file\n"); + compatible_file_close(wsi->u.http.fd); + wsi->u.http.fd = LWS_INVALID_FILE; + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0); + } } -#ifndef LWS_NO_EXTENSIONS /* * are his extensions okay with him closing? Eg he might be a mux * parent and just his ch1 aspect is closing? */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - if (!wsi->active_extensions[n]->callback) - continue; - - m = wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, - wsi->active_extensions_user[n], NULL, 0); - - /* - * if somebody vetoed actually closing him at this time.... - * up to the extension to track the attempted close, let's - * just bail - */ - - if (m) { - lwsl_ext("extension vetoed close\n"); - return; - } + + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_CHECK_OK_TO_REALLY_CLOSE, NULL, 0) > 0) { + lwsl_ext("extension vetoed close\n"); + return; } /* @@ -246,34 +115,25 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, * if there are problems with send, just nuke the connection */ - ret = 1; - while (ret == 1) { - - /* default to nobody has more to spill */ - + do { ret = 0; eff_buf.token = NULL; eff_buf.token_len = 0; /* show every extension the new incoming data */ - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_FLUSH_PENDING_TX, - wsi->active_extensions_user[n], &eff_buf, 0); - if (m < 0) { - lwsl_ext("Extension reports fatal error\n"); - goto just_kill_connection; - } - if (m) - /* - * at least one extension told us he has more - * to spill, so we will go around again after - */ - ret = 1; + m = lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_FLUSH_PENDING_TX, &eff_buf, 0); + if (m < 0) { + lwsl_ext("Extension reports fatal error\n"); + goto just_kill_connection; } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; /* assuming they left us something to send, send it */ @@ -283,8 +143,7 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, lwsl_debug("close: ext spill failed\n"); goto just_kill_connection; } - } -#endif + } while (ret); /* * signal we are closing, libsocket_write will @@ -359,6 +218,12 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, free(wsi->u.ws.rxflow_buffer); wsi->u.ws.rxflow_buffer = NULL; } + if (wsi->truncated_send_malloc) { + /* not going to be completed... nuke it */ + free(wsi->truncated_send_malloc); + wsi->truncated_send_malloc = NULL; + wsi->truncated_send_len = 0; + } } /* tell the user it's all over for this guy */ @@ -370,68 +235,47 @@ libwebsocket_close_and_free_session(struct libwebsocket_context *context, lwsl_debug("calling back CLOSED\n"); wsi->protocol->callback(context, wsi, LWS_CALLBACK_CLOSED, wsi->user_space, NULL, 0); - } else if ( wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED ) { + } else if (wsi->mode == LWS_CONNMODE_HTTP_SERVING_ACCEPTED) { lwsl_debug("calling back CLOSED_HTTP\n"); context->protocols[0].callback(context, wsi, LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0 ); } else lwsl_debug("not calling back closed\n"); -#ifndef LWS_NO_EXTENSIONS /* deallocate any active extension contexts */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - if (!wsi->active_extensions[n]->callback) - continue; - - wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_DESTROY, - wsi->active_extensions_user[n], NULL, 0); - + + if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_DESTROY, NULL, 0) < 0) + lwsl_warn("extension destruction failed\n"); +#ifndef LWS_NO_EXTENSIONS + for (n = 0; n < wsi->count_active_extensions; n++) free(wsi->active_extensions_user[n]); - } - +#endif /* * inform all extensions in case they tracked this guy out of band * even though not active on him specifically */ - - ext = context->extensions; - while (ext && ext->callback) { - ext->callback(context, ext, wsi, - LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, - NULL, NULL, 0); - ext++; - } -#endif + if (lws_ext_callback_for_each_extension_type(context, wsi, + LWS_EXT_CALLBACK_DESTROY_ANY_WSI_CLOSING, NULL, 0) < 0) + lwsl_warn("ext destroy wsi failed\n"); /* lwsl_info("closing fd=%d\n", wsi->sock); */ -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->ssl) { - n = SSL_get_fd(wsi->ssl); - SSL_shutdown(wsi->ssl); - compatible_close(n); - SSL_free(wsi->ssl); - } else { -#endif - if (wsi->sock) { - n = shutdown(wsi->sock, SHUT_RDWR); - if (n) - lwsl_debug("closing: shutdown returned %d\n", - errno); - - n = compatible_close(wsi->sock); - if (n) - lwsl_debug("closing: close returned %d\n", - errno); - } -#ifdef LWS_OPENSSL_SUPPORT + if (!lws_ssl_close(wsi) && wsi->sock >= 0) { + n = shutdown(wsi->sock, SHUT_RDWR); + if (n) + lwsl_debug("closing: shutdown ret %d\n", LWS_ERRNO); + + n = compatible_close(wsi->sock); + if (n) + lwsl_debug("closing: close ret %d\n", LWS_ERRNO); } -#endif + + /* outermost destroy notification for wsi (user_space still intact) */ + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_WSI_DESTROY, wsi->user_space, NULL, 0); + if (wsi->protocol && wsi->protocol->per_session_data_size && - wsi->user_space) /* user code may own */ + wsi->user_space && !wsi->user_space_externally_allocated) free(wsi->user_space); free(wsi); @@ -459,66 +303,95 @@ libwebsockets_get_peer_addresses(struct libwebsocket_context *context, char *rip, int rip_len) { socklen_t len; - struct sockaddr_in sin; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 sin6; +#endif + struct sockaddr_in sin4; struct hostent *host; struct hostent *host1; char ip[128]; unsigned char *p; int n; - int ret = -1; #ifdef AF_LOCAL struct sockaddr_un *un; #endif + int ret = -1; rip[0] = '\0'; name[0] = '\0'; lws_latency_pre(context, wsi); - len = sizeof(sin); - if (getpeername(fd, (struct sockaddr *) &sin, &len) < 0) { - perror("getpeername"); - goto bail; - } +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { - host = gethostbyaddr((char *) &sin.sin_addr, sizeof(sin.sin_addr), - AF_INET); - if (host == NULL) { - perror("gethostbyaddr"); - goto bail; - } + len = sizeof(sin6); + if (getpeername(fd, (struct sockaddr *) &sin6, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } - strncpy(name, host->h_name, name_len); - name[name_len - 1] = '\0'; - - host1 = gethostbyname(host->h_name); - if (host1 == NULL) - goto bail; - p = (unsigned char *)host1; - n = 0; - while (p != NULL) { - p = (unsigned char *)host1->h_addr_list[n++]; - if (p == NULL) - continue; - if ((host1->h_addrtype != AF_INET) + if (!lws_plat_inet_ntop(AF_INET6, &sin6.sin6_addr, rip, rip_len)) { + lwsl_err("inet_ntop", strerror(LWS_ERRNO)); + goto bail; + } + + // Strip off the IPv4 to IPv6 header if one exists + if (strncmp(rip, "::ffff:", 7) == 0) + memmove(rip, rip + 7, strlen(rip) - 6); + + getnameinfo((struct sockaddr *)&sin6, + sizeof(struct sockaddr_in6), name, + name_len, NULL, 0, 0); + + } else +#endif + { + len = sizeof(sin4); + if (getpeername(fd, (struct sockaddr *) &sin4, &len) < 0) { + lwsl_warn("getpeername: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + host = gethostbyaddr((char *) &sin4.sin_addr, + sizeof(sin4.sin_addr), AF_INET); + if (host == NULL) { + lwsl_warn("gethostbyaddr: %s\n", strerror(LWS_ERRNO)); + goto bail; + } + + strncpy(name, host->h_name, name_len); + name[name_len - 1] = '\0'; + + host1 = gethostbyname(host->h_name); + if (host1 == NULL) + goto bail; + p = (unsigned char *)host1; + n = 0; + while (p != NULL) { + p = (unsigned char *)host1->h_addr_list[n++]; + if (p == NULL) + continue; + if ((host1->h_addrtype != AF_INET) #ifdef AF_LOCAL - && (host1->h_addrtype != AF_LOCAL) + && (host1->h_addrtype != AF_LOCAL) #endif - ) - continue; + ) + continue; - if (host1->h_addrtype == AF_INET) - sprintf(ip, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]); + if (host1->h_addrtype == AF_INET) + sprintf(ip, "%u.%u.%u.%u", + p[0], p[1], p[2], p[3]); #ifdef AF_LOCAL - else { - un = (struct sockaddr_un *)p; - strncpy(ip, un->sun_path, sizeof(ip) - 1); - ip[sizeof(ip) - 1] = '\0'; - } + else { + un = (struct sockaddr_un *)p; + strncpy(ip, un->sun_path, sizeof(ip) - 1); + ip[sizeof(ip) - 1] = '\0'; + } #endif - p = NULL; - strncpy(rip, ip, rip_len); - rip[rip_len - 1] = '\0'; + p = NULL; + strncpy(rip, ip, rip_len); + rip[rip_len - 1] = '\0'; + } } ret = 0; @@ -526,1053 +399,142 @@ libwebsockets_get_peer_addresses(struct libwebsocket_context *context, lws_latency(context, wsi, "libwebsockets_get_peer_addresses", ret, 1); } -LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context, - void *buf, int len) -{ - int n; - char *p = (char *)buf; - -#ifdef WIN32 - for (n = 0; n < len; n++) - p[n] = (unsigned char)rand(); -#else - n = read(context->fd_random, p, len); -#endif - return n; -} -int lws_set_socket_options(struct libwebsocket_context *context, int fd) +/** + * libwebsocket_context_user() - get the user data associated with the context + * @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_EXTERN void * +libwebsocket_context_user(struct libwebsocket_context *context) { - int optval = 1; - socklen_t optlen = sizeof(optval); -#ifdef WIN32 - unsigned long optl = 0; -#endif -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) - struct protoent *tcp_proto; -#endif + return context->user_space; +} - if (context->ka_time) { - /* enable keepalive on this socket */ - optval = 1; - if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, - (const void *)&optval, optlen) < 0) - return 1; -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__CYGWIN__) - - /* - * didn't find a way to set these per-socket, need to - * tune kernel systemwide values - */ -#elif WIN32 - { - DWORD dwBytesRet; - struct tcp_keepalive alive; - alive.onoff = TRUE; - alive.keepalivetime = context->ka_time; - alive.keepaliveinterval = context->ka_interval; - - if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), - NULL, 0, &dwBytesRet, NULL, NULL)) - return 1; - } -#else - /* set the keepalive conditions we want on it too */ - optval = context->ka_time; - if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE, - (const void *)&optval, optlen) < 0) - return 1; +/** + * libwebsocket_callback_all_protocol() - Callback all connections using + * the given protocol with the given reason + * + * @protocol: Protocol whose connections will get callbacks + * @reason: Callback reason index + */ - optval = context->ka_interval; - if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL, - (const void *)&optval, optlen) < 0) - return 1; +LWS_VISIBLE int +libwebsocket_callback_all_protocol( + const struct libwebsocket_protocols *protocol, int reason) +{ + struct libwebsocket_context *context = protocol->owning_server; + int n; + struct libwebsocket *wsi; - optval = context->ka_probes; - if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT, - (const void *)&optval, optlen) < 0) - return 1; -#endif + for (n = 0; n < context->fds_count; n++) { + wsi = context->lws_lookup[context->fds[n].fd]; + if (!wsi) + continue; + if (wsi->protocol == protocol) + protocol->callback(context, wsi, + reason, wsi->user_space, NULL, 0); } - /* Disable Nagle */ - optval = 1; -#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) - setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen); -#else - tcp_proto = getprotobyname("TCP"); - setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen); -#endif - - /* We are nonblocking... */ -#ifdef WIN32 - ioctlsocket(fd, FIONBIO, &optl); -#else - fcntl(fd, F_SETFL, O_NONBLOCK); -#endif - return 0; } -LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi) +/** + * libwebsocket_set_timeout() - marks the wsi as subject to a timeout + * + * You will not need this unless you are doing something special + * + * @wsi: Websocket connection instance + * @reason: timeout reason + * @secs: how many seconds + */ + +LWS_VISIBLE void +libwebsocket_set_timeout(struct libwebsocket *wsi, + enum pending_timeout reason, int secs) { - struct pollfd fds; + time_t now; - fds.fd = wsi->sock; - fds.events = POLLOUT; - fds.revents = 0; + time(&now); - if (poll(&fds, 1, 0) != 1) - return 1; + wsi->pending_timeout_limit = now + secs; + wsi->pending_timeout = reason; +} - if ((fds.revents & POLLOUT) == 0) - return 1; - /* okay to send another packet without blocking */ +/** + * libwebsocket_get_socket_fd() - returns the socket file descriptor + * + * You will not need this unless you are doing something special + * + * @wsi: Websocket connection instance + */ - return 0; +LWS_VISIBLE int +libwebsocket_get_socket_fd(struct libwebsocket *wsi) +{ + return wsi->sock; } -int -lws_handle_POLLOUT_event(struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd) +#ifdef LWS_LATENCY +void +lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi, + const char *action, int ret, int completed) { - int n; - -#ifndef LWS_NO_EXTENSIONS - struct lws_tokens eff_buf; - int ret; - int m; - int handled = 0; + unsigned long long u; + char buf[256]; - for (n = 0; n < wsi->count_active_extensions; n++) { - if (!wsi->active_extensions[n]->callback) - continue; + u = time_in_microseconds(); - m = wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_IS_WRITEABLE, - wsi->active_extensions_user[n], NULL, 0); - if (m > handled) - handled = m; + if (!action) { + wsi->latency_start = u; + if (!wsi->action_start) + wsi->action_start = u; + return; } + if (completed) { + if (wsi->action_start == wsi->latency_start) + sprintf(buf, + "Completion first try lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, + (void *)wsi, ret, action); + else + sprintf(buf, + "Completion %lluus: lat %lluus: %p: ret %d: %s\n", + u - wsi->action_start, + u - wsi->latency_start, + (void *)wsi, ret, action); + wsi->action_start = 0; + } else + sprintf(buf, "lat %lluus: %p: ret %d: %s\n", + u - wsi->latency_start, (void *)wsi, ret, action); - if (handled == 1) - goto notify_action; - - if (!wsi->extension_data_pending || handled == 2) - goto user_service; + if (u - wsi->latency_start > context->worst_latency) { + context->worst_latency = u - wsi->latency_start; + strcpy(context->worst_latency_info, buf); + } + lwsl_latency("%s", buf); +} +#endif - /* - * check in on the active extensions, see if they - * had pending stuff to spill... they need to get the - * first look-in otherwise sequence will be disordered - * - * NULL, zero-length eff_buf means just spill pending - */ - ret = 1; - while (ret == 1) { - /* default to nobody has more to spill */ - - ret = 0; - eff_buf.token = NULL; - eff_buf.token_len = 0; - - /* give every extension a chance to spill */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PACKET_TX_PRESEND, - wsi->active_extensions_user[n], &eff_buf, 0); - if (m < 0) { - lwsl_err("ext reports fatal error\n"); - return -1; - } - if (m) - /* - * at least one extension told us he has more - * to spill, so we will go around again after - */ - ret = 1; - } - - /* assuming they gave us something to send, send it */ - - if (eff_buf.token_len) { - n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, - eff_buf.token_len); - if (n < 0) - return -1; - /* - * Keep amount spilled small to minimize chance of this - */ - if (n != eff_buf.token_len) { - lwsl_err("Unable to spill ext %d vs %s\n", - eff_buf.token_len, n); - return -1; - } - } else - continue; - - /* no extension has more to spill */ - - if (!ret) - continue; - - /* - * There's more to spill from an extension, but we just sent - * something... did that leave the pipe choked? - */ - - if (!lws_send_pipe_choked(wsi)) - /* no we could add more */ - continue; - - lwsl_info("choked in POLLOUT service\n"); - - /* - * Yes, he's choked. Leave the POLLOUT masked on so we will - * come back here when he is unchoked. Don't call the user - * callback to enforce ordering of spilling, he'll get called - * when we come back here and there's nothing more to spill. - */ - - return 0; - } - - wsi->extension_data_pending = 0; - -user_service: -#endif - /* one shot */ - - if (pollfd) { - pollfd->events &= ~POLLOUT; - - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLEAR_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLOUT); - } -#ifndef LWS_NO_EXTENSIONS -notify_action: -#endif - - if (wsi->mode == LWS_CONNMODE_WS_CLIENT) - n = LWS_CALLBACK_CLIENT_WRITEABLE; - else - n = LWS_CALLBACK_SERVER_WRITEABLE; - - return user_callback_handle_rxflow(wsi->protocol->callback, context, - wsi, (enum libwebsocket_callback_reasons) n, - wsi->user_space, NULL, 0); -} - - - -int -libwebsocket_service_timeout_check(struct libwebsocket_context *context, - struct libwebsocket *wsi, unsigned int sec) -{ -#ifndef LWS_NO_EXTENSIONS - int n; - - /* - * if extensions want in on it (eg, we are a mux parent) - * give them a chance to service child timeouts - */ - - for (n = 0; n < wsi->count_active_extensions; n++) - wsi->active_extensions[n]->callback( - context, wsi->active_extensions[n], - wsi, LWS_EXT_CALLBACK_1HZ, - wsi->active_extensions_user[n], NULL, sec); - -#endif - if (!wsi->pending_timeout) - return 0; - - /* - * if we went beyond the allowed time, kill the - * connection - */ - - if (sec > wsi->pending_timeout_limit) { - lwsl_info("TIMEDOUT WAITING\n"); - libwebsocket_close_and_free_session(context, - wsi, LWS_CLOSE_STATUS_NOSTATUS); - return 1; - } - - return 0; -} - -/** - * libwebsocket_service_fd() - Service polled socket with something waiting - * @context: Websocket context - * @pollfd: The pollfd entry describing the socket fd and which events - * happened. - * - * This function takes a pollfd that has POLLIN or POLLOUT activity and - * services it according to the state of the associated - * struct libwebsocket. - * - * The one call deals with all "service" that might happen on a socket - * including listen accepts, http files as well as websocket protocol. - * - * If a pollfd says it has something, you can just pass it to - * libwebsocket_serice_fd() whether it is a socket handled by lws or not. - * If it sees it is a lws socket, the traffic will be handled and - * pollfd->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. - */ - -LWS_VISIBLE int -libwebsocket_service_fd(struct libwebsocket_context *context, - struct pollfd *pollfd) -{ - struct libwebsocket *wsi; - int n; - int m; - int listen_socket_fds_index = 0; - struct timeval tv; - int timed_out = 0; - int our_fd = 0; - char draining_flow = 0; - -#ifndef LWS_NO_EXTENSIONS - int more = 1; -#endif - struct lws_tokens eff_buf; - - if (context->listen_service_fd) - listen_socket_fds_index = context->lws_lookup[ - context->listen_service_fd]->position_in_fds_table; - - /* - * you can call us 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. - */ - - gettimeofday(&tv, NULL); - - if (context->last_timeout_check_s != tv.tv_sec) { - context->last_timeout_check_s = tv.tv_sec; - - #ifndef WIN32 - /* if our parent went down, don't linger around */ - if (context->started_with_parent && - kill(context->started_with_parent, 0) < 0) - kill(getpid(), SIGTERM); - #endif - - /* global timeout check once per second */ - - if (pollfd) - our_fd = pollfd->fd; - - for (n = 0; n < context->fds_count; n++) { - m = context->fds[n].fd; - wsi = context->lws_lookup[m]; - if (!wsi) - continue; - - if (libwebsocket_service_timeout_check(context, wsi, - tv.tv_sec)) - /* he did time out... */ - if (m == our_fd) { - /* it was the guy we came to service! */ - timed_out = 1; - /* mark as handled */ - pollfd->revents = 0; - } - } - } - - /* the socket we came to service timed out, nothing to do */ - if (timed_out) - return 0; - - /* just here for timeout management? */ - - if (pollfd == NULL) - return 0; - - /* no, here to service a socket descriptor */ - - wsi = context->lws_lookup[pollfd->fd]; - if (wsi == NULL) - /* not lws connection ... leave revents alone and return */ - return 0; - - /* - * so that caller can tell we handled, past here we need to - * zero down pollfd->revents after handling - */ - - /* - * deal with listen service piggybacking - * every listen_service_modulo services of other fds, we - * sneak one in to service the listen socket if there's anything waiting - * - * To handle connection storms, as found in ab, if we previously saw a - * pending connection here, it causes us to check again next time. - */ - - if (context->listen_service_fd && pollfd != - &context->fds[listen_socket_fds_index]) { - context->listen_service_count++; - if (context->listen_service_extraseen || - context->listen_service_count == - context->listen_service_modulo) { - context->listen_service_count = 0; - m = 1; - if (context->listen_service_extraseen > 5) - m = 2; - while (m--) { - /* - * even with extpoll, we prepared this - * internal fds for listen - */ - n = poll(&context->fds[listen_socket_fds_index], - 1, 0); - if (n > 0) { /* there's a conn waiting for us */ - libwebsocket_service_fd(context, - &context-> - fds[listen_socket_fds_index]); - context->listen_service_extraseen++; - } else { - if (context->listen_service_extraseen) - context-> - listen_service_extraseen--; - break; - } - } - } - - } - - /* okay, what we came here to do... */ - - switch (wsi->mode) { - -#ifndef LWS_NO_SERVER - case LWS_CONNMODE_HTTP_SERVING: - case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: - case LWS_CONNMODE_SERVER_LISTENER: - case LWS_CONNMODE_SSL_ACK_PENDING: - n = lws_server_socket_service(context, wsi, pollfd); - goto handled; -#endif - - case LWS_CONNMODE_WS_SERVING: - case LWS_CONNMODE_WS_CLIENT: - - /* handle session socket closed */ - - if ((!(pollfd->revents & POLLIN)) && - (pollfd->revents & (POLLERR | POLLHUP))) { - - lwsl_debug("Session Socket %p (fd=%d) dead\n", - (void *)wsi, pollfd->fd); - - goto close_and_handled; - } - - /* the guy requested a callback when it was OK to write */ - - if ((pollfd->revents & POLLOUT) && - wsi->state == WSI_STATE_ESTABLISHED && - lws_handle_POLLOUT_event(context, wsi, pollfd) < 0) { - lwsl_info("libwebsocket_service_fd: closing\n"); - goto close_and_handled; - } - - - if (wsi->u.ws.rxflow_buffer && - (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { - lwsl_info("draining rxflow\n"); - /* well, drain it */ - eff_buf.token = (char *)wsi->u.ws.rxflow_buffer + - wsi->u.ws.rxflow_pos; - eff_buf.token_len = wsi->u.ws.rxflow_len - - wsi->u.ws.rxflow_pos; - draining_flow = 1; - goto drain; - } - - /* any incoming data ready? */ - - if (!(pollfd->revents & POLLIN)) - break; - -#ifdef LWS_OPENSSL_SUPPORT -read_pending: - if (wsi->ssl) { - eff_buf.token_len = SSL_read(wsi->ssl, - context->service_buffer, - sizeof(context->service_buffer)); - if (!eff_buf.token_len) { - n = SSL_get_error(wsi->ssl, eff_buf.token_len); - lwsl_err("SSL_read returned 0 with reason %s\n", - ERR_error_string(n, - (char *)context->service_buffer)); - } - } else -#endif - eff_buf.token_len = recv(pollfd->fd, - context->service_buffer, - sizeof(context->service_buffer), 0); - - if (eff_buf.token_len < 0) { - lwsl_debug("service_fd read ret = %d, errno = %d\n", - eff_buf.token_len, errno); - if (errno != EINTR && errno != EAGAIN) - goto close_and_handled; - n = 0; - goto handled; - } - if (!eff_buf.token_len) { - lwsl_info("service_fd: closing due to 0 length read\n"); - goto close_and_handled; - } - - /* - * give any active extensions a chance to munge the buffer - * before parse. We pass in a pointer to an lws_tokens struct - * prepared with the default buffer and content length that's in - * there. Rather than rewrite the default buffer, extensions - * that expect to grow the buffer can adapt .token to - * point to their own per-connection buffer in the extension - * user allocation. By default with no extensions or no - * extension callback handling, just the normal input buffer is - * used then so it is efficient. - */ - - eff_buf.token = (char *)context->service_buffer; -drain: -#ifndef LWS_NO_EXTENSIONS - more = 1; - while (more) { - - more = 0; - - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, - wsi->active_extensions_user[n], - &eff_buf, 0); - if (m < 0) { - lwsl_ext( - "Extension reports fatal error\n"); - goto close_and_handled; - } - if (m) - more = 1; - } -#endif - /* service incoming data */ - - if (eff_buf.token_len) { - n = libwebsocket_read(context, wsi, - (unsigned char *)eff_buf.token, - eff_buf.token_len); - if (n < 0) { - /* we closed wsi */ - n = 0; - goto handled; - } - } -#ifndef LWS_NO_EXTENSIONS - eff_buf.token = NULL; - eff_buf.token_len = 0; - } -#endif - if (draining_flow && wsi->u.ws.rxflow_buffer && - wsi->u.ws.rxflow_pos == wsi->u.ws.rxflow_len) { - lwsl_info("flow buffer: drained\n"); - free(wsi->u.ws.rxflow_buffer); - wsi->u.ws.rxflow_buffer = NULL; - /* having drained the rxflow buffer, can rearm POLLIN */ - _libwebsocket_rx_flow_control(wsi); - } - -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->ssl && SSL_pending(wsi->ssl)) - goto read_pending; -#endif - break; - - default: -#ifdef LWS_NO_CLIENT - break; -#else - n = lws_client_socket_service(context, wsi, pollfd); - goto handled; -#endif - } - - n = 0; - goto handled; - -close_and_handled: - libwebsocket_close_and_free_session(context, wsi, - LWS_CLOSE_STATUS_NOSTATUS); - n = 1; - -handled: - pollfd->revents = 0; - return n; -} - - -/** - * libwebsocket_context_destroy() - Destroy the websocket context - * @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 void -libwebsocket_context_destroy(struct libwebsocket_context *context) -{ -#ifndef LWS_NO_EXTENSIONS - int n; - int m; - struct libwebsocket_extension *ext; - struct libwebsocket_protocols *protocol = context->protocols; - -#ifdef LWS_LATENCY - if (context->worst_latency_info[0]) - lwsl_notice("Worst latency: %s\n", context->worst_latency_info); -#endif - - for (n = 0; n < context->fds_count; n++) { - struct libwebsocket *wsi = - context->lws_lookup[context->fds[n].fd]; - libwebsocket_close_and_free_session(context, - wsi, LWS_CLOSE_STATUS_NOSTATUS /* no protocol close */); - n--; - } - - /* - * give all extensions a chance to clean up any per-context - * allocations they might have made - */ - - ext = context->extensions; - m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_DESTRUCT; - if (context->listen_port) - m = LWS_EXT_CALLBACK_SERVER_CONTEXT_DESTRUCT; - while (ext && ext->callback) { - ext->callback(context, ext, NULL, - (enum libwebsocket_extension_callback_reasons)m, - NULL, NULL, 0); - ext++; - } - - /* - * inform all the protocols that they are done and will have no more - * callbacks - */ - - while (protocol->callback) { - protocol->callback(context, NULL, LWS_CALLBACK_PROTOCOL_DESTROY, - NULL, NULL, 0); - protocol++; - } - -#endif - -#ifdef WIN32 -#else - close(context->fd_random); -#endif - -#ifdef LWS_OPENSSL_SUPPORT - if (context->ssl_ctx) - SSL_CTX_free(context->ssl_ctx); - if (context->ssl_client_ctx) - SSL_CTX_free(context->ssl_client_ctx); - - ERR_remove_state(0); - ERR_free_strings(); - EVP_cleanup(); - CRYPTO_cleanup_all_ex_data(); -#endif - - if (context->fds) - free(context->fds); - if (context->lws_lookup) - free(context->lws_lookup); - - free(context); - -#ifdef WIN32 - WSACleanup(); -#endif -} - -/** - * libwebsocket_context_user() - get the user data associated with the context - * @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_EXTERN void * -libwebsocket_context_user(struct libwebsocket_context *context) -{ - return context->user_space; -} - -/** - * libwebsocket_service() - Service any pending websocket activity - * @context: Websocket context - * @timeout_ms: Timeout for poll; 0 means return immediately if nothing needed - * service otherwise block and service immediately, returning - * after the timeout if nothing needed service. - * - * This function deals with any pending websocket traffic, for three - * kinds of event. It handles these events on both server and client - * types of connection the same. - * - * 1) Accept new connections to our context's server - * - * 2) Call the receive callback for incoming frame data received by - * server or client connections. - * - * You need to call this service function periodically to all the above - * functions to happen; if your application is single-threaded you can - * just call it in your main event loop. - * - * Alternatively you can fork a new process that asynchronously handles - * calling this service in a loop. In that case you are happy if this - * call blocks your thread until it needs to take care of something and - * would call it with a large nonzero timeout. Your loop then takes no - * CPU while there is nothing happening. - * - * If you are calling it in a single-threaded app, you don't want it to - * wait around blocking other things in your loop from happening, so you - * would call it with a timeout_ms of 0, so it returns immediately if - * nothing is pending, or as soon as it services whatever was pending. - */ - -LWS_VISIBLE int -libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) -{ - int n; - int m; - - /* stay dead once we are dead */ - - if (context == NULL) - return 1; - - /* wait for something to need service */ - - n = poll(context->fds, context->fds_count, timeout_ms); - if (n == 0) /* poll timeout */ - return 0; - - if (n < 0) - return -1; - - /* any socket with events to service? */ - - for (n = 0; n < context->fds_count; n++) { - if (!context->fds[n].revents) - continue; - m = libwebsocket_service_fd(context, &context->fds[n]); - if (m < 0) - return -1; - /* if something closed, retry this slot */ - if (m) - n--; - } - - return 0; -} - -#ifndef LWS_NO_EXTENSIONS -int -lws_any_extension_handled(struct libwebsocket_context *context, - struct libwebsocket *wsi, - enum libwebsocket_extension_callback_reasons r, - void *v, size_t len) -{ - int n; - int handled = 0; - - /* maybe an extension will take care of it for us */ - - for (n = 0; n < wsi->count_active_extensions && !handled; n++) { - if (!wsi->active_extensions[n]->callback) - continue; - - handled |= wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - r, wsi->active_extensions_user[n], v, len); - } - - return handled; -} - - -void * -lws_get_extension_user_matching_ext(struct libwebsocket *wsi, - struct libwebsocket_extension *ext) -{ - int n = 0; - - if (wsi == NULL) - return NULL; - - while (n < wsi->count_active_extensions) { - if (wsi->active_extensions[n] != ext) { - n++; - continue; - } - return wsi->active_extensions_user[n]; - } - - return NULL; -} -#endif - -/** - * libwebsocket_callback_on_writable() - Request a callback when this socket - * becomes able to be written to without - * blocking - * - * @context: libwebsockets context - * @wsi: Websocket connection instance to get callback for - */ - -LWS_VISIBLE int -libwebsocket_callback_on_writable(struct libwebsocket_context *context, - struct libwebsocket *wsi) -{ -#ifndef LWS_NO_EXTENSIONS - int n; - int handled = 0; - - /* maybe an extension will take care of it for us */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - if (!wsi->active_extensions[n]->callback) - continue; - - handled |= wsi->active_extensions[n]->callback(context, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, - wsi->active_extensions_user[n], NULL, 0); - } - - if (handled) - return 1; -#endif - if (wsi->position_in_fds_table < 0) { - lwsl_err("libwebsocket_callback_on_writable: failed to find socket %d\n", - wsi->sock); - return -1; - } - - context->fds[wsi->position_in_fds_table].events |= POLLOUT; - - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_SET_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLOUT); - - return 1; -} - -/** - * libwebsocket_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. - * - * @protocol: Protocol whose connections will get callbacks - */ - -LWS_VISIBLE int -libwebsocket_callback_on_writable_all_protocol( - const struct libwebsocket_protocols *protocol) -{ - struct libwebsocket_context *context = protocol->owning_server; - int n; - struct libwebsocket *wsi; - - for (n = 0; n < context->fds_count; n++) { - wsi = context->lws_lookup[context->fds[n].fd]; - if (!wsi) - continue; - if (wsi->protocol == protocol) - libwebsocket_callback_on_writable(context, wsi); - } - - return 0; -} - -/** - * libwebsocket_set_timeout() - marks the wsi as subject to a timeout - * - * You will not need this unless you are doing something special - * - * @wsi: Websocket connection instance - * @reason: timeout reason - * @secs: how many seconds - */ - -void -libwebsocket_set_timeout(struct libwebsocket *wsi, - enum pending_timeout reason, int secs) -{ - struct timeval tv; - - gettimeofday(&tv, NULL); - - wsi->pending_timeout_limit = tv.tv_sec + secs; - wsi->pending_timeout = reason; -} - - -/** - * libwebsocket_get_socket_fd() - returns the socket file descriptor - * - * You will not need this unless you are doing something special - * - * @wsi: Websocket connection instance - */ - -LWS_VISIBLE int -libwebsocket_get_socket_fd(struct libwebsocket *wsi) -{ - return wsi->sock; -} - -#ifdef LWS_LATENCY -void -lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi, - const char *action, int ret, int completed) -{ - struct timeval tv; - unsigned long u; - char buf[256]; - - gettimeofday(&tv, NULL); - - u = (tv.tv_sec * 1000000) + tv.tv_usec; - - if (action) { - if (completed) { - if (wsi->action_start == wsi->latency_start) - sprintf(buf, - "Completion first try lat %luus: %p: ret %d: %s\n", - u - wsi->latency_start, - (void *)wsi, ret, action); - else - sprintf(buf, - "Completion %luus: lat %luus: %p: ret %d: %s\n", - u - wsi->action_start, - u - wsi->latency_start, - (void *)wsi, ret, action); - wsi->action_start = 0; - } else - sprintf(buf, "lat %luus: %p: ret %d: %s\n", - u - wsi->latency_start, - (void *)wsi, ret, action); - if (u - wsi->latency_start > context->worst_latency) { - context->worst_latency = u - wsi->latency_start; - strcpy(context->worst_latency_info, buf); - } - lwsl_latency("%s", buf); - } else { - wsi->latency_start = u; - if (!wsi->action_start) - wsi->action_start = u; - } -} -#endif - -#ifdef LWS_NO_SERVER -int -_libwebsocket_rx_flow_control(struct libwebsocket *wsi) -{ - return 0; -} -#else -int -_libwebsocket_rx_flow_control(struct libwebsocket *wsi) -{ - struct libwebsocket_context *context = wsi->protocol->owning_server; - - /* there is no pending change */ - if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) - return 0; - - /* stuff is still buffered, not ready to really accept new input */ - if (wsi->u.ws.rxflow_buffer) { - /* get ourselves called back to deal with stashed buffer */ - libwebsocket_callback_on_writable(context, wsi); - return 0; - } - - /* pending is cleared, we can change rxflow state */ - - wsi->u.ws.rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; - - lwsl_info("rxflow: wsi %p change_to %d\n", wsi, - wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW); - - /* adjust the pollfd for this wsi */ - - if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW) - context->fds[wsi->position_in_fds_table].events |= POLLIN; - else - context->fds[wsi->position_in_fds_table].events &= ~POLLIN; - - if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW) - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_SET_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLIN); - else - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLEAR_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLIN); - - return 1; -} -#endif - -/** - * libwebsocket_rx_flow_control() - Enable and disable socket servicing for - * receieved packets. - * - * If the output side of a server process becomes choked, this allows flow - * control for the input side. - * - * @wsi: Websocket connection instance to get callback for - * @enable: 0 = disable read servicing for this connection, 1 = enable - */ +/** + * libwebsocket_rx_flow_control() - Enable and disable socket servicing for + * receieved packets. + * + * If the output side of a server process becomes choked, this allows flow + * control for the input side. + * + * @wsi: Websocket connection instance to get callback for + * @enable: 0 = disable read servicing for this connection, 1 = enable + */ LWS_VISIBLE int libwebsocket_rx_flow_control(struct libwebsocket *wsi, int enable) @@ -1623,52 +585,12 @@ libwebsocket_rx_flow_allow_all_protocol( * * @context: Websocket context */ - - LWS_VISIBLE extern const char * libwebsocket_canonical_hostname(struct libwebsocket_context *context) { return (const char *)context->canonical_hostname; } - -static void sigpipe_handler(int x) -{ -} - -#ifdef LWS_OPENSSL_SUPPORT -static int -OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - - SSL *ssl; - int n; - struct libwebsocket_context *context; - - ssl = X509_STORE_CTX_get_ex_data(x509_ctx, - SSL_get_ex_data_X509_STORE_CTX_idx()); - - /* - * !!! nasty openssl requires the index to come as a library-scope - * static - */ - context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); - - n = context->protocols[0].callback(NULL, NULL, - LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, - x509_ctx, ssl, preverify_ok); - - /* convert return code from 0 = OK to 1 = OK */ - - if (!n) - n = 1; - else - n = 0; - - return n; -} -#endif - int user_callback_handle_rxflow(callback_function callback_function, struct libwebsocket_context *context, struct libwebsocket *wsi, @@ -1686,560 +608,49 @@ int user_callback_handle_rxflow(callback_function callback_function, /** - * libwebsocket_create_context() - Create the websocket handler - * @info: pointer to struct with parameters - * - * This function creates the listening socket (if serving) and takes care - * of all initialization in one step. - * - * After initialization, it returns a struct libwebsocket_context * that - * represents this server. After calling, user code needs to take care - * of calling libwebsocket_service() with the context pointer to get the - * server's sockets serviced. This can be done in the same process context - * or a forked process, or another thread, + * libwebsocket_set_proxy() - Setups proxy to libwebsocket_context. + * @context: pointer to struct libwebsocket_context you want set proxy to + * @proxy: pointer to c string containing proxy in format address:port * - * 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. + * Returns 0 if proxy string was parsed and proxy was setup. + * Returns -1 if @proxy is NULL or has incorrect format. * - * 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 callack activity. + * This is only required if your OS does not provide the http_proxy + * enviroment variable (eg, OSX) * - * 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. + * IMPORTANT! You should call this function right after creation of the + * libwebsocket_context and before call to connect. If you call this + * function after connect behavior is undefined. + * This function will override proxy settings made on libwebsocket_context + * creation with genenv() call. */ -LWS_VISIBLE struct libwebsocket_context * -libwebsocket_create_context(struct lws_context_creation_info *info) +LWS_VISIBLE int +libwebsocket_set_proxy(struct libwebsocket_context *context, const char *proxy) { - struct libwebsocket_context *context = NULL; char *p; - int n; -#ifndef LWS_NO_SERVER - int opt = 1; - struct libwebsocket *wsi; - struct sockaddr_in serv_addr; -#endif -#ifndef LWS_NO_EXTENSIONS - int m; - struct libwebsocket_extension *ext; -#endif - -#ifdef LWS_OPENSSL_SUPPORT - SSL_METHOD *method; -#endif - -#ifndef LWS_NO_DAEMONIZE - int pid_daemon = get_daemonize_pid(); -#endif - - lwsl_notice("Initial logging level %d\n", log_level); - lwsl_notice("Library version: %s\n", library_version); - lwsl_info(" LWS_MAX_HEADER_LEN: %u\n", LWS_MAX_HEADER_LEN); - lwsl_info(" LWS_MAX_PROTOCOLS: %u\n", LWS_MAX_PROTOCOLS); -#ifndef LWS_NO_EXTENSIONS - lwsl_info(" LWS_MAX_EXTENSIONS_ACTIVE: %u\n", - LWS_MAX_EXTENSIONS_ACTIVE); -#else - lwsl_notice(" Configured without extension support\n"); -#endif - lwsl_info(" SPEC_LATEST_SUPPORTED: %u\n", SPEC_LATEST_SUPPORTED); - lwsl_info(" AWAITING_TIMEOUT: %u\n", AWAITING_TIMEOUT); - if (info->ssl_cipher_list) - lwsl_info(" SSL ciphers: '%s'\n", info->ssl_cipher_list); - lwsl_info(" SYSTEM_RANDOM_FILEPATH: '%s'\n", SYSTEM_RANDOM_FILEPATH); - lwsl_info(" LWS_MAX_ZLIB_CONN_BUFFER: %u\n", LWS_MAX_ZLIB_CONN_BUFFER); - -#ifdef _WIN32 - { - WORD wVersionRequested; - WSADATA wsaData; - int err; - HMODULE wsdll; - - /* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */ - wVersionRequested = MAKEWORD(2, 2); - - err = WSAStartup(wVersionRequested, &wsaData); - if (err != 0) { - /* Tell the user that we could not find a usable */ - /* Winsock DLL. */ - lwsl_err("WSAStartup failed with error: %d\n", err); - return NULL; - } - - /* default to a poll() made out of select() */ - poll = emulated_poll; - - /* if windows socket lib available, use his WSAPoll */ - wsdll = GetModuleHandle(_T("Ws2_32.dll")); - if (wsdll) - poll = (PFNWSAPOLL)GetProcAddress(wsdll, "WSAPoll"); - - /* Finally fall back to emulated poll if all else fails */ - if (!poll) - poll = emulated_poll; - } -#endif - - context = (struct libwebsocket_context *) - malloc(sizeof(struct libwebsocket_context)); - if (!context) { - lwsl_err("No memory for websocket context\n"); - return NULL; - } - memset(context, 0, sizeof(*context)); -#ifndef LWS_NO_DAEMONIZE - context->started_with_parent = pid_daemon; - lwsl_notice(" Started with daemon pid %d\n", pid_daemon); -#endif - - context->listen_service_extraseen = 0; - context->protocols = info->protocols; - context->listen_port = info->port; - context->http_proxy_port = 0; - context->http_proxy_address[0] = '\0'; - context->options = info->options; - /* to reduce this allocation, */ - context->max_fds = getdtablesize(); - lwsl_notice(" static allocation: %u + (%u x %u fds) = %u bytes\n", - sizeof(struct libwebsocket_context), - sizeof(struct pollfd) + sizeof(struct libwebsocket *), - context->max_fds, - sizeof(struct libwebsocket_context) + - ((sizeof(struct pollfd) + sizeof(struct libwebsocket *)) * - context->max_fds)); - - context->fds = (struct pollfd *)malloc(sizeof(struct pollfd) * - context->max_fds); - if (context->fds == NULL) { - lwsl_err("Unable to allocate fds array for %d connections\n", - context->max_fds); - free(context); - return NULL; - } - context->lws_lookup = (struct libwebsocket **) - malloc(sizeof(struct libwebsocket *) * context->max_fds); - if (context->lws_lookup == NULL) { - lwsl_err( - "Unable to allocate lws_lookup array for %d connections\n", - context->max_fds); - free(context->fds); - free(context); - return NULL; - } - memset(context->lws_lookup, 0, sizeof(struct libwebsocket *) * - context->max_fds); - - context->fds_count = 0; -#ifndef LWS_NO_EXTENSIONS - context->extensions = info->extensions; -#endif - context->last_timeout_check_s = 0; - context->user_space = info->user; - -#ifdef WIN32 - context->fd_random = 0; -#else - context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); - if (context->fd_random < 0) { - lwsl_err("Unable to open random device %s %d\n", - SYSTEM_RANDOM_FILEPATH, context->fd_random); - goto bail; - } -#endif - -#ifdef LWS_OPENSSL_SUPPORT - context->use_ssl = 0; - context->ssl_ctx = NULL; - context->ssl_client_ctx = NULL; - openssl_websocket_private_data_index = 0; -#endif - - strcpy(context->canonical_hostname, "unknown"); - -#ifndef LWS_NO_SERVER - if (!(info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME)) { - /* find canonical hostname */ - gethostname((char *)context->canonical_hostname, - sizeof(context->canonical_hostname) - 1); - - lwsl_notice(" canonical_hostname = %s\n", - context->canonical_hostname); - } -#endif - - /* split the proxy ads:port if given */ + + if (!proxy) + return -1; - p = getenv("http_proxy"); - if (p) { - strncpy(context->http_proxy_address, p, - sizeof(context->http_proxy_address) - 1); - context->http_proxy_address[ + strncpy(context->http_proxy_address, proxy, + sizeof(context->http_proxy_address) - 1); + context->http_proxy_address[ sizeof(context->http_proxy_address) - 1] = '\0'; + + p = strchr(context->http_proxy_address, ':'); + if (!p) { + lwsl_err("http_proxy needs to be ads:port\n"); - p = strchr(context->http_proxy_address, ':'); - if (p == NULL) { - lwsl_err("http_proxy needs to be ads:port\n"); - goto bail; - } - *p = '\0'; - context->http_proxy_port = atoi(p + 1); - - lwsl_notice(" Proxy %s:%u\n", - context->http_proxy_address, - context->http_proxy_port); - } - -#ifndef LWS_NO_SERVER - if (info->port) { - -#ifdef LWS_OPENSSL_SUPPORT - context->use_ssl = info->ssl_cert_filepath != NULL && - info->ssl_private_key_filepath != NULL; -#ifdef USE_CYASSL - lwsl_notice(" Compiled with CYASSL support\n"); -#else - lwsl_notice(" Compiled with OpenSSL support\n"); -#endif - if (context->use_ssl) - lwsl_notice(" Using SSL mode\n"); - else - lwsl_notice(" Using non-SSL mode\n"); - -#else - if (info->ssl_cert_filepath != NULL && - info->ssl_private_key_filepath != NULL) { - lwsl_notice(" Not compiled for OpenSSl support!\n"); - goto bail; - } - lwsl_notice(" Compiled without SSL support\n"); -#endif - - lwsl_notice( - " per-conn mem: %u + %u headers + protocol rx buf\n", - sizeof(struct libwebsocket), - sizeof(struct allocated_headers)); - } -#endif - - /* ignore SIGPIPE */ -#ifdef WIN32 -#else - signal(SIGPIPE, sigpipe_handler); -#endif - - -#ifdef LWS_OPENSSL_SUPPORT - - /* basic openssl init */ - - SSL_library_init(); - - OpenSSL_add_all_algorithms(); - SSL_load_error_strings(); - - openssl_websocket_private_data_index = - SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL); - - /* - * Firefox insists on SSLv23 not SSLv3 - * Konq disables SSLv2 by default now, SSLv23 works - */ - - method = (SSL_METHOD *)SSLv23_server_method(); - if (!method) { - lwsl_err("problem creating ssl method %lu: %s\n", - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - context->ssl_ctx = SSL_CTX_new(method); /* create context */ - if (!context->ssl_ctx) { - lwsl_err("problem creating ssl context %lu: %s\n", - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION); -#endif - SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); - if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_ctx, - info->ssl_cipher_list); - -#ifndef LWS_NO_CLIENT - - /* client context */ - - if (info->port == CONTEXT_PORT_NO_LISTEN) { - method = (SSL_METHOD *)SSLv23_client_method(); - if (!method) { - lwsl_err("problem creating ssl method %lu: %s\n", - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - /* create context */ - context->ssl_client_ctx = SSL_CTX_new(method); - if (!context->ssl_client_ctx) { - lwsl_err("problem creating ssl context %lu: %s\n", - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - -#ifdef SSL_OP_NO_COMPRESSION - SSL_CTX_set_options(context->ssl_client_ctx, - SSL_OP_NO_COMPRESSION); -#endif - SSL_CTX_set_options(context->ssl_client_ctx, - SSL_OP_CIPHER_SERVER_PREFERENCE); - if (info->ssl_cipher_list) - SSL_CTX_set_cipher_list(context->ssl_client_ctx, - info->ssl_cipher_list); - - /* openssl init for cert verification (for client sockets) */ - if (!info->ssl_ca_filepath) { - if (!SSL_CTX_load_verify_locations( - context->ssl_client_ctx, NULL, - LWS_OPENSSL_CLIENT_CERTS)) - lwsl_err( - "Unable to load SSL Client certs from %s " - "(set by --with-client-cert-dir= " - "in configure) -- client ssl isn't " - "going to work", LWS_OPENSSL_CLIENT_CERTS); - } else - if (!SSL_CTX_load_verify_locations( - context->ssl_client_ctx, info->ssl_ca_filepath, - NULL)) - lwsl_err( - "Unable to load SSL Client certs " - "file from %s -- client ssl isn't " - "going to work", info->ssl_ca_filepath); - - /* - * callback allowing user code to load extra verification certs - * helping the client to verify server identity - */ - - context->protocols[0].callback(context, NULL, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, - context->ssl_client_ctx, NULL, 0); - } -#endif - - /* as a server, are we requiring clients to identify themselves? */ - - if (info->options & - LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) { - - /* absolutely require the client cert */ - - SSL_CTX_set_verify(context->ssl_ctx, - SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, - OpenSSL_verify_callback); - - /* - * give user code a chance to load certs into the server - * allowing it to verify incoming client certs - */ - - context->protocols[0].callback(context, NULL, - LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, - context->ssl_ctx, NULL, 0); - } - - if (context->use_ssl) { - - /* openssl init for server sockets */ - - /* set the local certificate from CertFile */ - n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx, - info->ssl_cert_filepath); - if (n != 1) { - lwsl_err("problem getting cert '%s' %lu: %s\n", - info->ssl_cert_filepath, - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - /* set the private key from KeyFile */ - if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx, - info->ssl_private_key_filepath, - SSL_FILETYPE_PEM) != 1) { - lwsl_err("ssl problem getting key '%s' %lu: %s\n", - info->ssl_private_key_filepath, - ERR_get_error(), - ERR_error_string(ERR_get_error(), - (char *)context->service_buffer)); - goto bail; - } - /* verify private key */ - if (!SSL_CTX_check_private_key(context->ssl_ctx)) { - lwsl_err("Private SSL key doesn't match cert\n"); - goto bail; - } - - /* SSL is happy and has a cert it's content with */ - } -#endif - -#ifndef LWS_NO_SERVER - /* set up our external listening socket we serve on */ - - if (info->port) { - int sockfd; - - sockfd = socket(AF_INET, SOCK_STREAM, 0); - if (sockfd < 0) { - lwsl_err("ERROR opening socket\n"); - goto bail; - } - -#ifndef WIN32 - /* - * allow us to restart even if old sockets in TIME_WAIT - * (REUSEADDR on Unix means, "don't hang on to this - * address after the listener is closed." On Windows, though, - * it means "don't keep other processes from binding to - * this address while we're using it) - */ - setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, - (const void *)&opt, sizeof(opt)); -#endif - - /* Disable Nagle */ - opt = 1; - setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, - (const void *)&opt, sizeof(opt)); - - #ifdef WIN32 - opt = 0; - ioctlsocket(sockfd, FIONBIO, (unsigned long *)&opt); - #else - fcntl(sockfd, F_SETFL, O_NONBLOCK); - #endif - - bzero((char *) &serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - if (info->iface == NULL) - serv_addr.sin_addr.s_addr = INADDR_ANY; - else - interface_to_sa(info->iface, &serv_addr, - sizeof(serv_addr)); - serv_addr.sin_port = htons(info->port); - - n = bind(sockfd, (struct sockaddr *) &serv_addr, - sizeof(serv_addr)); - if (n < 0) { - lwsl_err("ERROR on binding to port %d (%d %d)\n", - info->port, n, errno); - close(sockfd); - goto bail; - } - - wsi = (struct libwebsocket *)malloc( - sizeof(struct libwebsocket)); - if (wsi == NULL) { - lwsl_err("Out of mem\n"); - close(sockfd); - goto bail; - } - memset(wsi, 0, sizeof(struct libwebsocket)); - wsi->sock = sockfd; -#ifndef LWS_NO_EXTENSIONS - wsi->count_active_extensions = 0; -#endif - wsi->mode = LWS_CONNMODE_SERVER_LISTENER; - - insert_wsi_socket_into_fds(context, wsi); - - context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO; - context->listen_service_count = 0; - context->listen_service_fd = sockfd; - - listen(sockfd, LWS_SOMAXCONN); - lwsl_notice(" Listening on port %d\n", info->port); - } -#endif - - /* - * drop any root privs for this process - * to listen on port < 1023 we would have needed root, but now we are - * listening, we don't want the power for anything else - */ -#ifdef WIN32 -#else - if (info->gid != -1) - if (setgid(info->gid)) - lwsl_warn("setgid: %s\n", strerror(errno)); - if (info->uid != -1) - if (setuid(info->uid)) - lwsl_warn("setuid: %s\n", strerror(errno)); -#endif - - /* initialize supported protocols */ - - for (context->count_protocols = 0; - info->protocols[context->count_protocols].callback; - context->count_protocols++) { - - lwsl_parser(" Protocol: %s\n", - info->protocols[context->count_protocols].name); - - info->protocols[context->count_protocols].owning_server = - context; - info->protocols[context->count_protocols].protocol_index = - context->count_protocols; - - /* - * inform all the protocols that they are doing their one-time - * initialization if they want to - */ - info->protocols[context->count_protocols].callback(context, - NULL, LWS_CALLBACK_PROTOCOL_INIT, NULL, NULL, 0); - } - -#ifndef LWS_NO_EXTENSIONS - /* - * give all extensions a chance to create any per-context - * allocations they need - */ - - m = LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT; - if (info->port) - m = LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT; - - if (info->extensions) { - ext = info->extensions; - while (ext->callback) { - lwsl_ext(" Extension: %s\n", ext->name); - ext->callback(context, ext, NULL, - (enum libwebsocket_extension_callback_reasons)m, - NULL, NULL, 0); - ext++; - } + return -1; } -#endif - return context; + *p = '\0'; + context->http_proxy_port = atoi(p + 1); + + lwsl_notice(" Proxy %s:%u\n", context->http_proxy_address, + context->http_proxy_port); -bail: - libwebsocket_context_destroy(context); - return NULL; + return 0; } /** @@ -2291,52 +702,24 @@ libwebsocket_ensure_user_space(struct libwebsocket *wsi) return 0; } -static void lwsl_emit_stderr(int level, const char *line) +LWS_VISIBLE void lwsl_emit_stderr(int level, const char *line) { char buf[300]; - struct timeval tv; + unsigned long long now; int n; - gettimeofday(&tv, NULL); - buf[0] = '\0'; for (n = 0; n < LLL_COUNT; n++) if (level == (1 << n)) { - sprintf(buf, "[%ld:%04d] %s: ", tv.tv_sec, - (int)(tv.tv_usec / 100), log_level_names[n]); + now = time_in_microseconds() / 100; + sprintf(buf, "[%lu:%04d] %s: ", (unsigned long) now / 10000, + (int)(now % 10000), log_level_names[n]); break; } fprintf(stderr, "%s%s", buf, line); } -#ifdef WIN32 -LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) -{ - lwsl_emit_stderr(level, line); -} -#else -LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) -{ - int syslog_level = LOG_DEBUG; - - switch (level) { - case LLL_ERR: - syslog_level = LOG_ERR; - break; - case LLL_WARN: - syslog_level = LOG_WARNING; - break; - case LLL_NOTICE: - syslog_level = LOG_NOTICE; - break; - case LLL_INFO: - syslog_level = LOG_INFO; - break; - } - syslog(syslog_level, "%s", line); -} -#endif LWS_VISIBLE void _lws_log(int filter, const char *format, ...) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index b3cfa154f9..0bb67f57bc 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -19,15 +19,19 @@ * MA 02110-1301 USA */ -#ifndef __LIBWEBSOCKET_H__ -#define __LIBWEBSOCKET_H__ +#ifndef LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C +#define LIBWEBSOCKET_H_3060898B846849FF9F88F5DB59B5950C #ifdef __cplusplus extern "C" { #include #endif + +#ifdef CMAKE_BUILD +#include "lws_config.h" +#endif -#ifdef WIN32 +#if defined(WIN32) || defined(_WIN32) #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN @@ -35,15 +39,11 @@ extern "C" { #include #include #include -#include "../win32port/win32helpers/websock-w32.h" - -#include "../win32port/win32helpers/gettimeofday.h" +#include #define strcasecmp stricmp #define getdtablesize() 30000 -typedef int ssize_t; - #define LWS_VISIBLE #ifdef LWS_DLL @@ -57,6 +57,7 @@ typedef int ssize_t; #endif #else // NOT WIN32 + #include #include @@ -68,13 +69,24 @@ typedef int ssize_t; #endif +#ifdef LWS_USE_LIBEV +#include +#endif /* LWS_USE_LIBEV */ + #include #ifndef LWS_EXTERN #define LWS_EXTERN extern #endif + +#ifdef _WIN32 +#define random rand +#else +#include +#include +#endif -#define CONTEXT_PORT_NO_LISTEN 0 +#define CONTEXT_PORT_NO_LISTEN -1 #define MAX_MUX_RECURSION 2 enum lws_log_levels { @@ -127,9 +139,20 @@ LWS_VISIBLE LWS_EXTERN void lwsl_hexdump(void *buf, size_t len); #endif +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +/* api change list for user code to test against */ + +#define LWS_FEATURE_SERVE_HTTP_FILE_HAS_OTHER_HEADERS_ARG + + enum libwebsocket_context_options { LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT = 2, LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME = 4, + LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT = 8, + LWS_SERVER_OPTION_LIBEV = 16, + LWS_SERVER_OPTION_DISABLE_IPV6 = 32, + LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS = 64, }; enum libwebsocket_callback_reasons { @@ -145,9 +168,13 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLIENT_WRITEABLE, LWS_CALLBACK_SERVER_WRITEABLE, LWS_CALLBACK_HTTP, + LWS_CALLBACK_HTTP_BODY, + LWS_CALLBACK_HTTP_BODY_COMPLETION, LWS_CALLBACK_HTTP_FILE_COMPLETION, LWS_CALLBACK_HTTP_WRITEABLE, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, + LWS_CALLBACK_FILTER_HTTP_CONNECTION, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, @@ -157,14 +184,38 @@ enum libwebsocket_callback_reasons { LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED, LWS_CALLBACK_PROTOCOL_INIT, LWS_CALLBACK_PROTOCOL_DESTROY, + LWS_CALLBACK_WSI_CREATE, /* always protocol[0] */ + LWS_CALLBACK_WSI_DESTROY, /* always protocol[0] */ + LWS_CALLBACK_GET_THREAD_ID, + /* external poll() management support */ LWS_CALLBACK_ADD_POLL_FD, LWS_CALLBACK_DEL_POLL_FD, - LWS_CALLBACK_SET_MODE_POLL_FD, - LWS_CALLBACK_CLEAR_MODE_POLL_FD, + LWS_CALLBACK_CHANGE_MODE_POLL_FD, + LWS_CALLBACK_LOCK_POLL, + LWS_CALLBACK_UNLOCK_POLL, + + LWS_CALLBACK_USER = 1000, /* user code can use any including / above */ }; -#ifndef LWS_NO_EXTENSIONS +// argument structure for all external poll related calls +// passed in via 'in' +struct libwebsocket_pollargs { + int fd; // applicable file descriptor + int events; // the new event mask + int prev_events; // the previous event mask +}; + +#ifdef _WIN32 +struct libwebsocket_pollfd { + SOCKET fd; + SHORT events; + SHORT revents; +}; +#else +#define libwebsocket_pollfd pollfd +#endif + enum libwebsocket_extension_callback_reasons { LWS_EXT_CALLBACK_SERVER_CONTEXT_CONSTRUCT, LWS_EXT_CALLBACK_CLIENT_CONTEXT_CONSTRUCT, @@ -190,7 +241,6 @@ enum libwebsocket_extension_callback_reasons { LWS_EXT_CALLBACK_PAYLOAD_TX, LWS_EXT_CALLBACK_PAYLOAD_RX, }; -#endif enum libwebsocket_write_protocol { LWS_WRITE_TEXT, @@ -229,6 +279,8 @@ struct lws_tokens { enum lws_token_indexes { WSI_TOKEN_GET_URI, + WSI_TOKEN_POST_URI, + WSI_TOKEN_OPTIONS_URI, WSI_TOKEN_HOST, WSI_TOKEN_CONNECTION, WSI_TOKEN_KEY1, @@ -251,6 +303,26 @@ enum lws_token_indexes { WSI_TOKEN_ACCEPT, WSI_TOKEN_NONCE, WSI_TOKEN_HTTP, + + /* http-related */ + WSI_TOKEN_HTTP_ACCEPT, + WSI_TOKEN_HTTP_AC_REQUEST_HEADERS, + WSI_TOKEN_HTTP_IF_MODIFIED_SINCE, + WSI_TOKEN_HTTP_IF_NONE_MATCH, + WSI_TOKEN_HTTP_ACCEPT_ENCODING, + WSI_TOKEN_HTTP_ACCEPT_LANGUAGE, + WSI_TOKEN_HTTP_PRAGMA, + WSI_TOKEN_HTTP_CACHE_CONTROL, + WSI_TOKEN_HTTP_AUTHORIZATION, + WSI_TOKEN_HTTP_COOKIE, + WSI_TOKEN_HTTP_CONTENT_LENGTH, + WSI_TOKEN_HTTP_CONTENT_TYPE, + WSI_TOKEN_HTTP_DATE, + WSI_TOKEN_HTTP_RANGE, + WSI_TOKEN_HTTP_REFERER, + WSI_TOKEN_HTTP_URI_ARGS, + + WSI_TOKEN_MUXURL, /* use token storage to stash these */ @@ -271,6 +343,10 @@ enum lws_token_indexes { WSI_INIT_TOKEN_MUXURL, }; +struct lws_token_limits { + unsigned short token_limit[WSI_TOKEN_COUNT]; +}; + /* * From RFC 6455 1000 @@ -377,6 +453,37 @@ enum lws_close_status { LWS_CLOSE_STATUS_TLS_FAILURE = 1015, }; +enum http_status { + HTTP_STATUS_OK = 200, + HTTP_STATUS_NO_CONTENT = 204, + + 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, +}; + struct libwebsocket; struct libwebsocket_context; /* needed even with extensions disabled for create context */ @@ -451,6 +558,12 @@ struct libwebsocket_extension; * total number of client connections allowed set * by MAX_CLIENTS. * + * LWS_CALLBACK_HTTP_BODY: the next @len bytes data from the http + * request body HTTP connection is now available in @in. + * + * LWS_CALLBACK_HTTP_BODY_COMPLETION: the expected amount of http request + * body has been delivered + * * LWS_CALLBACK_HTTP_WRITEABLE: you can write more down the http protocol * link now. * @@ -471,11 +584,32 @@ struct libwebsocket_extension; * 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. 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. + * 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_SERVER_NEW_CLIENT_INSTANTIATED: 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_HTTP_CONNECTION: 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_FILTER_PROTOCOL_CONNECTION: called when the handshake has * been received and parsed from the client, but the response is @@ -573,10 +707,17 @@ struct libwebsocket_extension; * context is getting destroyed. Take the opportunity to * deallocate everything that was allocated by the protocol. * - * The next four reasons are optional and only need taking care of if you + * LWS_CALLBACK_WSI_CREATE: outermost (earliest) wsi create notification + * + * LWS_CALLBACK_WSI_DESTROY: outermost (latest) wsi destroy notification + * + * The next five reasons are optional and only need taking care of if you * will be integrating libwebsockets sockets into an external polling * array. * + * For these calls, @in points to a struct libwebsocket_pollargs that + * contains @fd, @events and @prev_events members + * * LWS_CALLBACK_ADD_POLL_FD: libwebsocket deals with its poll() loop * internally, but in the case you are integrating with another * server you will need to have libwebsocket sockets share a @@ -584,28 +725,33 @@ struct libwebsocket_extension; * 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 contains the fd, and - * @len is the events bitmap (like, POLLIN). If you are using the - * internal polling loop (the "service" callback), you can just - * ignore these callbacks. + * serving case. + * This callback happens when a socket needs to be + * added to the polling loop: @in points to a struct + * libwebsocket_pollargs; the @fd member of the struct is the file + * descriptor, and @events contains the active events. + * + * If you are using the internal polling loop (the "service" + * callback), you can just ignore these callbacks. * * LWS_CALLBACK_DEL_POLL_FD: This callback happens when a socket descriptor * needs to be removed from an external polling array. @in is - * the socket desricptor. If you are using the internal polling + * again the struct libwebsocket_pollargs containing the @fd member + * to be removed. If you are using the internal polling * loop, you can just ignore it. * - * LWS_CALLBACK_SET_MODE_POLL_FD: This callback happens when libwebsockets - * wants to modify the events for the socket descriptor in @in. - * The handler should OR @len on to the events member of the pollfd - * struct for this socket descriptor. If you are using the - * internal polling loop, you can just ignore it. - * - * LWS_CALLBACK_CLEAR_MODE_POLL_FD: This callback occurs when libwebsockets - * wants to modify the events for the socket descriptor in @in. - * The handler should AND ~@len on to the events member of the - * pollfd struct for this socket descriptor. If you are using the - * internal polling loop, you can just ignore it. + * LWS_CALLBACK_CHANGE_MODE_POLL_FD: This callback happens when + * libwebsockets wants to modify the events for a connectiion. + * @in is the struct libwebsocket_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: + * LWS_CALLBACK_UNLOCK_POLL: These allow the external poll changes driven + * by libwebsockets to participate in an external thread locking + * scheme around the changes, so the whole thing is threadsafe. */ LWS_VISIBLE LWS_EXTERN int callback(struct libwebsocket_context *context, struct libwebsocket *wsi, @@ -709,6 +855,14 @@ typedef int (extension_callback_function)(struct libwebsocket_context *context, * libwebsockets_remaining_packet_payload(). Notice that you * just talk about frame size here, the LWS_SEND_BUFFER_PRE_PADDING * and post-padding are automatically also allocated on top. + * @no_buffer_all_partial_tx: Leave at zero if you want the library to take + * care of all partial tx for you. It's useful if you only have + * small tx packets and the chance of any truncated send is small + * enough any additional malloc / buffering overhead is less + * painful than writing the code to deal with partial sends. For + * protocols where you stream big blocks, set to nonzero and use + * the return value from libwebsocket_write() to manage how much + * got send yourself. * @owning_server: the server init call fills in this opaque pointer when * registering this protocol with the server. * @protocol_index: which protocol we are starting from zero @@ -723,6 +877,7 @@ struct libwebsocket_protocols { callback_function *callback; size_t per_session_data_size; size_t rx_buffer_size; + int no_buffer_all_partial_tx; /* * below are filled in on server init and can be left uninitialized, @@ -770,6 +925,8 @@ struct libwebsocket_extension { * @extensions: NULL or array of libwebsocket_extension structs listing the * extensions this context supports. If you configured with * --without-extensions, you should give NULL here. + * @token_limits: NULL or struct lws_token_limits pointer which is initialized + * with a token length limit for each possible WSI_TOKEN_*** * @ssl_cert_filepath: 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 @@ -798,10 +955,13 @@ struct lws_context_creation_info { const char *iface; struct libwebsocket_protocols *protocols; struct libwebsocket_extension *extensions; + struct lws_token_limits *token_limits; const char *ssl_cert_filepath; const char *ssl_private_key_filepath; const char *ssl_ca_filepath; const char *ssl_cipher_list; + const char *http_proxy_address; + unsigned int http_proxy_port; int gid; int uid; unsigned int options; @@ -821,6 +981,9 @@ lwsl_emit_syslog(int level, const char *line); LWS_VISIBLE LWS_EXTERN struct libwebsocket_context * libwebsocket_create_context(struct lws_context_creation_info *info); + +LWS_VISIBLE LWS_EXTERN int +libwebsocket_set_proxy(struct libwebsocket_context *context, const char *proxy); LWS_VISIBLE LWS_EXTERN void libwebsocket_context_destroy(struct libwebsocket_context *context); @@ -828,9 +991,22 @@ libwebsocket_context_destroy(struct libwebsocket_context *context); LWS_VISIBLE LWS_EXTERN int libwebsocket_service(struct libwebsocket_context *context, int timeout_ms); +LWS_VISIBLE LWS_EXTERN void +libwebsocket_cancel_service(struct libwebsocket_context *context); + +#ifdef LWS_USE_LIBEV +LWS_VISIBLE LWS_EXTERN int +libwebsocket_initloop( + struct libwebsocket_context *context, struct ev_loop *loop); + +LWS_VISIBLE void +libwebsocket_sigint_cb( + struct ev_loop *loop, struct ev_signal *watcher, int revents); +#endif /* LWS_USE_LIBEV */ + LWS_VISIBLE LWS_EXTERN int libwebsocket_service_fd(struct libwebsocket_context *context, - struct pollfd *pollfd); + struct libwebsocket_pollfd *pollfd); LWS_VISIBLE LWS_EXTERN void * libwebsocket_context_user(struct libwebsocket_context *context); @@ -838,6 +1014,7 @@ libwebsocket_context_user(struct libwebsocket_context *context); enum pending_timeout { NO_PENDING_TIMEOUT = 0, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE, + PENDING_TIMEOUT_AWAITING_CONNECT_RESPONSE, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, PENDING_TIMEOUT_AWAITING_PING, @@ -845,9 +1022,11 @@ enum pending_timeout { PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE, PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE, PENDING_TIMEOUT_SSL_ACCEPT, + PENDING_TIMEOUT_HTTP_CONTENT, + PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND, }; -LWS_EXTERN void +LWS_VISIBLE LWS_EXTERN void libwebsocket_set_timeout(struct libwebsocket *wsi, enum pending_timeout reason, int secs); @@ -890,14 +1069,23 @@ LWS_VISIBLE LWS_EXTERN int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, size_t len, enum libwebsocket_write_protocol protocol); +/* helper for case where buffer may be const */ +#define libwebsocket_write_http(wsi, buf, len) \ + libwebsocket_write(wsi, (unsigned char *)(buf), len, LWS_WRITE_HTTP) + LWS_VISIBLE LWS_EXTERN int libwebsockets_serve_http_file(struct libwebsocket_context *context, struct libwebsocket *wsi, const char *file, - const char *content_type); + const char *content_type, const char *other_headers); LWS_VISIBLE LWS_EXTERN int libwebsockets_serve_http_file_fragment(struct libwebsocket_context *context, struct libwebsocket *wsi); +LWS_VISIBLE LWS_EXTERN int libwebsockets_return_http_status( + struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned int code, + const char *html_body); + LWS_VISIBLE LWS_EXTERN const struct libwebsocket_protocols * libwebsockets_get_protocol(struct libwebsocket *wsi); @@ -909,6 +1097,10 @@ LWS_VISIBLE LWS_EXTERN int libwebsocket_callback_on_writable_all_protocol( const struct libwebsocket_protocols *protocol); +LWS_VISIBLE LWS_EXTERN int +libwebsocket_callback_all_protocol( + const struct libwebsocket_protocols *protocol, int reason); + LWS_VISIBLE LWS_EXTERN int libwebsocket_get_socket_fd(struct libwebsocket *wsi); diff --git a/lib/lws-plat-unix.c b/lib/lws-plat-unix.c new file mode 100755 index 0000000000..78fb6e4a31 --- /dev/null +++ b/lib/lws-plat-unix.c @@ -0,0 +1,404 @@ +#include "private-libwebsockets.h" + +/* + * included from libwebsockets.c for unix builds + */ + +unsigned long long time_in_microseconds(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (tv.tv_sec * 1000000) + tv.tv_usec; +} + +LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context, + void *buf, int len) +{ + return read(context->fd_random, (char *)buf, len); +} + +LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi) +{ + struct libwebsocket_pollfd fds; + + /* treat the fact we got a truncated send pending as if we're choked */ + if (wsi->truncated_send_len) + return 1; + + fds.fd = wsi->sock; + fds.events = POLLOUT; + fds.revents = 0; + + if (poll(&fds, 1, 0) != 1) + return 1; + + if ((fds.revents & POLLOUT) == 0) + return 1; + + /* okay to send another packet without blocking */ + + return 0; +} + +LWS_VISIBLE int +lws_poll_listen_fd(struct libwebsocket_pollfd *fd) +{ + return poll(fd, 1, 0); +} + +/* + * This is just used to interrupt poll waiting + * we don't have to do anything with it. + */ +static void lws_sigusr2(int sig) +{ +} + +/** + * libwebsocket_cancel_service() - Cancel servicing of pending websocket activity + * @context: Websocket context + * + * This function let a call to libwebsocket_service() waiting for a timeout + * immediately return. + */ +LWS_VISIBLE void +libwebsocket_cancel_service(struct libwebsocket_context *context) +{ + char buf = 0; + + if (write(context->dummy_pipe_fds[1], &buf, sizeof(buf)) != 1) + lwsl_err("Cannot write to dummy pipe"); +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + int syslog_level = LOG_DEBUG; + + switch (level) { + case LLL_ERR: + syslog_level = LOG_ERR; + break; + case LLL_WARN: + syslog_level = LOG_WARNING; + break; + case LLL_NOTICE: + syslog_level = LOG_NOTICE; + break; + case LLL_INFO: + syslog_level = LOG_INFO; + break; + } + syslog(syslog_level, "%s", line); +} + +LWS_VISIBLE int +lws_plat_service(struct libwebsocket_context *context, int timeout_ms) +{ + int n; + int m; + char buf; + + /* stay dead once we are dead */ + + if (!context) + return 1; + + lws_libev_run(context); + + context->service_tid = context->protocols[0].callback(context, NULL, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + + n = poll(context->fds, context->fds_count, timeout_ms); + context->service_tid = 0; + + if (n == 0) /* poll timeout */ { + libwebsocket_service_fd(context, NULL); + return 0; + } + + if (n < 0) { + if (LWS_ERRNO != LWS_EINTR) + return -1; + return 0; + } + + /* any socket with events to service? */ + + for (n = 0; n < context->fds_count; n++) { + if (!context->fds[n].revents) + continue; + + if (context->fds[n].fd == context->dummy_pipe_fds[0]) { + if (read(context->fds[n].fd, &buf, 1) != 1) + lwsl_err("Cannot read from dummy pipe."); + continue; + } + + m = libwebsocket_service_fd(context, &context->fds[n]); + if (m < 0) + return -1; + /* if something closed, retry this slot */ + if (m) + n--; + } + + return 0; +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct libwebsocket_context *context, int fd) +{ + int optval = 1; + socklen_t optlen = sizeof(optval); + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__OpenBSD__) + struct protoent *tcp_proto; +#endif + + if (context->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const void *)&optval, optlen) < 0) + return 1; + +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \ + defined(__CYGWIN__) || defined(__OpenBSD__) + + /* + * didn't find a way to set these per-socket, need to + * tune kernel systemwide values + */ +#else + /* set the keepalive conditions we want on it too */ + optval = context->ka_time; + if (setsockopt(fd, IPPROTO_IP, TCP_KEEPIDLE, + (const void *)&optval, optlen) < 0) + return 1; + + optval = context->ka_interval; + if (setsockopt(fd, IPPROTO_IP, TCP_KEEPINTVL, + (const void *)&optval, optlen) < 0) + return 1; + + optval = context->ka_probes; + if (setsockopt(fd, IPPROTO_IP, TCP_KEEPCNT, + (const void *)&optval, optlen) < 0) + return 1; +#endif + } + + /* Disable Nagle */ + optval = 1; +#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \ + !defined(__OpenBSD__) + setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen); +#else + tcp_proto = getprotobyname("TCP"); + setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen); +#endif + + /* We are nonblocking... */ + fcntl(fd, F_SETFL, O_NONBLOCK); + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ + if (info->gid != -1) + if (setgid(info->gid)) + lwsl_warn("setgid: %s\n", strerror(LWS_ERRNO)); + if (info->uid != -1) + if (setuid(info->uid)) + lwsl_warn("setuid: %s\n", strerror(LWS_ERRNO)); +} + +LWS_VISIBLE int +lws_plat_init_fd_tables(struct libwebsocket_context *context) +{ + if (lws_libev_init_fd_table(context)) + /* libev handled it instead */ + return 0; + + if (pipe(context->dummy_pipe_fds)) { + lwsl_err("Unable to create pipe\n"); + return 1; + } + + /* use the read end of pipe as first item */ + context->fds[0].fd = context->dummy_pipe_fds[0]; + context->fds[0].events = LWS_POLLIN; + context->fds[0].revents = 0; + context->fds_count = 1; + + context->fd_random = open(SYSTEM_RANDOM_FILEPATH, O_RDONLY); + if (context->fd_random < 0) { + lwsl_err("Unable to open random device %s %d\n", + SYSTEM_RANDOM_FILEPATH, context->fd_random); + return 1; + } + + return 0; +} + +static void sigpipe_handler(int x) +{ +} + + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + sigset_t mask; + + signal(SIGUSR2, lws_sigusr2); + sigemptyset(&mask); + sigaddset(&mask, SIGUSR2); + + sigprocmask(SIG_BLOCK, &mask, NULL); + + signal(SIGPIPE, sigpipe_handler); + + return 0; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct libwebsocket_context *context) +{ +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct libwebsocket_context *context) +{ + close(context->dummy_pipe_fds[0]); + close(context->dummy_pipe_fds[1]); + close(context->fd_random); +} + +/* cast a struct sockaddr_in6 * into addr for ipv6 */ + +LWS_VISIBLE int +interface_to_sa(struct libwebsocket_context *context, + const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ + int rc = -1; + + struct ifaddrs *ifr; + struct ifaddrs *ifc; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; +#endif + + getifaddrs(&ifr); + for (ifc = ifr; ifc != NULL && rc; ifc = ifc->ifa_next) { + if (!ifc->ifa_addr) + continue; + + lwsl_info(" interface %s vs %s\n", ifc->ifa_name, ifname); + + if (strcmp(ifc->ifa_name, ifname)) + continue; + + switch (ifc->ifa_addr->sa_family) { + case AF_INET: +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + /* map IPv4 to IPv6 */ + bzero((char *)&addr6->sin6_addr, + sizeof(struct in6_addr)); + addr6->sin6_addr.s6_addr[10] = 0xff; + addr6->sin6_addr.s6_addr[11] = 0xff; + memcpy(&addr6->sin6_addr.s6_addr[12], + &((struct sockaddr_in *)ifc->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + } else +#endif + memcpy(addr, + (struct sockaddr_in *)ifc->ifa_addr, + sizeof(struct sockaddr_in)); + break; +#ifdef LWS_USE_IPV6 + case AF_INET6: + if (rc >= 0) + break; + memcpy(&addr6->sin6_addr, + &((struct sockaddr_in6 *)ifc->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + break; +#endif + default: + continue; + } + rc = 0; + } + + freeifaddrs(ifr); + + if (rc == -1) { + /* check if bind to IP adddress */ +#ifdef LWS_USE_IPV6 + if (inet_pton(AF_INET6, ifname, &addr6->sin6_addr) == 1) + rc = 0; + else +#endif + if (inet_pton(AF_INET, ifname, &addr->sin_addr) == 1) + rc = 0; + } + + return rc; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ); + context->fds[context->fds_count++].revents = 0; +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi, int m) +{ +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct libwebsocket_context *context) +{ + /* if our parent went down, don't linger around */ + if (context->started_with_parent && + kill(context->started_with_parent, 0) < 0) + kill(getpid(), SIGTERM); +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd) +{ + return 0; +} + +LWS_VISIBLE int +lws_plat_open_file(const char* filename, unsigned long* filelen) +{ + struct stat stat_buf; + int ret = open(filename, O_RDONLY); + + if (ret < 0) + return LWS_INVALID_FILE; + + fstat(ret, &stat_buf); + *filelen = stat_buf.st_size; + return ret; +} + +#ifdef LWS_USE_IPV6 +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + return inet_ntop(af, src, dst, cnt); +} +#endif diff --git a/lib/lws-plat-win.c b/lib/lws-plat-win.c new file mode 100755 index 0000000000..b33f79c25f --- /dev/null +++ b/lib/lws-plat-win.c @@ -0,0 +1,358 @@ +#include "private-libwebsockets.h" + +unsigned long long +time_in_microseconds() +{ +#define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL + FILETIME filetime; + ULARGE_INTEGER datetime; + +#ifdef _WIN32_WCE + GetCurrentFT(&filetime); +#else + GetSystemTimeAsFileTime(&filetime); +#endif + + /* + * As per Windows documentation for FILETIME, copy the resulting FILETIME structure to a + * ULARGE_INTEGER structure using memcpy (using memcpy instead of direct assignment can + * prevent alignment faults on 64-bit Windows). + */ + memcpy(&datetime, &filetime, sizeof(datetime)); + + /* Windows file times are in 100s of nanoseconds. */ + return (datetime.QuadPart - DELTA_EPOCH_IN_MICROSECS) / 10; +} + +#ifdef _WIN32_WCE +time_t time(time_t *t) +{ + time_t ret = time_in_microseconds() / 1000000; + *t = ret; + return ret; +} +#endif + +LWS_VISIBLE int libwebsockets_get_random(struct libwebsocket_context *context, + void *buf, int len) +{ + int n; + char *p = (char *)buf; + + for (n = 0; n < len; n++) + p[n] = (unsigned char)rand(); + + return n; +} + +LWS_VISIBLE int lws_send_pipe_choked(struct libwebsocket *wsi) +{ + return wsi->sock_send_blocking; +} + +LWS_VISIBLE int lws_poll_listen_fd(struct libwebsocket_pollfd *fd) +{ + fd_set readfds; + struct timeval tv = { 0, 0 }; + + assert(fd->events == LWS_POLLIN); + + FD_ZERO(&readfds); + FD_SET(fd->fd, &readfds); + + return select(fd->fd + 1, &readfds, NULL, NULL, &tv); +} + +/** + * libwebsocket_cancel_service() - Cancel servicing of pending websocket activity + * @context: Websocket context + * + * This function let a call to libwebsocket_service() waiting for a timeout + * immediately return. + */ +LWS_VISIBLE void +libwebsocket_cancel_service(struct libwebsocket_context *context) +{ + WSASetEvent(context->events[0]); +} + +LWS_VISIBLE void lwsl_emit_syslog(int level, const char *line) +{ + lwsl_emit_stderr(level, line); +} + +LWS_VISIBLE int +lws_plat_service(struct libwebsocket_context *context, int timeout_ms) +{ + int n; + int i; + DWORD ev; + WSANETWORKEVENTS networkevents; + struct libwebsocket_pollfd *pfd; + + /* stay dead once we are dead */ + + if (context == NULL) + return 1; + + context->service_tid = context->protocols[0].callback(context, NULL, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + + for (i = 0; i < context->fds_count; ++i) { + pfd = &context->fds[i]; + if (pfd->fd == context->listen_service_fd) + continue; + + if (pfd->events & LWS_POLLOUT) { + if (context->lws_lookup[pfd->fd]->sock_send_blocking) + continue; + pfd->revents = LWS_POLLOUT; + n = libwebsocket_service_fd(context, pfd); + if (n < 0) + return n; + } + } + + ev = WSAWaitForMultipleEvents(context->fds_count + 1, + context->events, FALSE, timeout_ms, FALSE); + context->service_tid = 0; + + if (ev == WSA_WAIT_TIMEOUT) { + libwebsocket_service_fd(context, NULL); + return 0; + } + + if (ev == WSA_WAIT_EVENT_0) { + WSAResetEvent(context->events[0]); + return 0; + } + + if (ev < WSA_WAIT_EVENT_0 || ev > WSA_WAIT_EVENT_0 + context->fds_count) + return -1; + + pfd = &context->fds[ev - WSA_WAIT_EVENT_0 - 1]; + + if (WSAEnumNetworkEvents(pfd->fd, + context->events[ev - WSA_WAIT_EVENT_0], + &networkevents) == SOCKET_ERROR) { + lwsl_err("WSAEnumNetworkEvents() failed with error %d\n", + LWS_ERRNO); + return -1; + } + + pfd->revents = networkevents.lNetworkEvents; + + if (pfd->revents & LWS_POLLOUT) + context->lws_lookup[pfd->fd]->sock_send_blocking = FALSE; + + return libwebsocket_service_fd(context, pfd); +} + +LWS_VISIBLE int +lws_plat_set_socket_options(struct libwebsocket_context *context, int fd) +{ + int optval = 1; + int optlen = sizeof(optval); + u_long optl = 1; + DWORD dwBytesRet; + struct tcp_keepalive alive; + struct protoent *tcp_proto; + + if (context->ka_time) { + /* enable keepalive on this socket */ + optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, + (const char *)&optval, optlen) < 0) + return 1; + + alive.onoff = TRUE; + alive.keepalivetime = context->ka_time; + alive.keepaliveinterval = context->ka_interval; + + if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive), + NULL, 0, &dwBytesRet, NULL, NULL)) + return 1; + } + + /* Disable Nagle */ + optval = 1; + tcp_proto = getprotobyname("TCP"); + setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen); + + /* We are nonblocking... */ + ioctlsocket(fd, FIONBIO, &optl); + + return 0; +} + +LWS_VISIBLE void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info) +{ +} + +LWS_VISIBLE int +lws_plat_init_fd_tables(struct libwebsocket_context *context) +{ + context->events = (WSAEVENT *)malloc(sizeof(WSAEVENT) * + (context->max_fds + 1)); + if (context->events == NULL) { + lwsl_err("Unable to allocate events array for %d connections\n", + context->max_fds); + return 1; + } + + context->fds_count = 0; + context->events[0] = WSACreateEvent(); + + context->fd_random = 0; + + return 0; +} + +LWS_VISIBLE int +lws_plat_context_early_init(void) +{ + WORD wVersionRequested; + WSADATA wsaData; + int err; + + /* Use the MAKEWORD(lowbyte, highbyte) macro from Windef.h */ + wVersionRequested = MAKEWORD(2, 2); + + err = WSAStartup(wVersionRequested, &wsaData); + if (!err) + return 0; + /* + * Tell the user that we could not find a usable + * Winsock DLL + */ + lwsl_err("WSAStartup failed with error: %d\n", err); + + return 1; +} + +LWS_VISIBLE void +lws_plat_context_early_destroy(struct libwebsocket_context *context) +{ + if (context->events) { + WSACloseEvent(context->events[0]); + free(context->events); + } +} + +LWS_VISIBLE void +lws_plat_context_late_destroy(struct libwebsocket_context *context) +{ + WSACleanup(); +} + +LWS_VISIBLE int +interface_to_sa(struct libwebsocket_context *context, + const char *ifname, struct sockaddr_in *addr, size_t addrlen) +{ + return -1; +} + +LWS_VISIBLE void +lws_plat_insert_socket_into_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + context->fds[context->fds_count++].revents = 0; + context->events[context->fds_count] = WSACreateEvent(); + WSAEventSelect(wsi->sock, context->events[context->fds_count], LWS_POLLIN); +} + +LWS_VISIBLE void +lws_plat_delete_socket_from_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi, int m) +{ + WSACloseEvent(context->events[m + 1]); + context->events[m + 1] = context->events[context->fds_count + 1]; +} + +LWS_VISIBLE void +lws_plat_service_periodic(struct libwebsocket_context *context) +{ +} + +LWS_VISIBLE int +lws_plat_change_pollfd(struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd) +{ + long networkevents = LWS_POLLOUT | LWS_POLLHUP; + + if ((pfd->events & LWS_POLLIN)) + networkevents |= LWS_POLLIN; + + if (WSAEventSelect(wsi->sock, + context->events[wsi->position_in_fds_table + 1], + networkevents) != SOCKET_ERROR) + return 0; + + lwsl_err("WSAEventSelect() failed with error %d\n", LWS_ERRNO); + + return 1; +} + +LWS_VISIBLE HANDLE +lws_plat_open_file(const char* filename, unsigned long* filelen) +{ + HANDLE ret; + WCHAR buffer[MAX_PATH]; + + MultiByteToWideChar(CP_UTF8, 0, filename, -1, buffer, + sizeof(buffer) / sizeof(buffer[0])); + ret = CreateFileW(buffer, GENERIC_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (ret != LWS_INVALID_FILE) + *filelen = GetFileSize(ret, NULL); + + return ret; +} + +LWS_VISIBLE const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt) +{ + WCHAR *buffer; + DWORD bufferlen = cnt; + BOOL ok = FALSE; + + buffer = malloc(bufferlen); + if (!buffer) { + lwsl_err("Out of memory\n"); + return NULL; + } + + if (af == AF_INET) { + struct sockaddr_in srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin_family = AF_INET; + memcpy(&(srcaddr.sin_addr), src, sizeof(srcaddr.sin_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#ifdef LWS_USE_IPV6 + } else if (af == AF_INET6) { + struct sockaddr_in6 srcaddr; + bzero(&srcaddr, sizeof(srcaddr)); + srcaddr.sin6_family = AF_INET6; + memcpy(&(srcaddr.sin6_addr), src, sizeof(srcaddr.sin6_addr)); + + if (!WSAAddressToStringW((struct sockaddr*)&srcaddr, sizeof(srcaddr), 0, buffer, &bufferlen)) + ok = TRUE; +#endif + } else + lwsl_err("Unsupported type\n"); + + if (!ok) { + int rv = WSAGetLastError(); + lwsl_err("WSAAddressToString() : %d\n", rv); + } else { + if (WideCharToMultiByte(CP_ACP, 0, buffer, bufferlen, dst, cnt, 0, NULL) <= 0) + ok = FALSE; + } + + free(buffer); + return ok ? dst : NULL; +} diff --git a/lib/minilex.c b/lib/minilex.c index 4912ad60d1..cdc500ed23 100644 --- a/lib/minilex.c +++ b/lib/minilex.c @@ -1,294 +1,79 @@ +/* + * minilex.c + * + * High efficiency lexical state parser + * + * Copyright (C)2011-2014 Andy Green + * + * Licensed under LGPL2 + * + * Usage: gcc minilex.c -o minilex && ./minilex > lextable.h + * + * Run it twice to test parsing on the generated table on stderr + */ + #include #include #include +/* set of parsable strings -- ALL LOWER CASE */ + const char *set[] = { - "GET ", - "Host:", - "Connection:", - "Sec-WebSocket-Key1:", - "Sec-WebSocket-Key2:", - "Sec-WebSocket-Protocol:", - "Upgrade:", - "Origin:", - "Sec-WebSocket-Draft:", + "get ", + "post ", + "options ", + "host:", + "connection:", + "sec-websocket-key1:", + "sec-websocket-key2:", + "sec-websocket-protocol:", + "upgrade:", + "origin:", + "sec-websocket-draft:", "\x0d\x0a", - "Sec-WebSocket-Key:", - "Sec-WebSocket-Version:", - "Sec-WebSocket-Origin:", + "sec-websocket-key:", + "sec-websocket-version:", + "sec-websocket-origin:", + + "sec-websocket-extensions:", + + "sec-websocket-accept:", + "sec-websocket-nonce:", + "http/1.1 ", + + "accept:", + "access-control-request-headers:", + "if-modified-since:", + "if-none-match:", + "accept-encoding:", + "accept-language:", + "pragma:", + "cache-control:", + "authorization:", + "cookie:", + "content-length:", + "content-type:", + "date:", + "range:", + "referer:", + "", /* not matchable */ - "Sec-WebSocket-Extensions:", - - "Sec-WebSocket-Accept:", - "Sec-WebSocket-Nonce:", - "HTTP/1.1 ", }; -unsigned char lextable[] = { -/* pos 0: state 0 */ - 0x47 /* 'G' */, 0x07 /* to pos 14 state 1 */, - 0x48 /* 'H' */, 0x0A /* to pos 22 state 5 */, - 0x43 /* 'C' */, 0x0F /* to pos 34 state 10 */, - 0x53 /* 'S' */, 0x19 /* to pos 56 state 21 */, - 0x55 /* 'U' */, 0x3F /* to pos 134 state 51 */, - 0x4F /* 'O' */, 0x46 /* to pos 150 state 59 */, - 0x8D /* '.' */, 0x52 /* to pos 176 state 72 */, -/* pos 14: state 1 */ - 0xC5 /* 'E' */, 0x01 /* to pos 16 state 2 */, -/* pos 16: state 2 */ - 0xD4 /* 'T' */, 0x01 /* to pos 18 state 3 */, -/* pos 18: state 3 */ - 0xA0 /* ' ' */, 0x01 /* to pos 20 state 4 */, -/* pos 20: state 4 */ - 0x80, 0x00 /* terminal marker */, -/* pos 22: state 5 */ - 0x6F /* 'o' */, 0x02 /* to pos 26 state 6 */, - 0xD4 /* 'T' */, 0x76 /* to pos 260 state 114 */, -/* pos 26: state 6 */ - 0xF3 /* 's' */, 0x01 /* to pos 28 state 7 */, -/* pos 28: state 7 */ - 0xF4 /* 't' */, 0x01 /* to pos 30 state 8 */, -/* pos 30: state 8 */ - 0xBA /* ':' */, 0x01 /* to pos 32 state 9 */, -/* pos 32: state 9 */ - 0x81, 0x00 /* terminal marker */, -/* pos 34: state 10 */ - 0xEF /* 'o' */, 0x01 /* to pos 36 state 11 */, -/* pos 36: state 11 */ - 0xEE /* 'n' */, 0x01 /* to pos 38 state 12 */, -/* pos 38: state 12 */ - 0xEE /* 'n' */, 0x01 /* to pos 40 state 13 */, -/* pos 40: state 13 */ - 0xE5 /* 'e' */, 0x01 /* to pos 42 state 14 */, -/* pos 42: state 14 */ - 0xE3 /* 'c' */, 0x01 /* to pos 44 state 15 */, -/* pos 44: state 15 */ - 0xF4 /* 't' */, 0x01 /* to pos 46 state 16 */, -/* pos 46: state 16 */ - 0xE9 /* 'i' */, 0x01 /* to pos 48 state 17 */, -/* pos 48: state 17 */ - 0xEF /* 'o' */, 0x01 /* to pos 50 state 18 */, -/* pos 50: state 18 */ - 0xEE /* 'n' */, 0x01 /* to pos 52 state 19 */, -/* pos 52: state 19 */ - 0xBA /* ':' */, 0x01 /* to pos 54 state 20 */, -/* pos 54: state 20 */ - 0x82, 0x00 /* terminal marker */, -/* pos 56: state 21 */ - 0xE5 /* 'e' */, 0x01 /* to pos 58 state 22 */, -/* pos 58: state 22 */ - 0xE3 /* 'c' */, 0x01 /* to pos 60 state 23 */, -/* pos 60: state 23 */ - 0xAD /* '-' */, 0x01 /* to pos 62 state 24 */, -/* pos 62: state 24 */ - 0xD7 /* 'W' */, 0x01 /* to pos 64 state 25 */, -/* pos 64: state 25 */ - 0xE5 /* 'e' */, 0x01 /* to pos 66 state 26 */, -/* pos 66: state 26 */ - 0xE2 /* 'b' */, 0x01 /* to pos 68 state 27 */, -/* pos 68: state 27 */ - 0xD3 /* 'S' */, 0x01 /* to pos 70 state 28 */, -/* pos 70: state 28 */ - 0xEF /* 'o' */, 0x01 /* to pos 72 state 29 */, -/* pos 72: state 29 */ - 0xE3 /* 'c' */, 0x01 /* to pos 74 state 30 */, -/* pos 74: state 30 */ - 0xEB /* 'k' */, 0x01 /* to pos 76 state 31 */, -/* pos 76: state 31 */ - 0xE5 /* 'e' */, 0x01 /* to pos 78 state 32 */, -/* pos 78: state 32 */ - 0xF4 /* 't' */, 0x01 /* to pos 80 state 33 */, -/* pos 80: state 33 */ - 0xAD /* '-' */, 0x01 /* to pos 82 state 34 */, -/* pos 82: state 34 */ - 0x4B /* 'K' */, 0x08 /* to pos 98 state 35 */, - 0x50 /* 'P' */, 0x10 /* to pos 116 state 42 */, - 0x44 /* 'D' */, 0x27 /* to pos 164 state 66 */, - 0x56 /* 'V' */, 0x2F /* to pos 182 state 75 */, - 0x4F /* 'O' */, 0x36 /* to pos 198 state 83 */, - 0x45 /* 'E' */, 0x3C /* to pos 212 state 90 */, - 0x41 /* 'A' */, 0x46 /* to pos 234 state 101 */, - 0xCE /* 'N' */, 0x4C /* to pos 248 state 108 */, -/* pos 98: state 35 */ - 0xE5 /* 'e' */, 0x01 /* to pos 100 state 36 */, -/* pos 100: state 36 */ - 0xF9 /* 'y' */, 0x01 /* to pos 102 state 37 */, -/* pos 102: state 37 */ - 0x31 /* '1' */, 0x03 /* to pos 108 state 38 */, - 0x32 /* '2' */, 0x04 /* to pos 112 state 40 */, - 0xBA /* ':' */, 0x25 /* to pos 180 state 74 */, -/* pos 108: state 38 */ - 0xBA /* ':' */, 0x01 /* to pos 110 state 39 */, -/* pos 110: state 39 */ - 0x83, 0x00 /* terminal marker */, -/* pos 112: state 40 */ - 0xBA /* ':' */, 0x01 /* to pos 114 state 41 */, -/* pos 114: state 41 */ - 0x84, 0x00 /* terminal marker */, -/* pos 116: state 42 */ - 0xF2 /* 'r' */, 0x01 /* to pos 118 state 43 */, -/* pos 118: state 43 */ - 0xEF /* 'o' */, 0x01 /* to pos 120 state 44 */, -/* pos 120: state 44 */ - 0xF4 /* 't' */, 0x01 /* to pos 122 state 45 */, -/* pos 122: state 45 */ - 0xEF /* 'o' */, 0x01 /* to pos 124 state 46 */, -/* pos 124: state 46 */ - 0xE3 /* 'c' */, 0x01 /* to pos 126 state 47 */, -/* pos 126: state 47 */ - 0xEF /* 'o' */, 0x01 /* to pos 128 state 48 */, -/* pos 128: state 48 */ - 0xEC /* 'l' */, 0x01 /* to pos 130 state 49 */, -/* pos 130: state 49 */ - 0xBA /* ':' */, 0x01 /* to pos 132 state 50 */, -/* pos 132: state 50 */ - 0x85, 0x00 /* terminal marker */, -/* pos 134: state 51 */ - 0xF0 /* 'p' */, 0x01 /* to pos 136 state 52 */, -/* pos 136: state 52 */ - 0xE7 /* 'g' */, 0x01 /* to pos 138 state 53 */, -/* pos 138: state 53 */ - 0xF2 /* 'r' */, 0x01 /* to pos 140 state 54 */, -/* pos 140: state 54 */ - 0xE1 /* 'a' */, 0x01 /* to pos 142 state 55 */, -/* pos 142: state 55 */ - 0xE4 /* 'd' */, 0x01 /* to pos 144 state 56 */, -/* pos 144: state 56 */ - 0xE5 /* 'e' */, 0x01 /* to pos 146 state 57 */, -/* pos 146: state 57 */ - 0xBA /* ':' */, 0x01 /* to pos 148 state 58 */, -/* pos 148: state 58 */ - 0x86, 0x00 /* terminal marker */, -/* pos 150: state 59 */ - 0xF2 /* 'r' */, 0x01 /* to pos 152 state 60 */, -/* pos 152: state 60 */ - 0xE9 /* 'i' */, 0x01 /* to pos 154 state 61 */, -/* pos 154: state 61 */ - 0xE7 /* 'g' */, 0x01 /* to pos 156 state 62 */, -/* pos 156: state 62 */ - 0xE9 /* 'i' */, 0x01 /* to pos 158 state 63 */, -/* pos 158: state 63 */ - 0xEE /* 'n' */, 0x01 /* to pos 160 state 64 */, -/* pos 160: state 64 */ - 0xBA /* ':' */, 0x01 /* to pos 162 state 65 */, -/* pos 162: state 65 */ - 0x87, 0x00 /* terminal marker */, -/* pos 164: state 66 */ - 0xF2 /* 'r' */, 0x01 /* to pos 166 state 67 */, -/* pos 166: state 67 */ - 0xE1 /* 'a' */, 0x01 /* to pos 168 state 68 */, -/* pos 168: state 68 */ - 0xE6 /* 'f' */, 0x01 /* to pos 170 state 69 */, -/* pos 170: state 69 */ - 0xF4 /* 't' */, 0x01 /* to pos 172 state 70 */, -/* pos 172: state 70 */ - 0xBA /* ':' */, 0x01 /* to pos 174 state 71 */, -/* pos 174: state 71 */ - 0x88, 0x00 /* terminal marker */, -/* pos 176: state 72 */ - 0x8A /* '.' */, 0x01 /* to pos 178 state 73 */, -/* pos 178: state 73 */ - 0x89, 0x00 /* terminal marker */, -/* pos 180: state 74 */ - 0x8A, 0x00 /* terminal marker */, -/* pos 182: state 75 */ - 0xE5 /* 'e' */, 0x01 /* to pos 184 state 76 */, -/* pos 184: state 76 */ - 0xF2 /* 'r' */, 0x01 /* to pos 186 state 77 */, -/* pos 186: state 77 */ - 0xF3 /* 's' */, 0x01 /* to pos 188 state 78 */, -/* pos 188: state 78 */ - 0xE9 /* 'i' */, 0x01 /* to pos 190 state 79 */, -/* pos 190: state 79 */ - 0xEF /* 'o' */, 0x01 /* to pos 192 state 80 */, -/* pos 192: state 80 */ - 0xEE /* 'n' */, 0x01 /* to pos 194 state 81 */, -/* pos 194: state 81 */ - 0xBA /* ':' */, 0x01 /* to pos 196 state 82 */, -/* pos 196: state 82 */ - 0x8B, 0x00 /* terminal marker */, -/* pos 198: state 83 */ - 0xF2 /* 'r' */, 0x01 /* to pos 200 state 84 */, -/* pos 200: state 84 */ - 0xE9 /* 'i' */, 0x01 /* to pos 202 state 85 */, -/* pos 202: state 85 */ - 0xE7 /* 'g' */, 0x01 /* to pos 204 state 86 */, -/* pos 204: state 86 */ - 0xE9 /* 'i' */, 0x01 /* to pos 206 state 87 */, -/* pos 206: state 87 */ - 0xEE /* 'n' */, 0x01 /* to pos 208 state 88 */, -/* pos 208: state 88 */ - 0xBA /* ':' */, 0x01 /* to pos 210 state 89 */, -/* pos 210: state 89 */ - 0x8C, 0x00 /* terminal marker */, -/* pos 212: state 90 */ - 0xF8 /* 'x' */, 0x01 /* to pos 214 state 91 */, -/* pos 214: state 91 */ - 0xF4 /* 't' */, 0x01 /* to pos 216 state 92 */, -/* pos 216: state 92 */ - 0xE5 /* 'e' */, 0x01 /* to pos 218 state 93 */, -/* pos 218: state 93 */ - 0xEE /* 'n' */, 0x01 /* to pos 220 state 94 */, -/* pos 220: state 94 */ - 0xF3 /* 's' */, 0x01 /* to pos 222 state 95 */, -/* pos 222: state 95 */ - 0xE9 /* 'i' */, 0x01 /* to pos 224 state 96 */, -/* pos 224: state 96 */ - 0xEF /* 'o' */, 0x01 /* to pos 226 state 97 */, -/* pos 226: state 97 */ - 0xEE /* 'n' */, 0x01 /* to pos 228 state 98 */, -/* pos 228: state 98 */ - 0xF3 /* 's' */, 0x01 /* to pos 230 state 99 */, -/* pos 230: state 99 */ - 0xBA /* ':' */, 0x01 /* to pos 232 state 100 */, -/* pos 232: state 100 */ - 0x8D, 0x00 /* terminal marker */, -/* pos 234: state 101 */ - 0xE3 /* 'c' */, 0x01 /* to pos 236 state 102 */, -/* pos 236: state 102 */ - 0xE3 /* 'c' */, 0x01 /* to pos 238 state 103 */, -/* pos 238: state 103 */ - 0xE5 /* 'e' */, 0x01 /* to pos 240 state 104 */, -/* pos 240: state 104 */ - 0xF0 /* 'p' */, 0x01 /* to pos 242 state 105 */, -/* pos 242: state 105 */ - 0xF4 /* 't' */, 0x01 /* to pos 244 state 106 */, -/* pos 244: state 106 */ - 0xBA /* ':' */, 0x01 /* to pos 246 state 107 */, -/* pos 246: state 107 */ - 0x8E, 0x00 /* terminal marker */, -/* pos 248: state 108 */ - 0xEF /* 'o' */, 0x01 /* to pos 250 state 109 */, -/* pos 250: state 109 */ - 0xEE /* 'n' */, 0x01 /* to pos 252 state 110 */, -/* pos 252: state 110 */ - 0xE3 /* 'c' */, 0x01 /* to pos 254 state 111 */, -/* pos 254: state 111 */ - 0xE5 /* 'e' */, 0x01 /* to pos 256 state 112 */, -/* pos 256: state 112 */ - 0xBA /* ':' */, 0x01 /* to pos 258 state 113 */, -/* pos 258: state 113 */ - 0x8F, 0x00 /* terminal marker */, -/* pos 260: state 114 */ - 0xD4 /* 'T' */, 0x01 /* to pos 262 state 115 */, -/* pos 262: state 115 */ - 0xD0 /* 'P' */, 0x01 /* to pos 264 state 116 */, -/* pos 264: state 116 */ - 0xAF /* '/' */, 0x01 /* to pos 266 state 117 */, -/* pos 266: state 117 */ - 0xB1 /* '1' */, 0x01 /* to pos 268 state 118 */, -/* pos 268: state 118 */ - 0xAE /* '.' */, 0x01 /* to pos 270 state 119 */, -/* pos 270: state 119 */ - 0xB1 /* '1' */, 0x01 /* to pos 272 state 120 */, -/* pos 272: state 120 */ - 0xA0 /* ' ' */, 0x01 /* to pos 274 state 121 */, -/* pos 274: state 121 */ - 0x90, 0x00 /* terminal marker */, -/* total size 276 bytes */ - +/* + * b7 = 0 = 1-byte seq + * 0x08 = fail + * 2-byte seq + * 0x00 - 0x07, then terminal as given in 2nd byte + 3-byte seq + * no match: go fwd 3 byte, match: jump fwd by amt in +1/+2 bytes + * = 1 = 1-byte seq + * no match: die, match go fwd 1 byte + */ +unsigned char lextable[] = { + #include "lextable.h" }; #define PARALLEL 30 @@ -298,29 +83,41 @@ struct state { int state[PARALLEL]; int count; int bytepos; + + int real_pos; }; struct state state[1000]; int next = 1; +#define FAIL_CHAR 0x08 + int lextable_decode(int pos, char c) { + while (1) { - if (lextable[pos + 1] == 0) // terminal marker + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; return pos; - - if ((lextable[pos] & 0x7f) == c) - return pos + (lextable[pos + 1] << 1); - - if (lextable[pos] & 0x80) - return -1; - - pos += 2; + } else { /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; + + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ + } } - return pos; } - int main(void) { int n = 0; @@ -330,6 +127,8 @@ int main(void) int walk; int saw; int y; + int j; + int pos = 0; while (n < sizeof(set) / sizeof(set[0])) { @@ -337,12 +136,18 @@ int main(void) walk = 0; prev = 0; + if (set[n][0] == '\0') { + n++; + continue; + } + while (set[n][m]) { saw = 0; for (y = 0; y < state[walk].count; y++) - if (state[walk].c[y] == set[n][m]) { /* exists */ - walk = state[walk].state[y]; /* go forward */ + if (state[walk].c[y] == set[n][m]) { + /* exists -- go forward */ + walk = state[walk].state[y]; saw = 1; break; } @@ -356,19 +161,14 @@ int main(void) state[walk].state[state[walk].count] = next; state[walk].count++; - -// if (set[n][m + 1] == '\0') /* terminal */ - walk = next++; + walk = next++; again: m++; } - state[walk].c[0] = n; + state[walk].c[0] = n++; state[walk].state[0] = 0; /* terminal marker */ state[walk].count = 1; - - n++; - } walk = 0; @@ -376,65 +176,141 @@ int main(void) state[n].bytepos = walk; walk += (2 * state[n].count); } -#if 0 + + /* compute everyone's position first */ + + pos = 0; + walk = 0; for (n = 0; n < next; n++) { - fprintf(stderr, "State %d\n", n); - for (m = 0; m < state[n].count; m++) - fprintf(stderr, "'%c' -> %d\n", state[n].c[m], state[n].state[m]); - fprintf(stderr, "(stop)\n"); + + state[n].real_pos = pos; + + for (m = 0; m < state[n].count; m++) { + + if (state[n].state[m] == 0) + pos += 2; /* terminal marker */ + else { /* c is a character */ + if ((state[state[n].state[m]].bytepos - + walk) == 2) + pos++; + else { + pos += 3; + if (m == state[n].count - 1) + pos++; /* fail */ + } + } + walk += 2; + } } -#endif walk = 0; + pos = 0; for (n = 0; n < next; n++) { - fprintf(stderr, "/* pos %d: state %d */\n", walk, n); for (m = 0; m < state[n].count; m++) { + + if (!m) + fprintf(stdout, "/* pos %04x: %3d */ ", + state[n].real_pos, n); + else + fprintf(stdout, " "); + y = state[n].c[m]; saw = state[n].state[m]; - if (m == state[n].count - 1) - y |= 0x80; /* last option */ + if (saw == 0) { // c is a terminal then - if (saw == 0) // c is a terminal then - fprintf(stderr, " 0x%02X, 0x00 /* terminal marker */, \n", y); - else { // c is a character and we need a byte delta - if ((state[saw].bytepos - walk) / 2 > 0xff) { - fprintf(stderr, "Tried to jump > 510 bytes ahead\n"); - return 1; + if (y > 0x7ff) { + fprintf(stderr, "terminal too big\n"); + return 2; } - prev = y &0x7f; - if (prev < 32 || prev > 126) - prev = '.'; - fprintf(stderr, " 0x%02X /* '%c' */, 0x%02X /* to pos %d state %d */,\n", y, prev, (state[saw].bytepos - walk) / 2, state[saw].bytepos, saw); + + fprintf(stdout, " 0x%02X, 0x%02X " + " " + "/* - terminal marker %2d - */,\n", + y >> 8, y & 0xff, y & 0x7f); + pos += 2; + walk += 2; + continue; + } + + /* c is a character */ + + prev = y &0x7f; + if (prev < 32 || prev > 126) + prev = '.'; + + + if ((state[saw].bytepos - walk) == 2) { + fprintf(stdout, " 0x%02X /* '%c' -> */,\n", + y | 0x80, prev); + pos++; + walk += 2; + continue; + } + + j = state[saw].real_pos - pos; + + if (j > 0xffff) { + fprintf(stderr, + "Jump > 64K bytes ahead (%d to %d)\n", + state[n].real_pos, state[saw].real_pos); + return 1; + } + fprintf(stdout, " 0x%02X /* '%c' */, 0x%02X, 0x%02X " + "/* (to 0x%04X state %3d) */,\n", + y, prev, + j & 0xff, j >> 8, + state[saw].real_pos, saw); + pos += 3; + + if (m == state[n].count - 1) { + fprintf(stdout, + " 0x%02X, /* fail */\n", + FAIL_CHAR); + pos++; /* fail */ } + walk += 2; } } - fprintf(stderr, "/* total size %d bytes */\n", walk); + fprintf(stdout, "/* total size %d bytes */\n", pos); + + /* + * Try to parse every legal input string + */ for (n = 0; n < sizeof(set) / sizeof(set[0]); n++) { walk = 0; m = 0; + y = -1; - fprintf(stderr, "Trying %s\n", set[n]); + if (set[n][0] == '\0') + continue; + + fprintf(stderr, " trying '%s'\n", set[n]); while (set[n][m]) { walk = lextable_decode(walk, set[n][m]); if (walk < 0) { fprintf(stderr, "failed\n"); - break; + return 3; } - if (lextable[walk + 1] == 0) { - fprintf(stderr, "decode: %d\n", lextable[walk] & 0x7f); + + if (lextable[walk] < FAIL_CHAR) { + y = (lextable[walk] << 8) + lextable[walk + 1]; break; } m++; } + + if (y != n) { + fprintf(stderr, "decode failed %d\n", y); + return 4; + } } + fprintf(stderr, "All decode OK\n"); + return 0; } - - - diff --git a/lib/output.c b/lib/output.c index 224a0f27a5..28e15a6d41 100644 --- a/lib/output.c +++ b/lib/output.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2013 Andy Green + * Copyright (C) 2010-2014 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,10 +21,6 @@ #include "private-libwebsockets.h" -#ifdef WIN32 -#include -#endif - static int libwebsocket_0405_frame_mask_generate(struct libwebsocket *wsi) { @@ -91,180 +87,132 @@ LWS_VISIBLE void lwsl_hexdump(void *vbuf, size_t len) #endif /* - * notice this returns number of bytes sent, or -1 + * notice this returns number of bytes consumed, or -1 */ int lws_issue_raw(struct libwebsocket *wsi, unsigned char *buf, size_t len) { struct libwebsocket_context *context = wsi->protocol->owning_server; int n; -#ifndef LWS_NO_EXTENSIONS + size_t real_len = len; int m; + + if (!len) + return 0; + /* just ignore sends after we cleared the truncation buffer */ + if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE && + !wsi->truncated_send_len) + return len; + + if (wsi->truncated_send_len && (buf < wsi->truncated_send_malloc || + buf > (wsi->truncated_send_malloc + + wsi->truncated_send_len + + wsi->truncated_send_offset))) { + lwsl_err("****** %x Sending new, pending truncated ...\n", wsi); + assert(0); + } - /* - * one of the extensions is carrying our data itself? Like mux? - */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - /* - * there can only be active extensions after handshake completed - * so we can rely on protocol being set already in here - */ - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, - wsi->active_extensions_user[n], &buf, len); - if (m < 0) { - lwsl_ext("Extension reports fatal error\n"); - return -1; - } - if (m) /* handled */ { -/* lwsl_ext("ext sent it\n"); */ - return m; - } + m = lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PACKET_TX_DO_SEND, &buf, len); + if (m < 0) + return -1; + if (m) /* handled */ { + n = m; + goto handle_truncated_send; } -#endif - if (!wsi->sock) - lwsl_warn("** error 0 sock but expected to send\n"); + if (wsi->sock < 0) + lwsl_warn("** error invalid sock but expected to send\n"); /* * nope, send it on the socket directly */ - -#if 0 - lwsl_debug(" TX: "); - lws_hexdump(buf, len); -#endif - lws_latency_pre(context, wsi); -#ifdef LWS_OPENSSL_SUPPORT - if (wsi->ssl) { - n = SSL_write(wsi->ssl, buf, len); - lws_latency(context, wsi, "SSL_write lws_issue_raw", n, n >= 0); - if (n < 0) { - lwsl_debug("ERROR writing to socket\n"); - return -1; - } - } else { -#endif - n = send(wsi->sock, buf, len, MSG_NOSIGNAL); - lws_latency(context, wsi, "send lws_issue_raw", n, n == len); - if (n < 0) { - lwsl_debug("ERROR writing len %d to skt %d\n", len, n); - return -1; - } -#ifdef LWS_OPENSSL_SUPPORT - } -#endif - return n; -} - -#ifdef LWS_NO_EXTENSIONS -int -lws_issue_raw_ext_access(struct libwebsocket *wsi, - unsigned char *buf, size_t len) -{ - return lws_issue_raw(wsi, buf, len); -} -#else -int -lws_issue_raw_ext_access(struct libwebsocket *wsi, - unsigned char *buf, size_t len) -{ - int ret; - struct lws_tokens eff_buf; - int m; - int n; + n = lws_ssl_capable_write(wsi, buf, len); + lws_latency(context, wsi, "send lws_issue_raw", n, n == len); - eff_buf.token = (char *)buf; - eff_buf.token_len = len; + switch (n) { + case LWS_SSL_CAPABLE_ERROR: + return -1; + case LWS_SSL_CAPABLE_MORE_SERVICE: + /* nothing got sent, not fatal, retry the whole thing later */ + n = 0; + break; + } +handle_truncated_send: /* - * while we have original buf to spill ourselves, or extensions report - * more in their pipeline + * we were already handling a truncated send? */ - - ret = 1; - while (ret == 1) { - - /* default to nobody has more to spill */ - - ret = 0; - - /* show every extension the new incoming data */ - - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PACKET_TX_PRESEND, - wsi->active_extensions_user[n], &eff_buf, 0); - if (m < 0) { - lwsl_ext("Extension: fatal error\n"); - return -1; - } - if (m) - /* - * at least one extension told us he has more - * to spill, so we will go around again after - */ - ret = 1; - } - - /* assuming they left us something to send, send it */ - - if (eff_buf.token_len) { - n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, - eff_buf.token_len); - if (n < 0) - return -1; - /* - * Keep amount spilled small to minimize chance of this - */ - if (n != eff_buf.token_len) { - lwsl_err("Unable to spill ext %d vs %d\n", - eff_buf.token_len, n); - return -1; + if (wsi->truncated_send_len) { + lwsl_info("***** %x partial send moved on by %d (vs %d)\n", + wsi, n, real_len); + wsi->truncated_send_offset += n; + wsi->truncated_send_len -= n; + + if (!wsi->truncated_send_len) { + lwsl_info("***** %x partial send completed\n", wsi); + /* done with it, but don't free it */ + n = real_len; + if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + lwsl_info("***** %x signalling to close now\n", wsi); + return -1; /* retry closing now */ } - } + /* always callback on writeable */ + libwebsocket_callback_on_writable( + wsi->protocol->owning_server, wsi); - lwsl_parser("written %d bytes to client\n", eff_buf.token_len); - - /* no extension has more to spill */ - - if (!ret) - break; - - /* we used up what we had */ + return n; + } - eff_buf.token = NULL; - eff_buf.token_len = 0; + if (n == real_len) + /* what we just sent went out cleanly */ + return n; + if (n && wsi->u.ws.clean_buffer) /* - * Did that leave the pipe choked? + * This buffer unaffected by extension rewriting. + * It means the user code is expected to deal with + * partial sends. (lws knows the header was already + * sent, so on next send will just resume sending + * payload) */ + return n; - if (!lws_send_pipe_choked(wsi)) - /* no we could add more */ - continue; - - lwsl_debug("choked\n"); + /* + * Newly truncated send. Buffer the remainder (it will get + * first priority next time the socket is writable) + */ + lwsl_info("***** %x new partial sent %d from %d total\n", + wsi, n, real_len); - /* - * Yes, he's choked. Don't spill the rest now get a callback - * when he is ready to send and take care of it there - */ - libwebsocket_callback_on_writable( - wsi->protocol->owning_server, wsi); - wsi->extension_data_pending = 1; - ret = 0; + /* + * - if we still have a suitable malloc lying around, use it + * - or, if too small, reallocate it + * - or, if no buffer, create it + */ + if (!wsi->truncated_send_malloc || + real_len - n > wsi->truncated_send_allocation) { + if (wsi->truncated_send_malloc) + free(wsi->truncated_send_malloc); + + wsi->truncated_send_allocation = real_len - n; + wsi->truncated_send_malloc = malloc(real_len - n); + if (!wsi->truncated_send_malloc) { + lwsl_err("truncated send: unable to malloc %d\n", + real_len - n); + return -1; + } } + wsi->truncated_send_offset = 0; + wsi->truncated_send_len = real_len - n; + memcpy(wsi->truncated_send_malloc, buf + n, real_len - n); + + /* since something buffered, force it to get another chance to send */ + libwebsocket_callback_on_writable(wsi->protocol->owning_server, wsi); - return len; + return real_len; } -#endif /** * libwebsocket_write() - Apply protocol then write data to client @@ -306,12 +254,10 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, unsigned char *dropmask = NULL; unsigned char is_masked_bit = 0; size_t orig_len = len; -#ifndef LWS_NO_EXTENSIONS struct lws_tokens eff_buf; - int m; -#endif - if (len == 0 && protocol != LWS_WRITE_CLOSE && protocol != LWS_WRITE_PING && protocol != LWS_WRITE_PONG) { + if (len == 0 && protocol != LWS_WRITE_CLOSE && + protocol != LWS_WRITE_PING && protocol != LWS_WRITE_PONG) { lwsl_warn("zero length libwebsocket_write attempt\n"); return 0; } @@ -324,8 +270,18 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, if (wsi->state != WSI_STATE_ESTABLISHED) return -1; -#ifndef LWS_NO_EXTENSIONS - /* give a change to the extensions to modify payload */ + /* if we are continuing a frame that already had its header done */ + + if (wsi->u.ws.inside_frame) + goto do_more_inside_frame; + + /* if he wants all partials buffered, never have a clean_buffer */ + wsi->u.ws.clean_buffer = !wsi->protocol->no_buffer_all_partial_tx; + + /* + * give a chance to the extensions to modify payload + * pre-TX mangling is not allowed to truncate + */ eff_buf.token = (char *)buf; eff_buf.token_len = len; @@ -335,24 +291,29 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, case LWS_WRITE_CLOSE: break; default: - - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PAYLOAD_TX, - wsi->active_extensions_user[n], &eff_buf, 0); - if (m < 0) - return -1; - } + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PAYLOAD_TX, &eff_buf, 0) < 0) + return -1; } + /* + * an extension did something we need to keep... for example, if + * compression extension, it has already updated its state according + * to this being issued + */ + if ((char *)buf != eff_buf.token) + /* + * extension recreated it: + * need to buffer this if not all sent + */ + wsi->u.ws.clean_buffer = 0; + buf = (unsigned char *)eff_buf.token; len = eff_buf.token_len; -#endif switch (wsi->ietf_spec_revision) { case 13: + if (masked7) { pre += 4; dropmask = &buf[0 - pre]; @@ -435,6 +396,8 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, break; } +do_more_inside_frame: + /* * Deal with masking if we are in client -> server direction and * the protocol demands it @@ -442,32 +405,27 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, if (wsi->mode == LWS_CONNMODE_WS_CLIENT) { - if (libwebsocket_0405_frame_mask_generate(wsi)) { - lwsl_err("lws_write: frame mask generation failed\n"); - return -1; - } + if (!wsi->u.ws.inside_frame) + if (libwebsocket_0405_frame_mask_generate(wsi)) { + lwsl_err("frame mask generation failed\n"); + return -1; + } /* * in v7, just mask the payload */ - for (n = 4; n < (int)len + 4; n++) - dropmask[n] = dropmask[n] ^ + if (dropmask) { /* never set if already inside frame */ + for (n = 4; n < (int)len + 4; n++) + dropmask[n] = dropmask[n] ^ wsi->u.ws.frame_masking_nonce_04[ (wsi->u.ws.frame_mask_index++) & 3]; - if (dropmask) /* copy the frame nonce into place */ - memcpy(dropmask, - wsi->u.ws.frame_masking_nonce_04, 4); + memcpy(dropmask, wsi->u.ws.frame_masking_nonce_04, 4); + } } send_raw: - -#if 0 - lwsl_debug("send %ld: ", len + post); - lwsl_hexdump(&buf[-pre], len + post); -#endif - switch (protocol) { case LWS_WRITE_CLOSE: /* lwsl_hexdump(&buf[-pre], len + post); */ @@ -480,6 +438,8 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, break; } + wsi->u.ws.inside_frame = 1; + /* * give any active extensions a chance to munge the buffer * before send. We pass in a pointer to an lws_tokens struct @@ -492,24 +452,59 @@ LWS_VISIBLE int libwebsocket_write(struct libwebsocket *wsi, unsigned char *buf, * used then so it is efficient. * * callback returns 1 in case it wants to spill more buffers + * + * This takes care of holding the buffer if send is incomplete, ie, + * if wsi->u.ws.clean_buffer is 0 (meaning an extension meddled with + * the buffer). If wsi->u.ws.clean_buffer is 1, it will instead + * return to the user code how much OF THE USER BUFFER was consumed. */ n = lws_issue_raw_ext_access(wsi, buf - pre, len + pre + post); - if (n < 0) + if (n <= 0) return n; - return orig_len - ((len - pre + post) -n ); + if (n == len + pre + post) { + /* everything in the buffer was handled (or rebuffered...) */ + wsi->u.ws.inside_frame = 0; + return orig_len; + } + + /* + * it is how many bytes of user buffer got sent... may be < orig_len + * in which case callback when writable has already been arranged + * and user code can call libwebsocket_write() again with the rest + * later. + */ + + return n - (pre + post); } LWS_VISIBLE int libwebsockets_serve_http_file_fragment( struct libwebsocket_context *context, struct libwebsocket *wsi) { - int n, m; + int n; + int m; while (!lws_send_pipe_choked(wsi)) { - n = read(wsi->u.http.fd, context->service_buffer, + + if (wsi->truncated_send_len) { + if (lws_issue_raw(wsi, wsi->truncated_send_malloc + + wsi->truncated_send_offset, + wsi->truncated_send_len) < 0) { + lwsl_info("closing from libwebsockets_serve_http_file_fragment\n"); + return -1; + } + continue; + } + + if (wsi->u.http.filepos == wsi->u.http.filelen) + goto all_sent; + + compatible_file_read(n, wsi->u.http.fd, context->service_buffer, sizeof(context->service_buffer)); - if (n > 0) { + if (n < 0) + return -1; /* caller will close */ + if (n) { m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP); if (m < 0) @@ -518,13 +513,11 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment( wsi->u.http.filepos += m; if (m != n) /* adjust for what was not sent */ - lseek(wsi->u.http.fd, m - n, SEEK_CUR); + compatible_file_seek_cur(wsi->u.http.fd, m - n); } - - if (n < 0) - return -1; /* caller will close */ - - if (wsi->u.http.filepos == wsi->u.http.filelen) { +all_sent: + if (!wsi->truncated_send_len && + wsi->u.http.filepos == wsi->u.http.filelen) { wsi->state = WSI_STATE_HTTP; if (wsi->protocol->callback) @@ -537,75 +530,42 @@ LWS_VISIBLE int libwebsockets_serve_http_file_fragment( } } - lwsl_notice("choked before able to send whole file (post)\n"); + lwsl_info("choked before able to send whole file (post)\n"); libwebsocket_callback_on_writable(context, wsi); return 0; /* indicates further processing must be done */ } -/** - * libwebsockets_serve_http_file() - Send a file back to the client using http - * @context: libwebsockets context - * @wsi: Websocket instance (available from user callback) - * @file: The file to issue over http - * @content_type: The http content type, eg, text/html - * - * 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 the wsi should be closed. - * ==0 indicates the file transfer is started and needs more service later, - * the wsi should be left alone. - */ - -LWS_VISIBLE int libwebsockets_serve_http_file(struct libwebsocket_context *context, - struct libwebsocket *wsi, const char *file, - const char *content_type) +LWS_VISIBLE int +lws_ssl_capable_read_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len) { - struct stat stat_buf; - unsigned char *p = context->service_buffer; - int ret = 0; + int n; - wsi->u.http.fd = open(file, O_RDONLY -#ifdef WIN32 - | _O_BINARY -#endif - ); - - if (wsi->u.http.fd < 1) { - lwsl_err("Unable to open '%s'\n", file); - p += sprintf((char *)p, - "HTTP/1.0 400 Bad\x0d\x0aServer: libwebsockets\x0d\x0a\x0d\x0a" - ); - wsi->u.http.fd = 0; - /* too small to care about partial, closing anyway */ - libwebsocket_write(wsi, context->service_buffer, - p - context->service_buffer, LWS_WRITE_HTTP); + n = recv(wsi->sock, buf, len, 0); + if (n >= 0) + return n; - return -1; - } + lwsl_warn("error on reading from skt\n"); + return LWS_SSL_CAPABLE_ERROR; +} - fstat(wsi->u.http.fd, &stat_buf); - wsi->u.http.filelen = stat_buf.st_size; - p += sprintf((char *)p, -"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a", - content_type); - p += sprintf((char *)p, - "Content-Length: %u\x0d\x0a\x0d\x0a", - (unsigned int)stat_buf.st_size); - - ret = libwebsocket_write(wsi, context->service_buffer, - p - context->service_buffer, LWS_WRITE_HTTP); - if (ret != (p - context->service_buffer)) { - lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer)); - return -1; - } +LWS_VISIBLE int +lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len) +{ + int n; + + n = send(wsi->sock, buf, len, 0); + if (n >= 0) + return n; - wsi->u.http.filepos = 0; - wsi->state = WSI_STATE_HTTP_ISSUING_FILE; + if (LWS_ERRNO == LWS_EAGAIN || + LWS_ERRNO == LWS_EWOULDBLOCK || + LWS_ERRNO == LWS_EINTR) { + if (LWS_ERRNO == LWS_EWOULDBLOCK) + lws_set_blocking_send(wsi); - return libwebsockets_serve_http_file_fragment(context, wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + lwsl_debug("ERROR writing len %d to skt %d\n", len, n); + return LWS_SSL_CAPABLE_ERROR; } - diff --git a/lib/parsers.c b/lib/parsers.c index c1d67303ef..ac9eda1257 100644 --- a/lib/parsers.c +++ b/lib/parsers.c @@ -21,290 +21,38 @@ #include "private-libwebsockets.h" -#ifdef WIN32 -#include -#endif - - unsigned char lextable[] = { - /* pos 0: state 0 */ - 0x47 /* 'G' */, 0x07 /* to pos 14 state 1 */, - 0x48 /* 'H' */, 0x0A /* to pos 22 state 5 */, - 0x43 /* 'C' */, 0x0F /* to pos 34 state 10 */, - 0x53 /* 'S' */, 0x19 /* to pos 56 state 21 */, - 0x55 /* 'U' */, 0x3F /* to pos 134 state 51 */, - 0x4F /* 'O' */, 0x46 /* to pos 150 state 59 */, - 0x8D /* '.' */, 0x52 /* to pos 176 state 72 */, - /* pos 14: state 1 */ - 0xC5 /* 'E' */, 0x01 /* to pos 16 state 2 */, - /* pos 16: state 2 */ - 0xD4 /* 'T' */, 0x01 /* to pos 18 state 3 */, - /* pos 18: state 3 */ - 0xA0 /* ' ' */, 0x01 /* to pos 20 state 4 */, - /* pos 20: state 4 */ - 0x80, 0x00 /* terminal marker */, - /* pos 22: state 5 */ - 0x6F /* 'o' */, 0x02 /* to pos 26 state 6 */, - 0xD4 /* 'T' */, 0x76 /* to pos 260 state 114 */, - /* pos 26: state 6 */ - 0xF3 /* 's' */, 0x01 /* to pos 28 state 7 */, - /* pos 28: state 7 */ - 0xF4 /* 't' */, 0x01 /* to pos 30 state 8 */, - /* pos 30: state 8 */ - 0xBA /* ':' */, 0x01 /* to pos 32 state 9 */, - /* pos 32: state 9 */ - 0x81, 0x00 /* terminal marker */, - /* pos 34: state 10 */ - 0xEF /* 'o' */, 0x01 /* to pos 36 state 11 */, - /* pos 36: state 11 */ - 0xEE /* 'n' */, 0x01 /* to pos 38 state 12 */, - /* pos 38: state 12 */ - 0xEE /* 'n' */, 0x01 /* to pos 40 state 13 */, - /* pos 40: state 13 */ - 0xE5 /* 'e' */, 0x01 /* to pos 42 state 14 */, - /* pos 42: state 14 */ - 0xE3 /* 'c' */, 0x01 /* to pos 44 state 15 */, - /* pos 44: state 15 */ - 0xF4 /* 't' */, 0x01 /* to pos 46 state 16 */, - /* pos 46: state 16 */ - 0xE9 /* 'i' */, 0x01 /* to pos 48 state 17 */, - /* pos 48: state 17 */ - 0xEF /* 'o' */, 0x01 /* to pos 50 state 18 */, - /* pos 50: state 18 */ - 0xEE /* 'n' */, 0x01 /* to pos 52 state 19 */, - /* pos 52: state 19 */ - 0xBA /* ':' */, 0x01 /* to pos 54 state 20 */, - /* pos 54: state 20 */ - 0x82, 0x00 /* terminal marker */, - /* pos 56: state 21 */ - 0xE5 /* 'e' */, 0x01 /* to pos 58 state 22 */, - /* pos 58: state 22 */ - 0xE3 /* 'c' */, 0x01 /* to pos 60 state 23 */, - /* pos 60: state 23 */ - 0xAD /* '-' */, 0x01 /* to pos 62 state 24 */, - /* pos 62: state 24 */ - 0xD7 /* 'W' */, 0x01 /* to pos 64 state 25 */, - /* pos 64: state 25 */ - 0xE5 /* 'e' */, 0x01 /* to pos 66 state 26 */, - /* pos 66: state 26 */ - 0xE2 /* 'b' */, 0x01 /* to pos 68 state 27 */, - /* pos 68: state 27 */ - 0xD3 /* 'S' */, 0x01 /* to pos 70 state 28 */, - /* pos 70: state 28 */ - 0xEF /* 'o' */, 0x01 /* to pos 72 state 29 */, - /* pos 72: state 29 */ - 0xE3 /* 'c' */, 0x01 /* to pos 74 state 30 */, - /* pos 74: state 30 */ - 0xEB /* 'k' */, 0x01 /* to pos 76 state 31 */, - /* pos 76: state 31 */ - 0xE5 /* 'e' */, 0x01 /* to pos 78 state 32 */, - /* pos 78: state 32 */ - 0xF4 /* 't' */, 0x01 /* to pos 80 state 33 */, - /* pos 80: state 33 */ - 0xAD /* '-' */, 0x01 /* to pos 82 state 34 */, - /* pos 82: state 34 */ - 0x4B /* 'K' */, 0x08 /* to pos 98 state 35 */, - 0x50 /* 'P' */, 0x10 /* to pos 116 state 42 */, - 0x44 /* 'D' */, 0x27 /* to pos 164 state 66 */, - 0x56 /* 'V' */, 0x2F /* to pos 182 state 75 */, - 0x4F /* 'O' */, 0x36 /* to pos 198 state 83 */, - 0x45 /* 'E' */, 0x3C /* to pos 212 state 90 */, - 0x41 /* 'A' */, 0x46 /* to pos 234 state 101 */, - 0xCE /* 'N' */, 0x4C /* to pos 248 state 108 */, - /* pos 98: state 35 */ - 0xE5 /* 'e' */, 0x01 /* to pos 100 state 36 */, - /* pos 100: state 36 */ - 0xF9 /* 'y' */, 0x01 /* to pos 102 state 37 */, - /* pos 102: state 37 */ - 0x31 /* '1' */, 0x03 /* to pos 108 state 38 */, - 0x32 /* '2' */, 0x04 /* to pos 112 state 40 */, - 0xBA /* ':' */, 0x25 /* to pos 180 state 74 */, - /* pos 108: state 38 */ - 0xBA /* ':' */, 0x01 /* to pos 110 state 39 */, - /* pos 110: state 39 */ - 0x83, 0x00 /* terminal marker */, - /* pos 112: state 40 */ - 0xBA /* ':' */, 0x01 /* to pos 114 state 41 */, - /* pos 114: state 41 */ - 0x84, 0x00 /* terminal marker */, - /* pos 116: state 42 */ - 0xF2 /* 'r' */, 0x01 /* to pos 118 state 43 */, - /* pos 118: state 43 */ - 0xEF /* 'o' */, 0x01 /* to pos 120 state 44 */, - /* pos 120: state 44 */ - 0xF4 /* 't' */, 0x01 /* to pos 122 state 45 */, - /* pos 122: state 45 */ - 0xEF /* 'o' */, 0x01 /* to pos 124 state 46 */, - /* pos 124: state 46 */ - 0xE3 /* 'c' */, 0x01 /* to pos 126 state 47 */, - /* pos 126: state 47 */ - 0xEF /* 'o' */, 0x01 /* to pos 128 state 48 */, - /* pos 128: state 48 */ - 0xEC /* 'l' */, 0x01 /* to pos 130 state 49 */, - /* pos 130: state 49 */ - 0xBA /* ':' */, 0x01 /* to pos 132 state 50 */, - /* pos 132: state 50 */ - 0x85, 0x00 /* terminal marker */, - /* pos 134: state 51 */ - 0xF0 /* 'p' */, 0x01 /* to pos 136 state 52 */, - /* pos 136: state 52 */ - 0xE7 /* 'g' */, 0x01 /* to pos 138 state 53 */, - /* pos 138: state 53 */ - 0xF2 /* 'r' */, 0x01 /* to pos 140 state 54 */, - /* pos 140: state 54 */ - 0xE1 /* 'a' */, 0x01 /* to pos 142 state 55 */, - /* pos 142: state 55 */ - 0xE4 /* 'd' */, 0x01 /* to pos 144 state 56 */, - /* pos 144: state 56 */ - 0xE5 /* 'e' */, 0x01 /* to pos 146 state 57 */, - /* pos 146: state 57 */ - 0xBA /* ':' */, 0x01 /* to pos 148 state 58 */, - /* pos 148: state 58 */ - 0x86, 0x00 /* terminal marker */, - /* pos 150: state 59 */ - 0xF2 /* 'r' */, 0x01 /* to pos 152 state 60 */, - /* pos 152: state 60 */ - 0xE9 /* 'i' */, 0x01 /* to pos 154 state 61 */, - /* pos 154: state 61 */ - 0xE7 /* 'g' */, 0x01 /* to pos 156 state 62 */, - /* pos 156: state 62 */ - 0xE9 /* 'i' */, 0x01 /* to pos 158 state 63 */, - /* pos 158: state 63 */ - 0xEE /* 'n' */, 0x01 /* to pos 160 state 64 */, - /* pos 160: state 64 */ - 0xBA /* ':' */, 0x01 /* to pos 162 state 65 */, - /* pos 162: state 65 */ - 0x87, 0x00 /* terminal marker */, - /* pos 164: state 66 */ - 0xF2 /* 'r' */, 0x01 /* to pos 166 state 67 */, - /* pos 166: state 67 */ - 0xE1 /* 'a' */, 0x01 /* to pos 168 state 68 */, - /* pos 168: state 68 */ - 0xE6 /* 'f' */, 0x01 /* to pos 170 state 69 */, - /* pos 170: state 69 */ - 0xF4 /* 't' */, 0x01 /* to pos 172 state 70 */, - /* pos 172: state 70 */ - 0xBA /* ':' */, 0x01 /* to pos 174 state 71 */, - /* pos 174: state 71 */ - 0x88, 0x00 /* terminal marker */, - /* pos 176: state 72 */ - 0x8A /* '.' */, 0x01 /* to pos 178 state 73 */, - /* pos 178: state 73 */ - 0x89, 0x00 /* terminal marker */, - /* pos 180: state 74 */ - 0x8A, 0x00 /* terminal marker */, - /* pos 182: state 75 */ - 0xE5 /* 'e' */, 0x01 /* to pos 184 state 76 */, - /* pos 184: state 76 */ - 0xF2 /* 'r' */, 0x01 /* to pos 186 state 77 */, - /* pos 186: state 77 */ - 0xF3 /* 's' */, 0x01 /* to pos 188 state 78 */, - /* pos 188: state 78 */ - 0xE9 /* 'i' */, 0x01 /* to pos 190 state 79 */, - /* pos 190: state 79 */ - 0xEF /* 'o' */, 0x01 /* to pos 192 state 80 */, - /* pos 192: state 80 */ - 0xEE /* 'n' */, 0x01 /* to pos 194 state 81 */, - /* pos 194: state 81 */ - 0xBA /* ':' */, 0x01 /* to pos 196 state 82 */, - /* pos 196: state 82 */ - 0x8B, 0x00 /* terminal marker */, - /* pos 198: state 83 */ - 0xF2 /* 'r' */, 0x01 /* to pos 200 state 84 */, - /* pos 200: state 84 */ - 0xE9 /* 'i' */, 0x01 /* to pos 202 state 85 */, - /* pos 202: state 85 */ - 0xE7 /* 'g' */, 0x01 /* to pos 204 state 86 */, - /* pos 204: state 86 */ - 0xE9 /* 'i' */, 0x01 /* to pos 206 state 87 */, - /* pos 206: state 87 */ - 0xEE /* 'n' */, 0x01 /* to pos 208 state 88 */, - /* pos 208: state 88 */ - 0xBA /* ':' */, 0x01 /* to pos 210 state 89 */, - /* pos 210: state 89 */ - 0x8C, 0x00 /* terminal marker */, - /* pos 212: state 90 */ - 0xF8 /* 'x' */, 0x01 /* to pos 214 state 91 */, - /* pos 214: state 91 */ - 0xF4 /* 't' */, 0x01 /* to pos 216 state 92 */, - /* pos 216: state 92 */ - 0xE5 /* 'e' */, 0x01 /* to pos 218 state 93 */, - /* pos 218: state 93 */ - 0xEE /* 'n' */, 0x01 /* to pos 220 state 94 */, - /* pos 220: state 94 */ - 0xF3 /* 's' */, 0x01 /* to pos 222 state 95 */, - /* pos 222: state 95 */ - 0xE9 /* 'i' */, 0x01 /* to pos 224 state 96 */, - /* pos 224: state 96 */ - 0xEF /* 'o' */, 0x01 /* to pos 226 state 97 */, - /* pos 226: state 97 */ - 0xEE /* 'n' */, 0x01 /* to pos 228 state 98 */, - /* pos 228: state 98 */ - 0xF3 /* 's' */, 0x01 /* to pos 230 state 99 */, - /* pos 230: state 99 */ - 0xBA /* ':' */, 0x01 /* to pos 232 state 100 */, - /* pos 232: state 100 */ - 0x8D, 0x00 /* terminal marker */, - /* pos 234: state 101 */ - 0xE3 /* 'c' */, 0x01 /* to pos 236 state 102 */, - /* pos 236: state 102 */ - 0xE3 /* 'c' */, 0x01 /* to pos 238 state 103 */, - /* pos 238: state 103 */ - 0xE5 /* 'e' */, 0x01 /* to pos 240 state 104 */, - /* pos 240: state 104 */ - 0xF0 /* 'p' */, 0x01 /* to pos 242 state 105 */, - /* pos 242: state 105 */ - 0xF4 /* 't' */, 0x01 /* to pos 244 state 106 */, - /* pos 244: state 106 */ - 0xBA /* ':' */, 0x01 /* to pos 246 state 107 */, - /* pos 246: state 107 */ - 0x8E, 0x00 /* terminal marker */, - /* pos 248: state 108 */ - 0xEF /* 'o' */, 0x01 /* to pos 250 state 109 */, - /* pos 250: state 109 */ - 0xEE /* 'n' */, 0x01 /* to pos 252 state 110 */, - /* pos 252: state 110 */ - 0xE3 /* 'c' */, 0x01 /* to pos 254 state 111 */, - /* pos 254: state 111 */ - 0xE5 /* 'e' */, 0x01 /* to pos 256 state 112 */, - /* pos 256: state 112 */ - 0xBA /* ':' */, 0x01 /* to pos 258 state 113 */, - /* pos 258: state 113 */ - 0x8F, 0x00 /* terminal marker */, - /* pos 260: state 114 */ - 0xD4 /* 'T' */, 0x01 /* to pos 262 state 115 */, - /* pos 262: state 115 */ - 0xD0 /* 'P' */, 0x01 /* to pos 264 state 116 */, - /* pos 264: state 116 */ - 0xAF /* '/' */, 0x01 /* to pos 266 state 117 */, - /* pos 266: state 117 */ - 0xB1 /* '1' */, 0x01 /* to pos 268 state 118 */, - /* pos 268: state 118 */ - 0xAE /* '.' */, 0x01 /* to pos 270 state 119 */, - /* pos 270: state 119 */ - 0xB1 /* '1' */, 0x01 /* to pos 272 state 120 */, - /* pos 272: state 120 */ - 0xA0 /* ' ' */, 0x01 /* to pos 274 state 121 */, - /* pos 274: state 121 */ - 0x90, 0x00 /* terminal marker */, - /* total size 276 bytes */ + #include "lextable.h" }; +#define FAIL_CHAR 0x08 + int lextable_decode(int pos, char c) { - while (pos >= 0) { - if (lextable[pos + 1] == 0) /* terminal marker */ - return pos; - if ((lextable[pos] & 0x7f) == c) - return pos + (lextable[pos + 1] << 1); + c = tolower(c); - if (lextable[pos] & 0x80) - return -1; + while (1) { + if (lextable[pos] & (1 << 7)) { /* 1-byte, fail on mismatch */ + if ((lextable[pos] & 0x7f) != c) + return -1; + /* fall thru */ + pos++; + if (lextable[pos] == FAIL_CHAR) + return -1; + return pos; + } + /* b7 = 0, end or 3-byte */ + if (lextable[pos] < FAIL_CHAR) /* terminal marker */ + return pos; - pos += 2; + if (lextable[pos] == c) /* goto */ + return pos + (lextable[pos + 1]) + + (lextable[pos + 2] << 8); + /* fall thru goto */ + pos += 3; + /* continue */ } - return pos; } int lws_allocate_header_table(struct libwebsocket *wsi) @@ -327,9 +75,8 @@ LWS_VISIBLE int lws_hdr_total_length(struct libwebsocket *wsi, enum lws_token_in int len = 0; n = wsi->u.hdr.ah->frag_index[h]; - if (n == 0) + if (!n) return 0; - do { len += wsi->u.hdr.ah->frags[n].len; n = wsi->u.hdr.ah->frags[n].next_frag_index; @@ -348,7 +95,7 @@ LWS_VISIBLE int lws_hdr_copy(struct libwebsocket *wsi, char *dest, int len, return -1; n = wsi->u.hdr.ah->frag_index[h]; - if (n == 0) + if (!n) return 0; do { @@ -404,12 +151,50 @@ int lws_hdr_simple_create(struct libwebsocket *wsi, return 0; } -int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) +static char char_to_hex(const char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; +} + +static int issue_char(struct libwebsocket *wsi, unsigned char c) +{ + if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { + lwsl_warn("excessive header content\n"); + return -1; + } + + if( wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len >= + wsi->u.hdr.current_token_limit) { + lwsl_warn("header %i exceeds limit\n", wsi->u.hdr.parser_state); + return 1; + }; + + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; + if (c) + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len++; + + return 0; +} + +int libwebsocket_parse( + struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned char c) { int n; switch (wsi->u.hdr.parser_state) { case WSI_TOKEN_GET_URI: + case WSI_TOKEN_POST_URI: + case WSI_TOKEN_OPTIONS_URI: case WSI_TOKEN_HOST: case WSI_TOKEN_CONNECTION: case WSI_TOKEN_KEY1: @@ -426,6 +211,22 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) case WSI_TOKEN_NONCE: case WSI_TOKEN_EXTENSIONS: case WSI_TOKEN_HTTP: + case WSI_TOKEN_HTTP_ACCEPT: + case WSI_TOKEN_HTTP_AC_REQUEST_HEADERS: + case WSI_TOKEN_HTTP_IF_MODIFIED_SINCE: + case WSI_TOKEN_HTTP_IF_NONE_MATCH: + case WSI_TOKEN_HTTP_ACCEPT_ENCODING: + case WSI_TOKEN_HTTP_ACCEPT_LANGUAGE: + case WSI_TOKEN_HTTP_PRAGMA: + case WSI_TOKEN_HTTP_CACHE_CONTROL: + case WSI_TOKEN_HTTP_AUTHORIZATION: + case WSI_TOKEN_HTTP_COOKIE: + case WSI_TOKEN_HTTP_CONTENT_LENGTH: + case WSI_TOKEN_HTTP_CONTENT_TYPE: + case WSI_TOKEN_HTTP_DATE: + case WSI_TOKEN_HTTP_RANGE: + case WSI_TOKEN_HTTP_REFERER: + lwsl_parser("WSI_TOK_(%d) '%c'\n", wsi->u.hdr.parser_state, c); @@ -435,12 +236,159 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) wsi->u.hdr.parser_state]].len && c == ' ') break; - /* special case space terminator for get-uri */ - if (wsi->u.hdr.parser_state == WSI_TOKEN_GET_URI && c == ' ') { - c = '\0'; - wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + if ((wsi->u.hdr.parser_state != WSI_TOKEN_GET_URI) && + (wsi->u.hdr.parser_state != WSI_TOKEN_POST_URI) && + (wsi->u.hdr.parser_state != WSI_TOKEN_OPTIONS_URI)) + goto check_eol; + + /* special URI processing... end at space */ + + if (c == ' ') { + /* enforce starting with / */ + if (!wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len) + if (issue_char(wsi, '/') < 0) + return -1; + + /* begin parsing HTTP version: */ + if (issue_char(wsi, '\0') < 0) + return -1; + wsi->u.hdr.parser_state = WSI_TOKEN_HTTP; + goto start_fragment; + } + + /* special URI processing... convert %xx */ + + switch (wsi->u.hdr.ues) { + case URIES_IDLE: + if (c == '%') { + wsi->u.hdr.ues = URIES_SEEN_PERCENT; + goto swallow; + } + break; + case URIES_SEEN_PERCENT: + if (char_to_hex(c) < 0) { + /* regurgitate */ + if (issue_char(wsi, '%') < 0) + return -1; + wsi->u.hdr.ues = URIES_IDLE; + /* continue on to assess c */ + break; + } + wsi->u.hdr.esc_stash = c; + wsi->u.hdr.ues = URIES_SEEN_PERCENT_H1; + goto swallow; + + case URIES_SEEN_PERCENT_H1: + if (char_to_hex(c) < 0) { + /* regurgitate */ + issue_char(wsi, '%'); + wsi->u.hdr.ues = URIES_IDLE; + /* regurgitate + assess */ + if (libwebsocket_parse(context, wsi, wsi->u.hdr.esc_stash) < 0) + return -1; + /* continue on to assess c */ + break; + } + c = (char_to_hex(wsi->u.hdr.esc_stash) << 4) | + char_to_hex(c); + wsi->u.hdr.ues = URIES_IDLE; + break; + } + + /* + * special URI processing... + * convert /.. or /... or /../ etc to / + * convert /./ to / + * convert // or /// etc to / + * leave /.dir or whatever alone + */ + + switch (wsi->u.hdr.ups) { + case URIPS_IDLE: + /* issue the first / always */ + if (c == '/') + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_SEEN_SLASH: + /* swallow subsequent slashes */ + if (c == '/') + goto swallow; + /* track and swallow the first . after / */ + if (c == '.') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT; + goto swallow; + } else + wsi->u.hdr.ups = URIPS_IDLE; + break; + case URIPS_SEEN_SLASH_DOT: + /* swallow second . */ + if (c == '.') { + /* + * back up one dir level if possible + * safe against header fragmentation because + * the method URI can only be in 1 fragment + */ + if (wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len > 2) { + wsi->u.hdr.ah->pos--; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len--; + do { + wsi->u.hdr.ah->pos--; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len--; + } while (wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len > 1 && + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos] != '/'); + } + wsi->u.hdr.ups = URIPS_SEEN_SLASH_DOT_DOT; + goto swallow; + } + /* change /./ to / */ + if (c == '/') { + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + goto swallow; + } + /* it was like /.dir ... regurgitate the . */ + wsi->u.hdr.ups = URIPS_IDLE; + issue_char(wsi, '.'); + break; + + case URIPS_SEEN_SLASH_DOT_DOT: + /* swallow prior .. chars and any subsequent . */ + if (c == '.') + goto swallow; + /* last issued was /, so another / == // */ + if (c == '/') + goto swallow; + else /* last we issued was / so SEEN_SLASH */ + wsi->u.hdr.ups = URIPS_SEEN_SLASH; + break; + case URIPS_ARGUMENTS: + /* leave them alone */ + break; + } + + if (c == '?') { /* start of URI arguments */ + /* seal off uri header */ + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = '\0'; + + /* move to using WSI_TOKEN_HTTP_URI_ARGS */ + wsi->u.hdr.ah->next_frag_index++; + wsi->u.hdr.ah->frags[ + wsi->u.hdr.ah->next_frag_index].offset = + wsi->u.hdr.ah->pos; + wsi->u.hdr.ah->frags[ + wsi->u.hdr.ah->next_frag_index].len = 0; + wsi->u.hdr.ah->frags[ + wsi->u.hdr.ah->next_frag_index].next_frag_index = 0; + + wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP_URI_ARGS] = + wsi->u.hdr.ah->next_frag_index; + + /* defeat normal uri path processing */ + wsi->u.hdr.ups = URIPS_ARGUMENTS; + goto swallow; } +check_eol: + /* bail at EOL */ if (wsi->u.hdr.parser_state != WSI_TOKEN_CHALLENGE && c == '\x0d') { @@ -449,15 +397,16 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) lwsl_parser("*\n"); } - if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { - lwsl_warn("excessive header content\n"); - return -1; - } - wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = c; - if (c) - wsi->u.hdr.ah->frags[ - wsi->u.hdr.ah->next_frag_index].len++; - + { + int issue_result = issue_char(wsi, c); + if (issue_result < 0) { + return -1; + } + else if(issue_result > 0) { + wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; + }; + }; +swallow: /* per-protocol end of headers management */ if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) @@ -474,7 +423,9 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) if (wsi->u.hdr.lextable_pos < 0) { /* this is not a header we know about */ if (wsi->u.hdr.ah->frag_index[WSI_TOKEN_GET_URI] || - wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP]) { + wsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI] || + wsi->u.hdr.ah->frag_index[WSI_TOKEN_OPTIONS_URI] || + wsi->u.hdr.ah->frag_index[WSI_TOKEN_HTTP]) { /* * altready had the method, no idea what * this crap is, ignore @@ -490,18 +441,25 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) lwsl_info("Unknown method - dropping\n"); return -1; } - if (lextable[wsi->u.hdr.lextable_pos + 1] == 0) { + if (lextable[wsi->u.hdr.lextable_pos] < FAIL_CHAR) { /* terminal state */ - n = lextable[wsi->u.hdr.lextable_pos] & 0x7f; + n = (lextable[wsi->u.hdr.lextable_pos] << 8) | lextable[wsi->u.hdr.lextable_pos + 1]; lwsl_parser("known hdr %d\n", n); - if (n == WSI_TOKEN_GET_URI && wsi->u.hdr.ah->frag_index[WSI_TOKEN_GET_URI]) { lwsl_warn("Duplicated GET\n"); return -1; + } else if (n == WSI_TOKEN_POST_URI && + wsi->u.hdr.ah->frag_index[WSI_TOKEN_POST_URI]) { + lwsl_warn("Duplicated POST\n"); + return -1; + } else if (n == WSI_TOKEN_OPTIONS_URI && + wsi->u.hdr.ah->frag_index[WSI_TOKEN_OPTIONS_URI]) { + lwsl_warn("Duplicated OPTIONS\n"); + return -1; } /* @@ -513,6 +471,15 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) wsi->u.hdr.parser_state = (enum lws_token_indexes) (WSI_TOKEN_GET_URI + n); + + if( context->token_limits ) { + wsi->u.hdr.current_token_limit = \ + context->token_limits->token_limit[wsi->u.hdr.parser_state]; + } + else { + wsi->u.hdr.current_token_limit = sizeof(wsi->u.hdr.ah->data); + }; + if (wsi->u.hdr.parser_state == WSI_TOKEN_CHALLENGE) goto set_parsing_complete; @@ -539,28 +506,27 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) if (!n) { /* first fragment */ wsi->u.hdr.ah->frag_index[wsi->u.hdr.parser_state] = wsi->u.hdr.ah->next_frag_index; - } else { /* continuation */ - while (wsi->u.hdr.ah->frags[n].next_frag_index) + break; + } + /* continuation */ + while (wsi->u.hdr.ah->frags[n].next_frag_index) n = wsi->u.hdr.ah->frags[n].next_frag_index; - wsi->u.hdr.ah->frags[n].next_frag_index = + wsi->u.hdr.ah->frags[n].next_frag_index = wsi->u.hdr.ah->next_frag_index; - if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { - lwsl_warn("excessive header content\n"); - return -1; - } - - wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = ' '; - wsi->u.hdr.ah->frags[ - wsi->u.hdr.ah->next_frag_index].len++; + if (wsi->u.hdr.ah->pos == sizeof(wsi->u.hdr.ah->data)) { + lwsl_warn("excessive header content\n"); + return -1; } + wsi->u.hdr.ah->data[wsi->u.hdr.ah->pos++] = ' '; + wsi->u.hdr.ah->frags[wsi->u.hdr.ah->next_frag_index].len++; break; - /* skipping arg part of a name we didn't recognize */ case WSI_TOKEN_SKIPPING: lwsl_parser("WSI_TOKEN_SKIPPING '%c'\n", c); + if (c == '\x0d') wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING_SAW_CR; break; @@ -574,6 +540,7 @@ int libwebsocket_parse(struct libwebsocket *wsi, unsigned char c) wsi->u.hdr.parser_state = WSI_TOKEN_SKIPPING; break; /* we're done, ignore anything else */ + case WSI_PARSING_COMPLETE: lwsl_parser("WSI_PARSING_COMPLETE '%c'\n", c); break; @@ -621,14 +588,6 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) int n; struct lws_tokens eff_buf; int ret = 0; -#ifndef LWS_NO_EXTENSIONS - int handled; - int m; -#endif - -#if 0 - lwsl_debug("RX: %02X ", c); -#endif switch (wsi->lws_rx_parse_state) { case LWS_RXPS_NEW: @@ -746,8 +705,13 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) wsi->lws_rx_parse_state = LWS_RXPS_07_COLLECT_FRAME_KEY_1; else - wsi->lws_rx_parse_state = + if (wsi->u.ws.rx_packet_length) + wsi->lws_rx_parse_state = LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; + else { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; + goto spill; + } break; } break; @@ -855,8 +819,10 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) wsi->lws_rx_parse_state = LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED; wsi->u.ws.frame_mask_index = 0; - if (wsi->u.ws.rx_packet_length == 0) + if (wsi->u.ws.rx_packet_length == 0) { + wsi->lws_rx_parse_state = LWS_RXPS_NEW; goto spill; + } break; @@ -955,10 +921,8 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) break; default: -#ifndef LWS_NO_EXTENSIONS lwsl_parser("passing opc %x up to exts\n", wsi->u.ws.opcode); - /* * It's something special we can't understand here. * Pass the payload up to the extension's parsing @@ -969,20 +933,9 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) LWS_SEND_BUFFER_PRE_PADDING]; eff_buf.token_len = wsi->u.ws.rx_user_buffer_head; - handled = 0; - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX, - wsi->active_extensions_user[n], - &eff_buf, 0); - if (m) - handled = 1; - } - - if (!handled) -#endif + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_EXTENDED_PAYLOAD_RX, + &eff_buf, 0) <= 0) /* not handle or fail */ lwsl_ext("ext opc opcode 0x%x unknown\n", wsi->u.ws.opcode); @@ -999,22 +952,11 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) eff_buf.token = &wsi->u.ws.rx_user_buffer[ LWS_SEND_BUFFER_PRE_PADDING]; eff_buf.token_len = wsi->u.ws.rx_user_buffer_head; -#ifndef LWS_NO_EXTENSIONS - for (n = 0; n < wsi->count_active_extensions; n++) { - m = wsi->active_extensions[n]->callback( - wsi->protocol->owning_server, - wsi->active_extensions[n], wsi, - LWS_EXT_CALLBACK_PAYLOAD_RX, - wsi->active_extensions_user[n], - &eff_buf, 0); - if (m < 0) { - lwsl_ext( - "Extension '%s' failed to handle payload!\n", - wsi->active_extensions[n]->name); - return -1; - } - } -#endif + + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PAYLOAD_RX, &eff_buf, 0) < 0) + return -1; + if (eff_buf.token_len > 0) { eff_buf.token[eff_buf.token_len] = '\0'; @@ -1044,56 +986,6 @@ libwebsocket_rx_sm(struct libwebsocket *wsi, unsigned char c) } -int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, - unsigned char *buf, size_t len) -{ - size_t n = 0; - int m; - -#if 0 - lwsl_parser("received %d byte packet\n", (int)len); - lwsl_hexdump(buf, len); -#endif - - /* let the rx protocol state machine have as much as it needs */ - - while (n < len) { - /* - * we were accepting input but now we stopped doing so - */ - if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { - /* his RX is flowcontrolled, don't send remaining now */ - if (!wsi->u.ws.rxflow_buffer) { - /* a new rxflow, buffer it and warn caller */ - lwsl_info("new rxflow input buffer len %d\n", - len - n); - wsi->u.ws.rxflow_buffer = - (unsigned char *)malloc(len - n); - wsi->u.ws.rxflow_len = len - n; - wsi->u.ws.rxflow_pos = 0; - memcpy(wsi->u.ws.rxflow_buffer, - buf + n, len - n); - } else - /* rxflow while we were spilling prev rxflow */ - lwsl_info("stalling in existing rxflow buf\n"); - - return 1; - } - - /* account for what we're using in rxflow buffer */ - if (wsi->u.ws.rxflow_buffer) - wsi->u.ws.rxflow_pos++; - - /* process the byte */ - m = libwebsocket_rx_sm(wsi, buf[n++]); - if (m < 0) - return -1; - } - - return 0; -} - - /** * libwebsockets_remaining_packet_payload() - Bytes to come before "overall" * rx packet is complete diff --git a/lib/pollfd.c b/lib/pollfd.c new file mode 100755 index 0000000000..3995a378ac --- /dev/null +++ b/lib/pollfd.c @@ -0,0 +1,239 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + */ + +#include "private-libwebsockets.h" + +int +insert_wsi_socket_into_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 }; + + if (context->fds_count >= context->max_fds) { + lwsl_err("Too many fds (%d)\n", context->max_fds); + return 1; + } + + if (wsi->sock >= context->max_fds) { + lwsl_err("Socket fd %d is too high (%d)\n", + wsi->sock, context->max_fds); + return 1; + } + + assert(wsi); + assert(wsi->sock >= 0); + + lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", + wsi, wsi->sock, context->fds_count); + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 0); + + context->lws_lookup[wsi->sock] = wsi; + wsi->position_in_fds_table = context->fds_count; + context->fds[context->fds_count].fd = wsi->sock; + context->fds[context->fds_count].events = LWS_POLLIN; + + lws_plat_insert_socket_into_fds(context, wsi); + + /* external POLL support via protocol 0 */ + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_ADD_POLL_FD, + wsi->user_space, (void *) &pa, 0); + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *)&pa, 0); + + return 0; +} + +int +remove_wsi_socket_from_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + int m; + struct libwebsocket_pollargs pa = { wsi->sock, 0, 0 }; + + lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE); + + if (!--context->fds_count) { + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *) &pa, 0); + goto do_ext; + } + + if (wsi->sock > context->max_fds) { + lwsl_err("Socket fd %d too high (%d)\n", + wsi->sock, context->max_fds); + return 1; + } + + lwsl_info("%s: wsi=%p, sock=%d, fds pos=%d\n", __func__, + wsi, wsi->sock, wsi->position_in_fds_table); + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_LOCK_POLL, + wsi->user_space, (void *)&pa, 0); + + m = wsi->position_in_fds_table; /* replace the contents for this */ + + /* have the last guy take up the vacant slot */ + context->fds[m] = context->fds[context->fds_count]; + + lws_plat_delete_socket_from_fds(context, wsi, m); + + /* + * end guy's fds_lookup entry remains unchanged + * (still same fd pointing to same wsi) + */ + /* end guy's "position in fds table" changed */ + context->lws_lookup[context->fds[context->fds_count].fd]-> + position_in_fds_table = m; + /* deletion guy's lws_lookup entry needs nuking */ + context->lws_lookup[wsi->sock] = NULL; + /* removed wsi has no position any more */ + wsi->position_in_fds_table = -1; + +do_ext: + /* remove also from external POLL support via protocol 0 */ + if (wsi->sock) { + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_DEL_POLL_FD, wsi->user_space, + (void *) &pa, 0); + } + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_UNLOCK_POLL, + wsi->user_space, (void *) &pa, 0); + return 0; +} + +int +lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or) +{ + struct libwebsocket_context *context = wsi->protocol->owning_server; + int tid; + int sampled_tid; + struct libwebsocket_pollfd *pfd; + struct libwebsocket_pollargs pa; + + pfd = &context->fds[wsi->position_in_fds_table]; + pa.fd = wsi->sock; + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0); + + pa.prev_events = pfd->events; + pa.events = pfd->events = (pfd->events & ~_and) | _or; + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_CHANGE_MODE_POLL_FD, + wsi->user_space, (void *) &pa, 0); + + /* + * if we changed something in this pollfd... + * ... and we're running in a different thread context + * than the service thread... + * ... and the service thread is waiting ... + * then cancel it to force a restart with our changed events + */ + if (pa.prev_events != pa.events) { + + if (lws_plat_change_pollfd(context, wsi, pfd)) { + lwsl_info("%s failed\n", __func__); + return 1; + } + + sampled_tid = context->service_tid; + if (sampled_tid) { + tid = context->protocols[0].callback(context, NULL, + LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0); + if (tid != sampled_tid) + libwebsocket_cancel_service(context); + } + } + + context->protocols[0].callback(context, wsi, + LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0); + + return 0; +} + + +/** + * libwebsocket_callback_on_writable() - Request a callback when this socket + * becomes able to be written to without + * blocking + * + * @context: libwebsockets context + * @wsi: Websocket connection instance to get callback for + */ + +LWS_VISIBLE int +libwebsocket_callback_on_writable(struct libwebsocket_context *context, + struct libwebsocket *wsi) +{ + if (lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0)) + return 1; + + if (wsi->position_in_fds_table < 0) { + lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock); + return -1; + } + + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + return -1; + + lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE); + + return 1; +} + +/** + * libwebsocket_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. + * + * @protocol: Protocol whose connections will get callbacks + */ + +LWS_VISIBLE int +libwebsocket_callback_on_writable_all_protocol( + const struct libwebsocket_protocols *protocol) +{ + struct libwebsocket_context *context = protocol->owning_server; + int n; + struct libwebsocket *wsi; + + for (n = 0; n < context->fds_count; n++) { + wsi = context->lws_lookup[context->fds[n].fd]; + if (!wsi) + continue; + if (wsi->protocol == protocol) + libwebsocket_callback_on_writable(context, wsi); + } + + return 0; +} diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index d7b6d0b813..87ade89d3c 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -23,58 +23,92 @@ #ifdef CMAKE_BUILD #include "lws_config.h" #else -#ifdef WIN32 +#if defined(WIN32) || defined(_WIN32) #define inline __inline -#else +#else /* not WIN32 */ #include "config.h" -#endif -#endif -#if _MSC_VER > 1000 || defined(_WIN32) -#else -#include -#include +#endif /* not WIN32 */ +#endif /* not CMAKE */ + +#ifdef HAVE_SYS_TYPES_H +#include #endif + #include #include #include +#include #include -#include -#include -#include #include -#ifdef __MINGW64__ -#else -#ifdef __MINGW32__ -#elif _MSC_VER > 1000 || defined(_WIN32) -#else -#include -#endif -#endif #include +#ifdef HAVE_SYS_STAT_H #include +#endif -#ifdef WIN32 +#if defined(WIN32) || defined(_WIN32) #define LWS_NO_DAEMONIZE -#ifndef EWOULDBLOCK -#define EWOULDBLOCK EAGAIN +#define LWS_ERRNO WSAGetLastError() +#define LWS_EAGAIN WSAEWOULDBLOCK +#define LWS_EALREADY WSAEALREADY +#define LWS_EINPROGRESS WSAEINPROGRESS +#define LWS_EINTR WSAEINTR +#define LWS_EISCONN WSAEISCONN +#define LWS_EWOULDBLOCK WSAEWOULDBLOCK +#define LWS_POLLHUP (FD_CLOSE) +#define LWS_POLLIN (FD_READ | FD_ACCEPT) +#define LWS_POLLOUT (FD_WRITE) +#define MSG_NOSIGNAL 0 +#define SHUT_RDWR SD_BOTH +#define SOL_TCP IPPROTO_TCP + +#define compatible_close(fd) closesocket(fd) +#define compatible_file_close(fd) CloseHandle(fd) +#define compatible_file_seek_cur(fd, offset) SetFilePointer(fd, offset, NULL, FILE_CURRENT) +#define compatible_file_read(amount, fd, buf, len) {\ + DWORD _amount; \ + if (!ReadFile(fd, buf, len, &_amount, NULL)) \ + amount = -1; \ + else \ + amount = _amount; \ + } +#define lws_set_blocking_send(wsi) wsi->sock_send_blocking = TRUE +#include +#include +#include +#ifdef HAVE_IN6ADDR_H +#include #endif +#include -#define compatible_close(fd) closesocket(fd); -#ifdef __MINGW64__ -#else -#ifdef __MINGW32__ -#else -#include +#ifndef __func__ +#define __func__ __FUNCTION__ #endif + +#ifdef _WIN32_WCE +#define vsnprintf _vsnprintf #endif -#include -#include -#include -#else + +#define LWS_INVALID_FILE INVALID_HANDLE_VALUE +#else /* not windows --> */ +#include +#include +#include +#include +#include +#include #include #include +#ifdef LWS_BUILTIN_GETIFADDRS + #include +#else + #include +#endif +#include +#include +#include +#include #ifndef LWS_NO_FORK #ifdef HAVE_SYS_PRCTL_H #include @@ -83,20 +117,48 @@ #include #include #include - #include +#ifdef LWS_USE_LIBEV +#include +#endif /* LWS_USE_LIBEV */ + #include #include -#define compatible_close(fd) close(fd); +#define LWS_ERRNO errno +#define LWS_EAGAIN EAGAIN +#define LWS_EALREADY EALREADY +#define LWS_EINPROGRESS EINPROGRESS +#define LWS_EINTR EINTR +#define LWS_EISCONN EISCONN +#define LWS_EWOULDBLOCK EWOULDBLOCK +#define LWS_INVALID_FILE -1 +#define LWS_POLLHUP (POLLHUP|POLLERR) +#define LWS_POLLIN (POLLIN) +#define LWS_POLLOUT (POLLOUT) +#define compatible_close(fd) close(fd) +#define compatible_file_close(fd) close(fd) +#define compatible_file_seek_cur(fd, offset) lseek(fd, offset, SEEK_CUR) +#define compatible_file_read(amount, fd, buf, len) \ + amount = read(fd, buf, len); +#define lws_set_blocking_send(wsi) +#endif + +#ifndef HAVE_BZERO +#define bzero(b, len) (memset((b), '\0', (len)), (void) 0) +#endif + +#ifndef HAVE_STRERROR +#define strerror(x) "" #endif +unsigned char * +ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); + #ifdef LWS_OPENSSL_SUPPORT #ifdef USE_CYASSL #include #include -unsigned char * -ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); #else #include #include @@ -108,6 +170,54 @@ ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); #include "libwebsockets.h" +#if defined(WIN32) || defined(_WIN32) + +#ifndef BIG_ENDIAN +#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ +#endif +#ifndef LITTLE_ENDIAN +#define LITTLE_ENDIAN 1234 +#endif +#ifndef BYTE_ORDER +#define BYTE_ORDER LITTLE_ENDIAN +#endif +typedef unsigned __int64 u_int64_t; + +#undef __P +#ifndef __P +#if __STDC__ +#define __P(protos) protos +#else +#define __P(protos) () +#endif +#endif + +#else + +#include +#include +#include + +#if defined(__APPLE__) +#include +#elif defined(__FreeBSD__) +#include +#elif defined(__linux__) +#include +#endif + +#if !defined(BYTE_ORDER) +# define BYTE_ORDER __BYTE_ORDER +#endif +#if !defined(LITTLE_ENDIAN) +# define LITTLE_ENDIAN __LITTLE_ENDIAN +#endif +#if !defined(BIG_ENDIAN) +# define BIG_ENDIAN __BIG_ENDIAN +#endif + +#endif + /* * Mac OSX as well as iOS do not define the MSG_NOSIGNAL flag, * but happily have something equivalent in the SO_NOSIGPIPE flag. @@ -173,11 +283,23 @@ enum lws_connection_states { WSI_STATE_HTTP, WSI_STATE_HTTP_ISSUING_FILE, WSI_STATE_HTTP_HEADERS, + WSI_STATE_HTTP_BODY, WSI_STATE_DEAD_SOCKET, WSI_STATE_ESTABLISHED, WSI_STATE_CLIENT_UNCONNECTED, WSI_STATE_RETURNED_CLOSE_ALREADY, WSI_STATE_AWAITING_CLOSE_ACK, + WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE, +}; + +enum http_version { + HTTP_VERSION_1_0, + HTTP_VERSION_1_1, +}; + +enum http_connection_type { + HTTP_CONNECTION_CLOSE, + HTTP_CONNECTION_KEEP_ALIVE }; enum lws_rx_parse_state { @@ -221,8 +343,11 @@ enum connection_mode { LWS_CONNMODE_SSL_ACK_PENDING, /* transient modes */ + LWS_CONNMODE_WS_CLIENT_WAITING_CONNECT, LWS_CONNMODE_WS_CLIENT_WAITING_PROXY_REPLY, LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE, + LWS_CONNMODE_WS_CLIENT_ISSUE_HANDSHAKE2, + LWS_CONNMODE_WS_CLIENT_WAITING_SSL, LWS_CONNMODE_WS_CLIENT_WAITING_SERVER_REPLY, LWS_CONNMODE_WS_CLIENT_WAITING_EXTENSION_CONNECT, LWS_CONNMODE_WS_CLIENT_PENDING_CANDIDATE_CHILD, @@ -239,17 +364,38 @@ enum { struct libwebsocket_protocols; struct libwebsocket; +#ifdef LWS_USE_LIBEV +struct lws_io_watcher { + struct ev_io watcher; + struct libwebsocket_context* context; +}; + +struct lws_signal_watcher { + struct ev_signal watcher; + struct libwebsocket_context* context; +}; +#endif /* LWS_USE_LIBEV */ + struct libwebsocket_context { - struct pollfd *fds; +#ifdef _WIN32 + WSAEVENT *events; +#endif + struct libwebsocket_pollfd *fds; struct libwebsocket **lws_lookup; /* fd to wsi */ int fds_count; +#ifdef LWS_USE_LIBEV + struct ev_loop* io_loop; + struct lws_io_watcher w_accept; + struct lws_signal_watcher w_sigint; +#endif /* LWS_USE_LIBEV */ int max_fds; int listen_port; + const char *iface; char http_proxy_address[128]; char canonical_hostname[128]; unsigned int http_proxy_port; unsigned int options; - unsigned long last_timeout_check_s; + time_t last_timeout_check_s; /* * usable by anything in the service code, but only if the scope @@ -266,6 +412,17 @@ struct libwebsocket_context { int listen_service_fd; int listen_service_extraseen; + /* + * set to the Thread ID that's doing the service loop just before entry + * to poll indicates service thread likely idling in poll() + * volatile because other threads may check it as part of processing + * for pollfd event change. + */ + volatile int service_tid; +#ifndef _WIN32 + int dummy_pipe_fds[2]; +#endif + int ka_time; int ka_probes; int ka_interval; @@ -277,6 +434,7 @@ struct libwebsocket_context { #ifdef LWS_OPENSSL_SUPPORT int use_ssl; + int allow_non_ssl_on_ssl_port; SSL_CTX *ssl_ctx; SSL_CTX *ssl_client_ctx; #endif @@ -285,9 +443,59 @@ struct libwebsocket_context { #ifndef LWS_NO_EXTENSIONS struct libwebsocket_extension *extensions; #endif + struct lws_token_limits *token_limits; void *user_space; }; +enum { + LWS_EV_READ = (1 << 0), + LWS_EV_WRITE = (1 << 1), + LWS_EV_START = (1 << 2), + LWS_EV_STOP = (1 << 3), +}; + +#ifdef LWS_USE_LIBEV +#define LWS_LIBEV_ENABLED(context) (context->options & LWS_SERVER_OPTION_LIBEV) +LWS_EXTERN void lws_feature_status_libev(struct lws_context_creation_info *info); +LWS_EXTERN void +lws_libev_accept(struct libwebsocket_context *context, + struct libwebsocket *new_wsi, int accept_fd); +LWS_EXTERN void +lws_libev_io(struct libwebsocket_context *context, + struct libwebsocket *wsi, int flags); +LWS_EXTERN int +lws_libev_init_fd_table(struct libwebsocket_context *context); +LWS_EXTERN void +lws_libev_run(struct libwebsocket_context *context); +#else +#define LWS_LIBEV_ENABLED(context) (0) +#define lws_feature_status_libev(_a) \ + lwsl_notice("libev support not compiled in\n") +#define lws_libev_accept(_a, _b, _c) ((void) 0) +#define lws_libev_io(_a, _b, _c) ((void) 0) +#define lws_libev_init_fd_table(_a) (0) +#define lws_libev_run(_a) ((void) 0) +#endif + +#ifdef LWS_USE_IPV6 +#define LWS_IPV6_ENABLED(context) (!(context->options & LWS_SERVER_OPTION_DISABLE_IPV6)) +#else +#define LWS_IPV6_ENABLED(context) (0) +#endif + +enum uri_path_states { + URIPS_IDLE, + URIPS_SEEN_SLASH, + URIPS_SEEN_SLASH_DOT, + URIPS_SEEN_SLASH_DOT_DOT, + URIPS_ARGUMENTS, +}; + +enum uri_esc_states { + URIES_IDLE, + URIES_SEEN_PERCENT, + URIES_SEEN_PERCENT_H1, +}; /* * This is totally opaque to code using the library. It's exported as a @@ -295,12 +503,6 @@ struct libwebsocket_context { * other APIs to get information out of it. */ -struct _lws_http_mode_related { - int fd; - unsigned long filepos; - unsigned long filelen; -}; - struct lws_fragments { unsigned short offset; unsigned short len; @@ -319,10 +521,30 @@ struct allocated_headers { #endif }; +struct _lws_http_mode_related { + struct allocated_headers *ah; /* mirroring _lws_header_related */ +#if defined(WIN32) || defined(_WIN32) + HANDLE fd; +#else + int fd; +#endif + unsigned long filepos; + unsigned long filelen; + + enum http_version request_version; + enum http_connection_type connection_type; + int content_length; + int content_remain; +}; + struct _lws_header_related { struct allocated_headers *ah; short lextable_pos; + unsigned short current_token_limit; unsigned char parser_state; /* enum lws_token_indexes */ + enum uri_path_states ups; + enum uri_esc_states ues; + char esc_stash; }; struct _lws_websocket_related { @@ -342,12 +564,18 @@ struct _lws_websocket_related { int rxflow_pos; unsigned int rxflow_change_to:2; unsigned int this_frame_masked:1; + unsigned int inside_frame:1; /* next write will be more of frame */ + unsigned int clean_buffer:1; /* buffer not rewritten by extension */ }; struct libwebsocket { /* lifetime members */ +#ifdef LWS_USE_LIBEV + struct lws_io_watcher w_read; + struct lws_io_watcher w_write; +#endif /* LWS_USE_LIBEV */ const struct libwebsocket_protocols *protocol; #ifndef LWS_NO_EXTENSIONS struct libwebsocket_extension * @@ -364,9 +592,10 @@ struct libwebsocket { char rx_frame_type; /* enum libwebsocket_write_protocol */ unsigned int hdr_parsing_completed:1; + unsigned int user_space_externally_allocated:1; char pending_timeout; /* enum pending_timeout */ - unsigned long pending_timeout_limit; + time_t pending_timeout_limit; int sock; int position_in_fds_table; @@ -375,6 +604,12 @@ struct libwebsocket { unsigned long latency_start; #endif + /* truncated send handling */ + unsigned char *truncated_send_malloc; /* non-NULL means buffering in progress */ + unsigned int truncated_send_allocation; /* size of malloc */ + unsigned int truncated_send_offset; /* where we are in terms of spilling */ + unsigned int truncated_send_len; /* how much is buffered */ + void *user_space; /* members with mutually exclusive lifetimes are unionized */ @@ -390,12 +625,22 @@ struct libwebsocket { BIO *client_bio; unsigned int use_ssl:2; #endif + +#ifdef _WIN32 + BOOL sock_send_blocking; +#endif }; +LWS_EXTERN int log_level; + LWS_EXTERN void libwebsocket_close_and_free_session(struct libwebsocket_context *context, struct libwebsocket *wsi, enum lws_close_status); +LWS_EXTERN int +remove_wsi_socket_from_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi); + #ifndef LWS_LATENCY static inline void lws_latency(struct libwebsocket_context *context, struct libwebsocket *wsi, const char *action, @@ -414,11 +659,8 @@ LWS_EXTERN int libwebsocket_client_rx_sm(struct libwebsocket *wsi, unsigned char c); LWS_EXTERN int -libwebsocket_parse(struct libwebsocket *wsi, unsigned char c); - -LWS_EXTERN int -libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, - unsigned char *buf, size_t len); +libwebsocket_parse(struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned char c); LWS_EXTERN int lws_b64_selftest(void); @@ -439,7 +681,7 @@ libwebsocket_service_timeout_check(struct libwebsocket_context *context, struct libwebsocket *wsi, unsigned int sec); LWS_EXTERN struct libwebsocket * -__libwebsocket_client_connect_2(struct libwebsocket_context *context, +libwebsocket_client_connect_2(struct libwebsocket_context *context, struct libwebsocket *wsi); LWS_EXTERN struct libwebsocket * @@ -451,17 +693,34 @@ libwebsockets_generate_client_handshake(struct libwebsocket_context *context, LWS_EXTERN int lws_handle_POLLOUT_event(struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd); + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd); +/* + * EXTENSIONS + */ + #ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE void +lws_context_init_extensions(struct lws_context_creation_info *info, + struct libwebsocket_context *context); LWS_EXTERN int lws_any_extension_handled(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_extension_callback_reasons r, void *v, size_t len); -LWS_EXTERN void * -lws_get_extension_user_matching_ext(struct libwebsocket *wsi, - struct libwebsocket_extension *ext); +LWS_EXTERN int +lws_ext_callback_for_each_active(struct libwebsocket *wsi, int reason, + void *buf, int len); +LWS_EXTERN int +lws_ext_callback_for_each_extension_type( + struct libwebsocket_context *context, struct libwebsocket *wsi, + int reason, void *arg, int len); +#else +#define lws_any_extension_handled(_a, _b, _c, _d, _e) (0) +#define lws_ext_callback_for_each_active(_a, _b, _c, _d) (0) +#define lws_ext_callback_for_each_extension_type(_a, _b, _c, _d, _e) (0) +#define lws_issue_raw_ext_access lws_issue_raw +#define lws_context_init_extensions(_a, _b) #endif LWS_EXTERN int @@ -486,7 +745,7 @@ user_callback_handle_rxflow(callback_function, void *in, size_t len); LWS_EXTERN int -lws_set_socket_options(struct libwebsocket_context *context, int fd); +lws_plat_set_socket_options(struct libwebsocket_context *context, int fd); LWS_EXTERN int lws_allocate_header_table(struct libwebsocket *wsi); @@ -501,25 +760,164 @@ lws_hdr_simple_create(struct libwebsocket *wsi, LWS_EXTERN int libwebsocket_ensure_user_space(struct libwebsocket *wsi); +LWS_EXTERN int +lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or); + #ifndef LWS_NO_SERVER +int lws_context_init_server(struct lws_context_creation_info *info, + struct libwebsocket_context *context); LWS_EXTERN int handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi); +LWS_EXTERN int +libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, + unsigned char *buf, size_t len); +LWS_EXTERN void +lws_server_get_canonical_hostname(struct libwebsocket_context *context, + struct lws_context_creation_info *info); +#else +#define lws_context_init_server(_a, _b) (0) +#define libwebsocket_interpret_incoming_packet(_a, _b, _c) (0) +#define lws_server_get_canonical_hostname(_a, _b) #endif #ifndef LWS_NO_DAEMONIZE LWS_EXTERN int get_daemonize_pid(); +#else +#define get_daemonize_pid() (0) #endif -extern int interface_to_sa(const char *ifname, - struct sockaddr_in *addr, size_t addrlen); +LWS_EXTERN int interface_to_sa(struct libwebsocket_context *context, + const char *ifname, struct sockaddr_in *addr, size_t addrlen); -#ifndef LWS_OPENSSL_SUPPORT +LWS_EXTERN void lwsl_emit_stderr(int level, const char *line); +#ifdef _WIN32 +LWS_EXTERN HANDLE lws_plat_open_file(const char* filename, unsigned long* filelen); +#else +LWS_EXTERN int lws_plat_open_file(const char* filename, unsigned long* filelen); +#endif + +enum lws_ssl_capable_status { + LWS_SSL_CAPABLE_ERROR = -1, + LWS_SSL_CAPABLE_MORE_SERVICE = -2, +}; + +#ifndef LWS_OPENSSL_SUPPORT +#define LWS_SSL_ENABLED(context) (0) unsigned char * -ws_SHA1(const unsigned char *d, size_t n, unsigned char *md); +SHA1(const unsigned char *d, size_t n, unsigned char *md); +#define lws_context_init_server_ssl(_a, _b) (0) +#define lws_ssl_destroy(_a) +#define lws_context_init_http2_ssl(_a) +#define lws_ssl_pending(_a) (0) +#define lws_ssl_capable_read lws_ssl_capable_read_no_ssl +#define lws_ssl_capable_write lws_ssl_capable_write_no_ssl +#define lws_server_socket_service_ssl(_a, _b, _c, _d, _e) (0) +#define lws_ssl_close(_a) (0) +#define lws_ssl_context_destroy(_a) +#else +#define LWS_SSL_ENABLED(context) (context->use_ssl) +LWS_EXTERN int lws_ssl_pending(struct libwebsocket *wsi); +LWS_EXTERN int openssl_websocket_private_data_index; +LWS_EXTERN int +lws_ssl_capable_read(struct libwebsocket *wsi, unsigned char *buf, int len); + +LWS_EXTERN int +lws_ssl_capable_write(struct libwebsocket *wsi, unsigned char *buf, int len); +LWS_EXTERN int +lws_server_socket_service_ssl(struct libwebsocket_context *context, + struct libwebsocket **wsi, struct libwebsocket *new_wsi, + int accept_fd, struct libwebsocket_pollfd *pollfd); +LWS_EXTERN int +lws_ssl_close(struct libwebsocket *wsi); +LWS_EXTERN void +lws_ssl_context_destroy(struct libwebsocket_context *context); +#ifndef LWS_NO_SERVER +LWS_EXTERN int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct libwebsocket_context *context); +#else +#define lws_context_init_server_ssl(_a, _b) (0) +#endif +LWS_EXTERN void +lws_ssl_destroy(struct libwebsocket_context *context); + +/* HTTP2-related */ +#ifdef LWS_USE_HTTP2 +LWS_EXTERN void +lws_context_init_http2_ssl(struct libwebsocket_context *context); #else +#define lws_context_init_http2_ssl(_a) +#endif +#endif -LWS_EXTERN int openssl_websocket_private_data_index; +LWS_EXTERN int +lws_ssl_capable_read_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len); + +LWS_EXTERN int +lws_ssl_capable_write_no_ssl(struct libwebsocket *wsi, unsigned char *buf, int len); +#ifndef LWS_NO_CLIENT + LWS_EXTERN int lws_client_socket_service( + struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd); +#ifdef LWS_OPENSSL_SUPPORT + LWS_EXTERN int lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct libwebsocket_context *context); +#else + #define lws_context_init_client_ssl(_a, _b) (0) +#endif + LWS_EXTERN int lws_handshake_client(struct libwebsocket *wsi, unsigned char **buf, size_t len); + LWS_EXTERN void + libwebsockets_decode_ssl_error(void); +#else +#define lws_context_init_client_ssl(_a, _b) (0) +#define lws_handshake_client(_a, _b, _c) (0) +#endif +#ifndef LWS_NO_SERVER + LWS_EXTERN int lws_server_socket_service( + struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd); + LWS_EXTERN int _libwebsocket_rx_flow_control(struct libwebsocket *wsi); + LWS_EXTERN int lws_handshake_server(struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned char **buf, size_t len); +#else +#define lws_server_socket_service(_a, _b, _c) (0) +#define _libwebsocket_rx_flow_control(_a) (0) +#define lws_handshake_server(_a, _b, _c, _d) (0) #endif + +/* + * lws_plat_ + */ +LWS_EXTERN void +lws_plat_delete_socket_from_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi, int m); +LWS_EXTERN void +lws_plat_insert_socket_into_fds(struct libwebsocket_context *context, + struct libwebsocket *wsi); +LWS_EXTERN void +lws_plat_service_periodic(struct libwebsocket_context *context); + +LWS_EXTERN int +lws_plat_change_pollfd(struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pfd); +LWS_EXTERN int +lws_plat_context_early_init(void); +LWS_EXTERN void +lws_plat_context_early_destroy(struct libwebsocket_context *context); +LWS_EXTERN void +lws_plat_context_late_destroy(struct libwebsocket_context *context); +LWS_EXTERN int +lws_poll_listen_fd(struct libwebsocket_pollfd *fd); +LWS_EXTERN int +lws_plat_service(struct libwebsocket_context *context, int timeout_ms); +LWS_EXTERN int +lws_plat_init_fd_tables(struct libwebsocket_context *context); +LWS_EXTERN void +lws_plat_drop_app_privileges(struct lws_context_creation_info *info); +LWS_EXTERN unsigned long long +time_in_microseconds(void); +LWS_EXTERN const char * +lws_plat_inet_ntop(int af, const void *src, char *dst, int cnt); diff --git a/lib/server-handshake.c b/lib/server-handshake.c index 536ce8ccb4..56cf9a13e4 100644 --- a/lib/server-handshake.c +++ b/lib/server-handshake.c @@ -22,11 +22,142 @@ #include "private-libwebsockets.h" #define LWS_CPYAPP(ptr, str) { strcpy(ptr, str); ptr += strlen(str); } +#ifndef LWS_NO_EXTENSIONS +LWS_VISIBLE int +lws_extension_server_handshake(struct libwebsocket_context *context, + struct libwebsocket *wsi, char **p) +{ + int n; + char *c; + char ext_name[128]; + struct libwebsocket_extension *ext; + int ext_count = 0; + int more = 1; -/* - * Perform the newer BASE64-encoded handshake scheme - */ + /* + * Figure out which extensions the client has that we want to + * enable on this connection, and give him back the list + */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) + return 0; + + /* + * break down the list of client extensions + * and go through them + */ + + if (lws_hdr_copy(wsi, (char *)context->service_buffer, + sizeof(context->service_buffer), + WSI_TOKEN_EXTENSIONS) < 0) + return 1; + + c = (char *)context->service_buffer; + lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); + wsi->count_active_extensions = 0; + n = 0; + while (more) { + + if (*c && (*c != ',' && *c != ' ' && *c != '\t')) { + ext_name[n] = *c++; + if (n < sizeof(ext_name) - 1) + n++; + continue; + } + ext_name[n] = '\0'; + if (!*c) + more = 0; + else { + c++; + if (!n) + continue; + } + + /* check a client's extension against our support */ + + ext = wsi->protocol->owning_server->extensions; + + while (ext && ext->callback) { + + if (strcmp(ext_name, ext->name)) { + ext++; + continue; + } + + /* + * oh, we do support this one he + * asked for... but let's ask user + * code if it's OK to apply it on this + * particular connection + protocol + */ + + n = wsi->protocol->owning_server-> + protocols[0].callback( + wsi->protocol->owning_server, + wsi, + LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, + wsi->user_space, ext_name, 0); + + /* + * zero return from callback means + * go ahead and allow the extension, + * it's what we get if the callback is + * unhandled + */ + + if (n) { + ext++; + continue; + } + + /* apply it */ + + if (ext_count) + *(*p)++ = ','; + else + LWS_CPYAPP(*p, + "\x0d\x0aSec-WebSocket-Extensions: "); + *p += sprintf(*p, "%s", ext_name); + ext_count++; + + /* instantiate the extension on this conn */ + + wsi->active_extensions_user[ + wsi->count_active_extensions] = + malloc(ext->per_session_data_size); + if (wsi->active_extensions_user[ + wsi->count_active_extensions] == NULL) { + lwsl_err("Out of mem\n"); + return 1; + } + memset(wsi->active_extensions_user[ + wsi->count_active_extensions], 0, + ext->per_session_data_size); + + wsi->active_extensions[ + wsi->count_active_extensions] = ext; + + /* allow him to construct his context */ + + ext->callback(wsi->protocol->owning_server, + ext, wsi, + LWS_EXT_CALLBACK_CONSTRUCT, + wsi->active_extensions_user[ + wsi->count_active_extensions], NULL, 0); + wsi->count_active_extensions++; + lwsl_parser("count_active_extensions <- %d\n", + wsi->count_active_extensions); + + ext++; + } + + n = 0; + } + + return 0; +} +#endif int handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) { @@ -35,13 +166,6 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) char *response; char *p; int accept_len; -#ifndef LWS_NO_EXTENSIONS - char *c; - char ext_name[128]; - struct libwebsocket_extension *ext; - int ext_count = 0; - int more = 1; -#endif if (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST) || !lws_hdr_total_length(wsi, WSI_TOKEN_KEY)) { @@ -64,7 +188,7 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) "%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", lws_hdr_simple_ptr(wsi, WSI_TOKEN_KEY)); - ws_SHA1(context->service_buffer, n, hash); + SHA1(context->service_buffer, n, hash); accept_len = lws_b64_encode_string((char *)hash, 20, (char *)context->service_buffer, @@ -104,141 +228,23 @@ handshake_0405(struct libwebsocket_context *context, struct libwebsocket *wsi) * Figure out which extensions the client has that we want to * enable on this connection, and give him back the list */ - - if (lws_hdr_total_length(wsi, WSI_TOKEN_EXTENSIONS)) { - - /* - * break down the list of client extensions - * and go through them - */ - - if (lws_hdr_copy(wsi, (char *)context->service_buffer, - sizeof(context->service_buffer), - WSI_TOKEN_EXTENSIONS) < 0) - goto bail; - - c = (char *)context->service_buffer; - lwsl_parser("WSI_TOKEN_EXTENSIONS = '%s'\n", c); - wsi->count_active_extensions = 0; - n = 0; - while (more) { - - if (*c && (*c != ',' && *c != ' ' && *c != '\t')) { - ext_name[n] = *c++; - if (n < sizeof(ext_name) - 1) - n++; - continue; - } - ext_name[n] = '\0'; - if (!*c) - more = 0; - else { - c++; - if (!n) - continue; - } - - /* check a client's extension against our support */ - - ext = wsi->protocol->owning_server->extensions; - - while (ext && ext->callback) { - - if (strcmp(ext_name, ext->name)) { - ext++; - continue; - } - - /* - * oh, we do support this one he - * asked for... but let's ask user - * code if it's OK to apply it on this - * particular connection + protocol - */ - - n = wsi->protocol->owning_server-> - protocols[0].callback( - wsi->protocol->owning_server, - wsi, - LWS_CALLBACK_CONFIRM_EXTENSION_OKAY, - wsi->user_space, ext_name, 0); - - /* - * zero return from callback means - * go ahead and allow the extension, - * it's what we get if the callback is - * unhandled - */ - - if (n) { - ext++; - continue; - } - - /* apply it */ - - if (ext_count) - *p++ = ','; - else - LWS_CPYAPP(p, - "\x0d\x0aSec-WebSocket-Extensions: "); - p += sprintf(p, "%s", ext_name); - ext_count++; - - /* instantiate the extension on this conn */ - - wsi->active_extensions_user[ - wsi->count_active_extensions] = - malloc(ext->per_session_data_size); - if (wsi->active_extensions_user[ - wsi->count_active_extensions] == NULL) { - lwsl_err("Out of mem\n"); - free(response); - goto bail; - } - memset(wsi->active_extensions_user[ - wsi->count_active_extensions], 0, - ext->per_session_data_size); - - wsi->active_extensions[ - wsi->count_active_extensions] = ext; - - /* allow him to construct his context */ - - ext->callback(wsi->protocol->owning_server, - ext, wsi, - LWS_EXT_CALLBACK_CONSTRUCT, - wsi->active_extensions_user[ - wsi->count_active_extensions], NULL, 0); - - wsi->count_active_extensions++; - lwsl_parser("count_active_extensions <- %d\n", - wsi->count_active_extensions); - - ext++; - } - - n = 0; - } - } + if (lws_extension_server_handshake(context, wsi, &p)) + goto bail; #endif /* end of response packet */ LWS_CPYAPP(p, "\x0d\x0a\x0d\x0a"); - -#ifndef LWS_NO_EXTENSIONS + if (!lws_any_extension_handled(context, wsi, LWS_EXT_CALLBACK_HANDSHAKE_REPLY_TX, response, p - response)) { -#else - { -#endif + /* okay send the handshake response accepting the connection */ lwsl_parser("issuing resp pkt %d len\n", (int)(p - response)); - #ifdef DEBUG +#ifdef DEBUG fwrite(response, 1, p - response, stderr); - #endif +#endif n = libwebsocket_write(wsi, (unsigned char *)response, p - response, LWS_WRITE_HTTP); if (n != (p - response)) { diff --git a/lib/server.c b/lib/server.c index 8d12a81b33..5f8749fc26 100644 --- a/lib/server.c +++ b/lib/server.c @@ -22,62 +22,488 @@ #include "private-libwebsockets.h" -#ifdef WIN32 -#include -#include -#else -#ifdef LWS_BUILTIN_GETIFADDRS -#include -#else -#include +int lws_context_init_server(struct lws_context_creation_info *info, + struct libwebsocket_context *context) +{ + int n; + int sockfd; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + int opt = 1; + struct libwebsocket *wsi; +#ifdef LWS_USE_IPV6 + struct sockaddr_in6 serv_addr6; #endif -#include -#include -#include + struct sockaddr_in serv_addr4; + struct sockaddr *v; + + /* set up our external listening socket we serve on */ + + if (info->port == CONTEXT_PORT_NO_LISTEN) + return 0; + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) + sockfd = socket(AF_INET6, SOCK_STREAM, 0); + else #endif + sockfd = socket(AF_INET, SOCK_STREAM, 0); -#ifdef LWS_OPENSSL_SUPPORT + if (sockfd < 0) { + lwsl_err("ERROR opening socket\n"); + return 1; + } -static void -libwebsockets_decode_ssl_error(void) -{ - char buf[256]; - u_long err; + /* + * allow us to restart even if old sockets in TIME_WAIT + */ + setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, + (const void *)&opt, sizeof(opt)); + + lws_plat_set_socket_options(context, sockfd); + +#ifdef LWS_USE_IPV6 + if (LWS_IPV6_ENABLED(context)) { + v = (struct sockaddr *)&serv_addr6; + n = sizeof(struct sockaddr_in6); + bzero((char *) &serv_addr6, sizeof(serv_addr6)); + serv_addr6.sin6_addr = in6addr_any; + serv_addr6.sin6_family = AF_INET6; + serv_addr6.sin6_port = htons(info->port); + } else +#endif + { + v = (struct sockaddr *)&serv_addr4; + n = sizeof(serv_addr4); + bzero((char *) &serv_addr4, sizeof(serv_addr4)); + serv_addr4.sin_addr.s_addr = INADDR_ANY; + serv_addr4.sin_family = AF_INET; + + if (info->iface) { + if (interface_to_sa(context, info->iface, + (struct sockaddr_in *)v, n) < 0) { + lwsl_err("Unable to find interface %s\n", + info->iface); + compatible_close(sockfd); + return 1; + } + } + + serv_addr4.sin_port = htons(info->port); + } /* ipv4 */ - while ((err = ERR_get_error()) != 0) { - ERR_error_string_n(err, buf, sizeof(buf)); - lwsl_err("*** %lu %s\n", err, buf); + n = bind(sockfd, v, n); + if (n < 0) { + lwsl_err("ERROR on binding to port %d (%d %d)\n", + info->port, n, LWS_ERRNO); + compatible_close(sockfd); + return 1; } + + if (getsockname(sockfd, (struct sockaddr *)&sin, &len) == -1) + lwsl_warn("getsockname: %s\n", strerror(LWS_ERRNO)); + else + info->port = ntohs(sin.sin_port); + + context->listen_port = info->port; + + wsi = (struct libwebsocket *)malloc(sizeof(struct libwebsocket)); + if (wsi == NULL) { + lwsl_err("Out of mem\n"); + compatible_close(sockfd); + return 1; + } + memset(wsi, 0, sizeof(struct libwebsocket)); + wsi->sock = sockfd; + wsi->mode = LWS_CONNMODE_SERVER_LISTENER; + + insert_wsi_socket_into_fds(context, wsi); + + context->listen_service_modulo = LWS_LISTEN_SERVICE_MODULO; + context->listen_service_count = 0; + context->listen_service_fd = sockfd; + + listen(sockfd, LWS_SOMAXCONN); + lwsl_notice(" Listening on port %d\n", info->port); + + return 0; } -#endif int -interface_to_sa(const char *ifname, struct sockaddr_in *addr, size_t addrlen) +_libwebsocket_rx_flow_control(struct libwebsocket *wsi) { - int rc = -1; -#ifdef WIN32 - /* TODO */ -#else - struct ifaddrs *ifr; - struct ifaddrs *ifc; - struct sockaddr_in *sin; - - getifaddrs(&ifr); - for (ifc = ifr; ifc != NULL; ifc = ifc->ifa_next) { - if (strcmp(ifc->ifa_name, ifname)) - continue; - if (ifc->ifa_addr == NULL) - continue; - sin = (struct sockaddr_in *)ifc->ifa_addr; - if (sin->sin_family != AF_INET) - continue; - memcpy(addr, sin, addrlen); - rc = 0; + struct libwebsocket_context *context = wsi->protocol->owning_server; + + /* there is no pending change */ + if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) + return 0; + + /* stuff is still buffered, not ready to really accept new input */ + if (wsi->u.ws.rxflow_buffer) { + /* get ourselves called back to deal with stashed buffer */ + libwebsocket_callback_on_writable(context, wsi); + return 0; } - freeifaddrs(ifr); -#endif - return rc; + /* pending is cleared, we can change rxflow state */ + + wsi->u.ws.rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; + + lwsl_info("rxflow: wsi %p change_to %d\n", wsi, + wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW); + + /* adjust the pollfd for this wsi */ + + if (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) { + lwsl_info("%s: fail\n", __func__); + return -1; + } + } else + if (lws_change_pollfd(wsi, LWS_POLLIN, 0)) + return -1; + + return 0; +} + + +int lws_handshake_server(struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned char **buf, size_t len) +{ + struct allocated_headers *ah; + char *uri_ptr = NULL; + int uri_len = 0; + enum http_version request_version; + enum http_connection_type connection_type; + int http_version_len, protocol_len; + char content_length_str[32]; + char protocol_list[128]; + char protocol_name[32]; + char http_version_str[10]; + char *p; + int n, hit; + + /* LWS_CONNMODE_WS_SERVING */ + + while (len--) { + if (libwebsocket_parse(context, wsi, *(*buf)++)) { + lwsl_info("libwebsocket_parse failed\n"); + goto bail_nuke_ah; + } + + if (wsi->u.hdr.parser_state != WSI_PARSING_COMPLETE) + continue; + + lwsl_parser("libwebsocket_parse sees parsing complete\n"); + + wsi->mode = LWS_CONNMODE_PRE_WS_SERVING_ACCEPT; + libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + + /* is this websocket protocol or normal http 1.0? */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE) || + !lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { + + /* it's not websocket.... shall we accept it as http? */ + + if (!lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) && + !lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) && + !lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) { + lwsl_warn("Missing URI in HTTP request\n"); + goto bail_nuke_ah; + } + + if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) && + lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + lwsl_warn("GET and POST methods?\n"); + goto bail_nuke_ah; + } + + if (libwebsocket_ensure_user_space(wsi)) + goto bail_nuke_ah; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI)) { + uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI); + uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI); + lwsl_info("HTTP GET request for '%s'\n", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_GET_URI)); + + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) { + lwsl_info("HTTP POST request for '%s'\n", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)); + uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI); + uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI); + } + if (lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI)) { + lwsl_info("HTTP OPTIONS request for '%s'\n", + lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI)); + uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_OPTIONS_URI); + uri_len = lws_hdr_total_length(wsi, WSI_TOKEN_OPTIONS_URI); + } + + /* + * Hm we still need the headers so the + * callback can look at leaders like the URI, but we + * need to transition to http union state.... hold a + * copy of u.hdr.ah and deallocate afterwards + */ + ah = wsi->u.hdr.ah; + + /* union transition */ + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->mode = LWS_CONNMODE_HTTP_SERVING_ACCEPTED; + wsi->state = WSI_STATE_HTTP; + wsi->u.http.fd = LWS_INVALID_FILE; + + /* expose it at the same offset as u.hdr */ + wsi->u.http.ah = ah; + + /* HTTP header had a content length? */ + + wsi->u.http.content_length = 0; + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) + wsi->u.http.content_length = 100 * 1024 * 1024; + + if (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH)) { + lws_hdr_copy(wsi, content_length_str, + sizeof(content_length_str) - 1, + WSI_TOKEN_HTTP_CONTENT_LENGTH); + wsi->u.http.content_length = atoi(content_length_str); + } + + /* http_version? Default to 1.0, override with token: */ + request_version = HTTP_VERSION_1_0; + + /* Works for single digit HTTP versions. : */ + http_version_len = lws_hdr_total_length(wsi, WSI_TOKEN_HTTP); + if (http_version_len > 7) { + lws_hdr_copy(wsi, http_version_str, + sizeof(http_version_str) - 1, WSI_TOKEN_HTTP); + if (http_version_str[5] == '1' && + http_version_str[7] == '1') + request_version = HTTP_VERSION_1_1; + } + wsi->u.http.request_version = request_version; + + /* HTTP/1.1 defaults to "keep-alive", 1.0 to "close" */ + if (request_version == HTTP_VERSION_1_1) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + else + connection_type = HTTP_CONNECTION_CLOSE; + + /* Override default if http "Connection:" header: */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION)) { + char http_conn_str[20]; + lws_hdr_copy(wsi, http_conn_str, + sizeof(http_conn_str)-1, WSI_TOKEN_CONNECTION); + http_conn_str[sizeof(http_conn_str)-1] = '\0'; + if (strcasecmp(http_conn_str,"keep-alive") == 0) + connection_type = HTTP_CONNECTION_KEEP_ALIVE; + + else if (strcasecmp(http_conn_str,"close") == 0) + connection_type = HTTP_CONNECTION_CLOSE; + + } + wsi->u.http.connection_type = connection_type; + + n = 0; + if (wsi->protocol->callback) + n = wsi->protocol->callback(context, wsi, + LWS_CALLBACK_FILTER_HTTP_CONNECTION, + wsi->user_space, uri_ptr, uri_len); + + if (!n) { + /* + * if there is content supposed to be coming, + * put a timeout on it having arrived + */ + libwebsocket_set_timeout(wsi, + PENDING_TIMEOUT_HTTP_CONTENT, + AWAITING_TIMEOUT); + + if (wsi->protocol->callback) + n = wsi->protocol->callback(context, wsi, + LWS_CALLBACK_HTTP, + wsi->user_space, uri_ptr, uri_len); + } + + /* now drop the header info we kept a pointer to */ + if (ah) + free(ah); + /* not possible to continue to use past here */ + wsi->u.http.ah = NULL; + + if (n) { + lwsl_info("LWS_CALLBACK_HTTP closing\n"); + return 1; /* struct ah ptr already nuked */ + } + + /* If we're not issuing a file, check for content_length or + * HTTP keep-alive. No keep-alive header allocation for + * ISSUING_FILE, as this uses HTTP/1.0. + * In any case, return 0 and let libwebsocket_read decide how to + * proceed based on state. */ + if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) + + /* Prepare to read body if we have a content length: */ + if (wsi->u.http.content_length > 0) + wsi->state = WSI_STATE_HTTP_BODY; + + + return 0; /* don't bail out of libwebsocket_read, just yet */ + } + + if (!wsi->protocol) + lwsl_err("NULL protocol at libwebsocket_read\n"); + + /* + * It's websocket + * + * Select the first protocol we support from the list + * the client sent us. + * + * Copy it to remove header fragmentation + */ + + if (lws_hdr_copy(wsi, protocol_list, sizeof(protocol_list) - 1, + WSI_TOKEN_PROTOCOL) < 0) { + lwsl_err("protocol list too long"); + goto bail_nuke_ah; + } + + protocol_len = lws_hdr_total_length(wsi, WSI_TOKEN_PROTOCOL); + protocol_list[protocol_len] = '\0'; + p = protocol_list; + hit = 0; + + while (*p && !hit) { + n = 0; + while (n < sizeof(protocol_name) - 1 && *p && *p !=',') + protocol_name[n++] = *p++; + protocol_name[n] = '\0'; + if (*p) + p++; + + lwsl_info("checking %s\n", protocol_name); + + n = 0; + while (context->protocols[n].callback) { + if (!wsi->protocol->name) + continue; + if (!strcmp(context->protocols[n].name, + protocol_name)) { + lwsl_info("prot match %d\n", n); + wsi->protocol = &context->protocols[n]; + hit = 1; + break; + } + + n++; + } + } + + /* we didn't find a protocol he wanted? */ + + if (!hit) { + if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL) == + NULL) { + /* + * some clients only have one protocol and + * do not sent the protocol list header... + * allow it and match to protocol 0 + */ + lwsl_info("defaulting to prot 0 handler\n"); + wsi->protocol = &context->protocols[0]; + } else { + lwsl_err("No protocol from list \"%s\" supported\n", + protocol_list); + goto bail_nuke_ah; + } + } + + /* allocate wsi->user storage */ + if (libwebsocket_ensure_user_space(wsi)) + goto bail_nuke_ah; + + /* + * Give the user code a chance to study the request and + * have the opportunity to deny it + */ + + if ((wsi->protocol->callback)(wsi->protocol->owning_server, wsi, + LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION, + wsi->user_space, + lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL), 0)) { + lwsl_warn("User code denied connection\n"); + goto bail_nuke_ah; + } + + + /* + * Perform the handshake according to the protocol version the + * client announced + */ + + switch (wsi->ietf_spec_revision) { + case 13: + lwsl_parser("lws_parse calling handshake_04\n"); + if (handshake_0405(context, wsi)) { + lwsl_info("hs0405 has failed the connection\n"); + goto bail_nuke_ah; + } + break; + + default: + lwsl_warn("Unknown client spec version %d\n", + wsi->ietf_spec_revision); + goto bail_nuke_ah; + } + + /* drop the header info -- no bail_nuke_ah after this */ + + if (wsi->u.hdr.ah) + free(wsi->u.hdr.ah); + + wsi->mode = LWS_CONNMODE_WS_SERVING; + + /* union transition */ + memset(&wsi->u, 0, sizeof(wsi->u)); + wsi->u.ws.rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* + * create the frame buffer for this connection according to the + * size mentioned in the protocol definition. If 0 there, use + * a big default for compatibility + */ + + n = wsi->protocol->rx_buffer_size; + if (!n) + n = LWS_MAX_SOCKET_IO_BUF; + n += LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING; + wsi->u.ws.rx_user_buffer = malloc(n); + if (!wsi->u.ws.rx_user_buffer) { + lwsl_err("Out of Mem allocating rx buffer %d\n", n); + return 1; + } + lwsl_info("Allocating RX buffer %d\n", n); + + if (setsockopt(wsi->sock, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) { + lwsl_warn("Failed to set SNDBUF to %d", n); + return 1; + } + + lwsl_parser("accepted v%02d connection\n", + wsi->ietf_spec_revision); + } /* while all chars are handled */ + + return 0; + +bail_nuke_ah: + /* drop the header info */ + if (wsi->u.hdr.ah) + free(wsi->u.hdr.ah); + return 1; } struct libwebsocket * @@ -92,9 +518,6 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context) } memset(new_wsi, 0, sizeof(struct libwebsocket)); -#ifndef LWS_NO_EXTENSIONS - new_wsi->count_active_extensions = 0; -#endif new_wsi->pending_timeout = NO_PENDING_TIMEOUT; /* intialize the instance struct */ @@ -118,24 +541,25 @@ libwebsocket_create_new_server_wsi(struct libwebsocket_context *context) new_wsi->user_space = NULL; new_wsi->ietf_spec_revision = 0; + /* + * outermost create notification for wsi + * no user_space because no protocol selection + */ + context->protocols[0].callback(context, new_wsi, + LWS_CALLBACK_WSI_CREATE, NULL, NULL, 0); + return new_wsi; } int lws_server_socket_service(struct libwebsocket_context *context, - struct libwebsocket *wsi, struct pollfd *pollfd) + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { - struct libwebsocket *new_wsi; - int accept_fd; + struct libwebsocket *new_wsi = NULL; + int accept_fd = 0; socklen_t clilen; struct sockaddr_in cli_addr; int n; - ssize_t len; -#ifdef LWS_OPENSSL_SUPPORT - int m; -#ifndef USE_CYASSL - BIO *bio; -#endif -#endif + int len; switch (wsi->mode) { @@ -144,53 +568,71 @@ int lws_server_socket_service(struct libwebsocket_context *context, /* handle http headers coming in */ - /* any incoming data ready? */ + /* pending truncated sends have uber priority */ + + if (wsi->truncated_send_malloc) { + if (pollfd->revents & LWS_POLLOUT) + if (lws_issue_raw(wsi, wsi->truncated_send_malloc + + wsi->truncated_send_offset, + wsi->truncated_send_len) < 0) { + lwsl_info("closing from socket service\n"); + return -1; + } + /* + * we can't afford to allow input processing send + * something new, so spin around he event loop until + * he doesn't have any partials + */ + break; + } - if (pollfd->revents & POLLIN) { + /* any incoming data ready? */ - #ifdef LWS_OPENSSL_SUPPORT - if (wsi->ssl) - len = SSL_read(wsi->ssl, - context->service_buffer, - sizeof(context->service_buffer)); - else - #endif - len = recv(pollfd->fd, + if (pollfd->revents & LWS_POLLIN) { + len = lws_ssl_capable_read(wsi, context->service_buffer, - sizeof(context->service_buffer), 0); - - if (len < 0) { - lwsl_debug("Socket read returned %d\n", len); - if (errno != EINTR && errno != EAGAIN) - libwebsocket_close_and_free_session( - context, wsi, - LWS_CLOSE_STATUS_NOSTATUS); - return 0; - } - if (!len) { + sizeof(context->service_buffer)); + switch (len) { + case 0: lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); + /* fallthru */ + case LWS_SSL_CAPABLE_ERROR: libwebsocket_close_and_free_session( - context, wsi, LWS_CLOSE_STATUS_NOSTATUS); + context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); return 0; + case LWS_SSL_CAPABLE_MORE_SERVICE: + break; } - n = libwebsocket_read(context, wsi, - context->service_buffer, len); - if (n < 0) - /* we closed wsi */ - return 0; + /* just ignore incoming if waiting for close */ + if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + + /* hm this may want to send (via HTTP callback for example) */ + n = libwebsocket_read(context, wsi, + context->service_buffer, len); + if (n < 0) + /* we closed wsi */ + return 0; + + /* hum he may have used up the writability above */ + break; + } } /* this handles POLLOUT for http serving fragments */ - if (!(pollfd->revents & POLLOUT)) + if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ - pollfd->events &= ~POLLOUT; + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + goto fail; + + lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( @@ -216,7 +658,7 @@ int lws_server_socket_service(struct libwebsocket_context *context, /* pollin means a client has connected to us then */ - if (!(pollfd->revents & POLLIN)) + if (!(pollfd->revents & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ @@ -229,15 +671,15 @@ int lws_server_socket_service(struct libwebsocket_context *context, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { - if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } - lwsl_warn("ERROR on accept: %s\n", strerror(errno)); + lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } - lws_set_socket_options(context, accept_fd); + lws_plat_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance @@ -261,130 +703,235 @@ int lws_server_socket_service(struct libwebsocket_context *context, new_wsi->sock = accept_fd; -#ifdef LWS_OPENSSL_SUPPORT - new_wsi->ssl = NULL; - if (!context->use_ssl) { -#endif + /* the transport is accepted... give him time to negotiate */ + libwebsocket_set_timeout(new_wsi, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + AWAITING_TIMEOUT); + + /* + * A new connection was accepted. Give the user a chance to + * set properties of the newly created wsi. There's no protocol + * selected yet so we issue this to protocols[0] + */ + + (context->protocols[0].callback)(context, new_wsi, + LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); + lws_libev_accept(context, new_wsi, accept_fd); + + if (!LWS_SSL_ENABLED(context)) { lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); - break; -#ifdef LWS_OPENSSL_SUPPORT } + break; - new_wsi->ssl = SSL_new(context->ssl_ctx); - if (new_wsi->ssl == NULL) { - lwsl_err("SSL_new failed: %s\n", - ERR_error_string(SSL_get_error( - new_wsi->ssl, 0), NULL)); - libwebsockets_decode_ssl_error(); - free(new_wsi); - compatible_close(accept_fd); - break; - } + default: + break; + } - SSL_set_ex_data(new_wsi->ssl, - openssl_websocket_private_data_index, context); - - SSL_set_fd(new_wsi->ssl, accept_fd); - - #ifdef USE_CYASSL - CyaSSL_set_using_nonblock(new_wsi->ssl, 1); - #else - bio = SSL_get_rbio(new_wsi->ssl); - if (bio) - BIO_set_nbio(bio, 1); /* nonblocking */ - else - lwsl_notice("NULL rbio\n"); - bio = SSL_get_wbio(new_wsi->ssl); - if (bio) - BIO_set_nbio(bio, 1); /* nonblocking */ - else - lwsl_notice("NULL rbio\n"); - #endif + if (lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd, pollfd)) + goto fail; - /* - * we are not accepted yet, but we need to enter ourselves - * as a live connection. That way we can retry when more - * pieces come if we're not sorted yet - */ + return 0; + +fail: + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); + return 1; +} - wsi = new_wsi; - wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; - insert_wsi_socket_into_fds(context, wsi); - libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, - AWAITING_TIMEOUT); +static const char *err400[] = { + "Bad Request", + "Unauthorized", + "Payment Required", + "Forbidden", + "Not Found", + "Method Not Allowed", + "Not Acceptable", + "Proxy Auth Required", + "Request Timeout", + "Conflict", + "Gone", + "Length Required", + "Precondition Failed", + "Request Entity Too Large", + "Request URI too Long", + "Unsupported Media Type", + "Requested Range Not Satisfiable", + "Expectation Failed" +}; + +static const char *err500[] = { + "Internal Server Error", + "Not Implemented", + "Bad Gateway", + "Service Unavailable", + "Gateway Timeout", + "HTTP Version Not Supported" +}; + +/** + * libwebsockets_return_http_status() - Return simple http status + * @context: libwebsockets context + * @wsi: Websocket instance (available from user callback) + * @code: Status index, eg, 404 + * @html_body: User-readable HTML description, or NULL + * + * Helper to report HTTP errors back to the client cleanly and + * consistently + */ +LWS_VISIBLE int libwebsockets_return_http_status( + struct libwebsocket_context *context, struct libwebsocket *wsi, + unsigned int code, const char *html_body) +{ + int n, m; + const char *description = ""; - lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); + if (!html_body) + html_body = ""; - /* fallthru */ + if (code >= 400 && code < (400 + ARRAY_SIZE(err400))) + description = err400[code - 400]; + if (code >= 500 && code < (500 + ARRAY_SIZE(err500))) + description = err500[code - 500]; - case LWS_CONNMODE_SSL_ACK_PENDING: + n = sprintf((char *)context->service_buffer, + "HTTP/1.0 %u %s\x0d\x0a" + "Server: libwebsockets\x0d\x0a" + "Content-Type: text/html\x0d\x0a\x0d\x0a" + "

%u %s

%s", + code, description, code, description, html_body); - pollfd->events &= ~POLLOUT; + lwsl_info((const char *)context->service_buffer); - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_CLEAR_MODE_POLL_FD, - wsi->user_space, (void *)(long)wsi->sock, POLLOUT); + m = libwebsocket_write(wsi, context->service_buffer, n, LWS_WRITE_HTTP); - lws_latency_pre(context, wsi); - n = SSL_accept(wsi->ssl); - lws_latency(context, wsi, - "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); + return m; +} - if (n != 1) { - m = SSL_get_error(wsi->ssl, n); - lwsl_debug("SSL_accept failed %d / %s\n", - m, ERR_error_string(m, NULL)); +/** + * libwebsockets_serve_http_file() - Send a file back to the client using http + * @context: libwebsockets context + * @wsi: Websocket instance (available from user callback) + * @file: The file to issue over http + * @content_type: The http content type, eg, text/html + * @other_headers: NULL or pointer to \0-terminated other header string + * + * 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 the wsi should be closed. + * ==0 indicates the file transfer is started and needs more service later, + * the wsi should be left alone. + */ - if (m == SSL_ERROR_WANT_READ) { - context->fds[ - wsi->position_in_fds_table].events |= POLLIN; +LWS_VISIBLE int libwebsockets_serve_http_file( + struct libwebsocket_context *context, + struct libwebsocket *wsi, const char *file, + const char *content_type, const char *other_headers) +{ + unsigned char *p = context->service_buffer; + int ret = 0; + int n; - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_SET_MODE_POLL_FD, - wsi->user_space, - (void *)(long)wsi->sock, POLLIN); - lwsl_info("SSL_ERROR_WANT_READ\n"); - break; - } - if (m == SSL_ERROR_WANT_WRITE) { - context->fds[ - wsi->position_in_fds_table].events |= POLLOUT; + wsi->u.http.fd = lws_plat_open_file(file, &wsi->u.http.filelen); - /* external POLL support via protocol 0 */ - context->protocols[0].callback(context, wsi, - LWS_CALLBACK_SET_MODE_POLL_FD, - wsi->user_space, - (void *)(long)wsi->sock, POLLOUT); - break; - } - lwsl_debug("SSL_accept failed skt %u: %s\n", - pollfd->fd, - ERR_error_string(m, NULL)); - libwebsocket_close_and_free_session(context, wsi, - LWS_CLOSE_STATUS_NOSTATUS); - break; - } + if (wsi->u.http.fd == LWS_INVALID_FILE) { + lwsl_err("Unable to open '%s'\n", file); + libwebsockets_return_http_status(context, wsi, + HTTP_STATUS_NOT_FOUND, NULL); + return -1; + } - /* OK, we are accepted */ + p += sprintf((char *)p, +"HTTP/1.0 200 OK\x0d\x0aServer: libwebsockets\x0d\x0a""Content-Type: %s\x0d\x0a", + content_type); + if (other_headers) { + n = strlen(other_headers); + memcpy(p, other_headers, n); + p += n; + } + p += sprintf((char *)p, + "Content-Length: %lu\x0d\x0a\x0d\x0a", wsi->u.http.filelen); + + ret = libwebsocket_write(wsi, context->service_buffer, + p - context->service_buffer, LWS_WRITE_HTTP); + if (ret != (p - context->service_buffer)) { + lwsl_err("_write returned %d from %d\n", ret, (p - context->service_buffer)); + return -1; + } - libwebsocket_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); + wsi->u.http.filepos = 0; + wsi->state = WSI_STATE_HTTP_ISSUING_FILE; - wsi->mode = LWS_CONNMODE_HTTP_SERVING; + return libwebsockets_serve_http_file_fragment(context, wsi); +} - lwsl_debug("accepted new SSL conn\n"); - break; + +int libwebsocket_interpret_incoming_packet(struct libwebsocket *wsi, + unsigned char *buf, size_t len) +{ + size_t n = 0; + int m; + +#if 0 + lwsl_parser("received %d byte packet\n", (int)len); + lwsl_hexdump(buf, len); #endif - default: - break; + /* let the rx protocol state machine have as much as it needs */ + + while (n < len) { + /* + * we were accepting input but now we stopped doing so + */ + if (!(wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { + /* his RX is flowcontrolled, don't send remaining now */ + if (!wsi->u.ws.rxflow_buffer) { + /* a new rxflow, buffer it and warn caller */ + lwsl_info("new rxflow input buffer len %d\n", + len - n); + wsi->u.ws.rxflow_buffer = + (unsigned char *)malloc(len - n); + wsi->u.ws.rxflow_len = len - n; + wsi->u.ws.rxflow_pos = 0; + memcpy(wsi->u.ws.rxflow_buffer, + buf + n, len - n); + } else + /* rxflow while we were spilling prev rxflow */ + lwsl_info("stalling in existing rxflow buf\n"); + + return 1; + } + + /* account for what we're using in rxflow buffer */ + if (wsi->u.ws.rxflow_buffer) + wsi->u.ws.rxflow_pos++; + + /* process the byte */ + m = libwebsocket_rx_sm(wsi, buf[n++]); + if (m < 0) + return -1; } + return 0; } +LWS_VISIBLE void +lws_server_get_canonical_hostname(struct libwebsocket_context *context, + struct lws_context_creation_info *info) +{ + if (info->options & LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME) + return; + + /* find canonical hostname */ + gethostname((char *)context->canonical_hostname, + sizeof(context->canonical_hostname) - 1); + + lwsl_notice(" canonical_hostname = %s\n", context->canonical_hostname); +} diff --git a/lib/service.c b/lib/service.c new file mode 100755 index 0000000000..1eeaf99fed --- /dev/null +++ b/lib/service.c @@ -0,0 +1,517 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + */ + +#include "private-libwebsockets.h" + +int +lws_handle_POLLOUT_event(struct libwebsocket_context *context, + struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) +{ + int n; + struct lws_tokens eff_buf; + int ret; + int m; + int handled = 0; + + /* pending truncated sends have uber priority */ + + if (wsi->truncated_send_len) { + if (lws_issue_raw(wsi, wsi->truncated_send_malloc + + wsi->truncated_send_offset, + wsi->truncated_send_len) < 0) { + lwsl_info("lws_handle_POLLOUT_event signalling to close\n"); + return -1; + } + /* leave POLLOUT active either way */ + return 0; + } else + if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { + lwsl_info("***** %x signalling to close in POLLOUT handler\n", wsi); + return -1; /* retry closing now */ + } + + + m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE, + NULL, 0); + if (handled == 1) + goto notify_action; +#ifndef LWS_NO_EXTENSIONS + if (!wsi->extension_data_pending || handled == 2) + goto user_service; +#endif + /* + * check in on the active extensions, see if they + * had pending stuff to spill... they need to get the + * first look-in otherwise sequence will be disordered + * + * NULL, zero-length eff_buf means just spill pending + */ + + ret = 1; + while (ret == 1) { + + /* default to nobody has more to spill */ + + ret = 0; + eff_buf.token = NULL; + eff_buf.token_len = 0; + + /* give every extension a chance to spill */ + + m = lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PACKET_TX_PRESEND, + &eff_buf, 0); + if (m < 0) { + lwsl_err("ext reports fatal error\n"); + return -1; + } + if (m) + /* + * at least one extension told us he has more + * to spill, so we will go around again after + */ + ret = 1; + + /* assuming they gave us something to send, send it */ + + if (eff_buf.token_len) { + n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + lwsl_info("closing from POLLOUT spill\n"); + return -1; + } + /* + * Keep amount spilled small to minimize chance of this + */ + if (n != eff_buf.token_len) { + lwsl_err("Unable to spill ext %d vs %s\n", + eff_buf.token_len, n); + return -1; + } + } else + continue; + + /* no extension has more to spill */ + + if (!ret) + continue; + + /* + * There's more to spill from an extension, but we just sent + * something... did that leave the pipe choked? + */ + + if (!lws_send_pipe_choked(wsi)) + /* no we could add more */ + continue; + + lwsl_info("choked in POLLOUT service\n"); + + /* + * Yes, he's choked. Leave the POLLOUT masked on so we will + * come back here when he is unchoked. Don't call the user + * callback to enforce ordering of spilling, he'll get called + * when we come back here and there's nothing more to spill. + */ + + return 0; + } +#ifndef LWS_NO_EXTENSIONS + wsi->extension_data_pending = 0; + +user_service: +#endif + /* one shot */ + + if (pollfd) { + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + return 1; + + lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); + } + +notify_action: + if (wsi->mode == LWS_CONNMODE_WS_CLIENT) + n = LWS_CALLBACK_CLIENT_WRITEABLE; + else + n = LWS_CALLBACK_SERVER_WRITEABLE; + + return user_callback_handle_rxflow(wsi->protocol->callback, context, + wsi, (enum libwebsocket_callback_reasons) n, + wsi->user_space, NULL, 0); +} + + + +int +libwebsocket_service_timeout_check(struct libwebsocket_context *context, + struct libwebsocket *wsi, unsigned int sec) +{ + /* + * if extensions want in on it (eg, we are a mux parent) + * give them a chance to service child timeouts + */ + if (lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_1HZ, NULL, sec) < 0) + return 0; + + if (!wsi->pending_timeout) + return 0; + + /* + * if we went beyond the allowed time, kill the + * connection + */ + if (sec > wsi->pending_timeout_limit) { + lwsl_info("TIMEDOUT WAITING on %d\n", wsi->pending_timeout); + libwebsocket_close_and_free_session(context, + wsi, LWS_CLOSE_STATUS_NOSTATUS); + return 1; + } + + return 0; +} + +/** + * libwebsocket_service_fd() - Service polled socket with something waiting + * @context: Websocket context + * @pollfd: The pollfd entry describing the socket fd and which events + * happened. + * + * This function takes a pollfd that has POLLIN or POLLOUT activity and + * services it according to the state of the associated + * struct libwebsocket. + * + * The one call deals with all "service" that might happen on a socket + * including listen accepts, http files as well as websocket protocol. + * + * If a pollfd says it has something, you can just pass it to + * libwebsocket_serice_fd() whether it is a socket handled by lws or not. + * If it sees it is a lws socket, the traffic will be handled and + * pollfd->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. + */ + +LWS_VISIBLE int +libwebsocket_service_fd(struct libwebsocket_context *context, + struct libwebsocket_pollfd *pollfd) +{ + struct libwebsocket *wsi; + int n; + int m; + int listen_socket_fds_index = 0; + time_t now; + int timed_out = 0; + int our_fd = 0; + char draining_flow = 0; + int more; + struct lws_tokens eff_buf; + + if (context->listen_service_fd) + listen_socket_fds_index = context->lws_lookup[ + context->listen_service_fd]->position_in_fds_table; + + /* + * you can call us 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. + */ + + time(&now); + + /* TODO: if using libev, we should probably use timeout watchers... */ + if (context->last_timeout_check_s != now) { + context->last_timeout_check_s = now; + + lws_plat_service_periodic(context); + + /* global timeout check once per second */ + + if (pollfd) + our_fd = pollfd->fd; + + for (n = 0; n < context->fds_count; n++) { + m = context->fds[n].fd; + wsi = context->lws_lookup[m]; + if (!wsi) + continue; + + if (libwebsocket_service_timeout_check(context, wsi, now)) + /* he did time out... */ + if (m == our_fd) { + /* it was the guy we came to service! */ + timed_out = 1; + /* mark as handled */ + pollfd->revents = 0; + } + } + } + + /* the socket we came to service timed out, nothing to do */ + if (timed_out) + return 0; + + /* just here for timeout management? */ + if (pollfd == NULL) + return 0; + + /* no, here to service a socket descriptor */ + wsi = context->lws_lookup[pollfd->fd]; + if (wsi == NULL) + /* not lws connection ... leave revents alone and return */ + return 0; + + /* + * so that caller can tell we handled, past here we need to + * zero down pollfd->revents after handling + */ + + /* + * deal with listen service piggybacking + * every listen_service_modulo services of other fds, we + * sneak one in to service the listen socket if there's anything waiting + * + * To handle connection storms, as found in ab, if we previously saw a + * pending connection here, it causes us to check again next time. + */ + + if (context->listen_service_fd && pollfd != + &context->fds[listen_socket_fds_index]) { + context->listen_service_count++; + if (context->listen_service_extraseen || + context->listen_service_count == + context->listen_service_modulo) { + context->listen_service_count = 0; + m = 1; + if (context->listen_service_extraseen > 5) + m = 2; + while (m--) { + /* + * even with extpoll, we prepared this + * internal fds for listen + */ + n = lws_poll_listen_fd(&context->fds[listen_socket_fds_index]); + if (n > 0) { /* there's a conn waiting for us */ + libwebsocket_service_fd(context, + &context-> + fds[listen_socket_fds_index]); + context->listen_service_extraseen++; + } else { + if (context->listen_service_extraseen) + context-> + listen_service_extraseen--; + break; + } + } + } + + } + + /* handle session socket closed */ + + if ((!(pollfd->revents & LWS_POLLIN)) && + (pollfd->revents & LWS_POLLHUP)) { + + lwsl_debug("Session Socket %p (fd=%d) dead\n", + (void *)wsi, pollfd->fd); + + goto close_and_handled; + } + + /* okay, what we came here to do... */ + + switch (wsi->mode) { + case LWS_CONNMODE_HTTP_SERVING: + case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: + case LWS_CONNMODE_SERVER_LISTENER: + case LWS_CONNMODE_SSL_ACK_PENDING: + n = lws_server_socket_service(context, wsi, pollfd); + if (n < 0) + goto close_and_handled; + goto handled; + + case LWS_CONNMODE_WS_SERVING: + case LWS_CONNMODE_WS_CLIENT: + + /* the guy requested a callback when it was OK to write */ + + if ((pollfd->revents & LWS_POLLOUT) && + (wsi->state == WSI_STATE_ESTABLISHED || + wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) && + lws_handle_POLLOUT_event(context, wsi, pollfd)) { + lwsl_info("libwebsocket_service_fd: closing\n"); + goto close_and_handled; + } + + if (wsi->u.ws.rxflow_buffer && + (wsi->u.ws.rxflow_change_to & LWS_RXFLOW_ALLOW)) { + lwsl_info("draining rxflow\n"); + /* well, drain it */ + eff_buf.token = (char *)wsi->u.ws.rxflow_buffer + + wsi->u.ws.rxflow_pos; + eff_buf.token_len = wsi->u.ws.rxflow_len - + wsi->u.ws.rxflow_pos; + draining_flow = 1; + goto drain; + } + + /* any incoming data ready? */ + + if (!(pollfd->revents & LWS_POLLIN)) + break; + +read_pending: + eff_buf.token_len = lws_ssl_capable_read(wsi, + context->service_buffer, + sizeof(context->service_buffer)); + switch (eff_buf.token_len) { + case 0: + lwsl_info("service_fd: closing due to 0 length read\n"); + goto close_and_handled; + case LWS_SSL_CAPABLE_MORE_SERVICE: + lwsl_info("SSL Capable more service\n"); + n = 0; + goto handled; + case LWS_SSL_CAPABLE_ERROR: + lwsl_info("Closing when error\n"); + goto close_and_handled; + } + + /* + * give any active extensions a chance to munge the buffer + * before parse. We pass in a pointer to an lws_tokens struct + * prepared with the default buffer and content length that's in + * there. Rather than rewrite the default buffer, extensions + * that expect to grow the buffer can adapt .token to + * point to their own per-connection buffer in the extension + * user allocation. By default with no extensions or no + * extension callback handling, just the normal input buffer is + * used then so it is efficient. + */ + + eff_buf.token = (char *)context->service_buffer; +drain: + + do { + + more = 0; + + m = lws_ext_callback_for_each_active(wsi, + LWS_EXT_CALLBACK_PACKET_RX_PREPARSE, &eff_buf, 0); + if (m < 0) + goto close_and_handled; + if (m) + more = 1; + + /* service incoming data */ + + if (eff_buf.token_len) { + n = libwebsocket_read(context, wsi, + (unsigned char *)eff_buf.token, + eff_buf.token_len); + if (n < 0) { + /* we closed wsi */ + n = 0; + goto handled; + } + } + + eff_buf.token = NULL; + eff_buf.token_len = 0; + } while (more); + + if (draining_flow && wsi->u.ws.rxflow_buffer && + wsi->u.ws.rxflow_pos == wsi->u.ws.rxflow_len) { + lwsl_info("flow buffer: drained\n"); + free(wsi->u.ws.rxflow_buffer); + wsi->u.ws.rxflow_buffer = NULL; + /* having drained the rxflow buffer, can rearm POLLIN */ + n = _libwebsocket_rx_flow_control(wsi); /* n ignored, needed for NO_SERVER case */ + } + + if (lws_ssl_pending(wsi)) + goto read_pending; + break; + + default: +#ifdef LWS_NO_CLIENT + break; +#else + n = lws_client_socket_service(context, wsi, pollfd); + goto handled; +#endif + } + + n = 0; + goto handled; + +close_and_handled: + lwsl_debug("Close and handled\n"); + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); + n = 1; + +handled: + pollfd->revents = 0; + return n; +} + +/** + * libwebsocket_service() - Service any pending websocket activity + * @context: Websocket context + * @timeout_ms: Timeout for poll; 0 means return immediately if nothing needed + * service otherwise block and service immediately, returning + * after the timeout if nothing needed service. + * + * This function deals with any pending websocket traffic, for three + * kinds of event. It handles these events on both server and client + * types of connection the same. + * + * 1) Accept new connections to our context's server + * + * 2) Call the receive callback for incoming frame data received by + * server or client connections. + * + * You need to call this service function periodically to all the above + * functions to happen; if your application is single-threaded you can + * just call it in your main event loop. + * + * Alternatively you can fork a new process that asynchronously handles + * calling this service in a loop. In that case you are happy if this + * call blocks your thread until it needs to take care of something and + * would call it with a large nonzero timeout. Your loop then takes no + * CPU while there is nothing happening. + * + * If you are calling it in a single-threaded app, you don't want it to + * wait around blocking other things in your loop from happening, so you + * would call it with a timeout_ms of 0, so it returns immediately if + * nothing is pending, or as soon as it services whatever was pending. + */ + +LWS_VISIBLE int +libwebsocket_service(struct libwebsocket_context *context, int timeout_ms) +{ + return lws_plat_service(context, timeout_ms); +} + diff --git a/lib/sha-1.c b/lib/sha-1.c index b1cb96fe46..b77ea4ddb5 100644 --- a/lib/sha-1.c +++ b/lib/sha-1.c @@ -32,40 +32,12 @@ * implemented by Jun-ichiro itojun Itoh */ -#include -#ifdef WIN32 - -#ifndef BIG_ENDIAN -#define BIG_ENDIAN 4321 /* to show byte order (taken from gcc) */ -#endif -#ifndef LITTLE_ENDIAN -#define LITTLE_ENDIAN 1234 -#endif -#ifndef BYTE_ORDER -#define BYTE_ORDER LITTLE_ENDIAN -#endif - -typedef unsigned __int64 u_int64_t; - -#undef __P -#ifndef __P -#if __STDC__ -#define __P(protos) protos -#else -#define __P(protos) () -#endif -#endif - -#define bzero(b, len) (memset((b), '\0', (len)), (void) 0) +#include "private-libwebsockets.h" -#else -#include -#include -#include +#ifdef HAVE_SYS_TYPES_H +#include #endif -#include - struct sha1_ctxt { union { unsigned char b8[20]; @@ -83,7 +55,9 @@ struct sha1_ctxt { }; /* sanity check */ -#if BYTE_ORDER != BIG_ENDIAN +#if !defined(BYTE_ORDER) || !defined(LITTLE_ENDIAN) || !defined(BIG_ENDIAN) +# define unsupported 1 +#elif BYTE_ORDER != BIG_ENDIAN # if BYTE_ORDER != LITTLE_ENDIAN # define unsupported 1 # endif diff --git a/lib/ssl-http2.c b/lib/ssl-http2.c new file mode 100755 index 0000000000..237f43cddb --- /dev/null +++ b/lib/ssl-http2.c @@ -0,0 +1,78 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + * + * Some or all of this file is based on code from nghttp2, which has the + * following license. Since it's more liberal than lws license, you're also + * at liberty to get the original code from + * https://github.com/tatsuhiro-t/nghttp2 under his liberal terms alone. + * + * nghttp2 - HTTP/2.0 C Library + * + * Copyright (c) 2012 Tatsuhiro Tsujikawa + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "private-libwebsockets.h" + +#ifndef LWS_NO_SERVER + +#if OPENSSL_VERSION_NUMBER >= 0x10002000L +static int alpn_select_proto_cb(SSL* ssl, + const unsigned char **out, + unsigned char *outlen, + const unsigned char *in, unsigned int inlen, + void *arg) +{ + lwsl_err((char *)in); + return SSL_TLSEXT_ERR_OK; /* SSL_TLSEXT_ERR_NOACK */ +} +#endif + +LWS_VISIBLE void +lws_context_init_http2_ssl(struct libwebsocket_context *context) +{ +#if OPENSSL_VERSION_NUMBER >= 0x10002000L + // ALPN selection callback + SSL_CTX_set_alpn_select_cb(context->ssl_ctx, alpn_select_proto_cb, NULL); + lwsl_notice(" HTTP2 / ALPN enabled\n"); +#else + lwsl_notice(" HTTP2 / ALPN configured but not supported by OpenSSL version\n"); +#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L +} + +#endif \ No newline at end of file diff --git a/lib/ssl.c b/lib/ssl.c new file mode 100755 index 0000000000..940e00df9e --- /dev/null +++ b/lib/ssl.c @@ -0,0 +1,571 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2014 Andy Green + * + * 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 + */ + +#include "private-libwebsockets.h" + +int openssl_websocket_private_data_index; + +static int +OpenSSL_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + SSL *ssl; + int n; + struct libwebsocket_context *context; + + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + + /* + * !!! nasty openssl requires the index to come as a library-scope + * static + */ + context = SSL_get_ex_data(ssl, openssl_websocket_private_data_index); + + n = context->protocols[0].callback(NULL, NULL, + LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION, + x509_ctx, ssl, preverify_ok); + + /* convert return code from 0 = OK to 1 = OK */ + return !n; +} + +LWS_VISIBLE int +lws_context_init_server_ssl(struct lws_context_creation_info *info, + struct libwebsocket_context *context) +{ + SSL_METHOD *method; + int error; + int n; + +#ifndef LWS_NO_SERVER + if (info->port != CONTEXT_PORT_NO_LISTEN) { + + context->use_ssl = info->ssl_cert_filepath != NULL && + info->ssl_private_key_filepath != NULL; +#ifdef USE_CYASSL + lwsl_notice(" Compiled with CYASSL support\n"); +#else + lwsl_notice(" Compiled with OpenSSL support\n"); +#endif + + if (info->ssl_cipher_list) + lwsl_notice(" SSL ciphers: '%s'\n", info->ssl_cipher_list); + + if (context->use_ssl) + lwsl_notice(" Using SSL mode\n"); + else + lwsl_notice(" Using non-SSL mode\n"); + } + +#else + if (info->ssl_cert_filepath != NULL && + info->ssl_private_key_filepath != NULL) { + lwsl_notice(" Not compiled for OpenSSl support!\n"); + return 1; + } + lwsl_notice(" Compiled without SSL support\n"); + } +#endif /* no server */ + + /* basic openssl init */ + + SSL_library_init(); + + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + + openssl_websocket_private_data_index = + SSL_get_ex_new_index(0, "libwebsockets", NULL, NULL, NULL); + + /* + * Firefox insists on SSLv23 not SSLv3 + * Konq disables SSLv2 by default now, SSLv23 works + */ + + method = (SSL_METHOD *)SSLv23_server_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + context->ssl_ctx = SSL_CTX_new(method); /* create context */ + if (!context->ssl_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(context->ssl_ctx, SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(context->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(context->ssl_ctx, + info->ssl_cipher_list); + + /* as a server, are we requiring clients to identify themselves? */ + + if (info->options & + LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT) { + + /* absolutely require the client cert */ + + SSL_CTX_set_verify(context->ssl_ctx, + SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, + OpenSSL_verify_callback); + + /* + * give user code a chance to load certs into the server + * allowing it to verify incoming client certs + */ + + context->protocols[0].callback(context, NULL, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS, + context->ssl_ctx, NULL, 0); + } + + if (info->options & LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT) { + /* Normally SSL listener rejects non-ssl, optionally allow */ + context->allow_non_ssl_on_ssl_port = 1; + } + + if (context->use_ssl) { + + /* openssl init for server sockets */ + + /* set the local certificate from CertFile */ + n = SSL_CTX_use_certificate_chain_file(context->ssl_ctx, + info->ssl_cert_filepath); + if (n != 1) { + error = ERR_get_error(); + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + error, + ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(context->ssl_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + error = ERR_get_error(); + lwsl_err("ssl problem getting key '%s' %lu: %s\n", + info->ssl_private_key_filepath, + error, + ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + /* verify private key */ + if (!SSL_CTX_check_private_key(context->ssl_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + + /* + * SSL is happy and has a cert it's content with + * If we're supporting HTTP2, initialize that + */ + + lws_context_init_http2_ssl(context); + } + + return 0; +} + +LWS_VISIBLE void +lws_ssl_destroy(struct libwebsocket_context *context) +{ + if (context->ssl_ctx) + SSL_CTX_free(context->ssl_ctx); + if (context->ssl_client_ctx) + SSL_CTX_free(context->ssl_client_ctx); + + ERR_remove_state(0); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} + +LWS_VISIBLE void +libwebsockets_decode_ssl_error(void) +{ + char buf[256]; + u_long err; + + while ((err = ERR_get_error()) != 0) { + ERR_error_string_n(err, buf, sizeof(buf)); + lwsl_err("*** %lu %s\n", err, buf); + } +} + +#ifndef LWS_NO_CLIENT +int lws_context_init_client_ssl(struct lws_context_creation_info *info, + struct libwebsocket_context *context) +{ + int error; + int n; + SSL_METHOD *method; + + if (info->port != CONTEXT_PORT_NO_LISTEN) + return 0; + + method = (SSL_METHOD *)SSLv23_client_method(); + if (!method) { + error = ERR_get_error(); + lwsl_err("problem creating ssl method %lu: %s\n", + error, ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + /* create context */ + context->ssl_client_ctx = SSL_CTX_new(method); + if (!context->ssl_client_ctx) { + error = ERR_get_error(); + lwsl_err("problem creating ssl context %lu: %s\n", + error, ERR_error_string(error, + (char *)context->service_buffer)); + return 1; + } + +#ifdef SSL_OP_NO_COMPRESSION + SSL_CTX_set_options(context->ssl_client_ctx, + SSL_OP_NO_COMPRESSION); +#endif + SSL_CTX_set_options(context->ssl_client_ctx, + SSL_OP_CIPHER_SERVER_PREFERENCE); + if (info->ssl_cipher_list) + SSL_CTX_set_cipher_list(context->ssl_client_ctx, + info->ssl_cipher_list); + +#ifdef LWS_SSL_CLIENT_USE_OS_CA_CERTS + if (!(info->options & LWS_SERVER_OPTION_DISABLE_OS_CA_CERTS)) + /* loads OS default CA certs */ + SSL_CTX_set_default_verify_paths(context->ssl_client_ctx); +#endif + + /* openssl init for cert verification (for client sockets) */ + if (!info->ssl_ca_filepath) { + if (!SSL_CTX_load_verify_locations( + context->ssl_client_ctx, NULL, + LWS_OPENSSL_CLIENT_CERTS)) + lwsl_err( + "Unable to load SSL Client certs from %s " + "(set by --with-client-cert-dir= " + "in configure) -- client ssl isn't " + "going to work", LWS_OPENSSL_CLIENT_CERTS); + } else + if (!SSL_CTX_load_verify_locations( + context->ssl_client_ctx, info->ssl_ca_filepath, + NULL)) + lwsl_err( + "Unable to load SSL Client certs " + "file from %s -- client ssl isn't " + "going to work", info->ssl_ca_filepath); + + /* + * callback allowing user code to load extra verification certs + * helping the client to verify server identity + */ + + /* support for client-side certificate authentication */ + if (info->ssl_cert_filepath) { + n = SSL_CTX_use_certificate_chain_file( + context->ssl_client_ctx, + info->ssl_cert_filepath); + if (n != 1) { + lwsl_err("problem getting cert '%s' %lu: %s\n", + info->ssl_cert_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)context->service_buffer)); + return 1; + } + } + if (info->ssl_private_key_filepath) { + /* set the private key from KeyFile */ + if (SSL_CTX_use_PrivateKey_file(context->ssl_client_ctx, + info->ssl_private_key_filepath, + SSL_FILETYPE_PEM) != 1) { + lwsl_err("use_PrivateKey_file '%s' %lu: %s\n", + info->ssl_private_key_filepath, + ERR_get_error(), + ERR_error_string(ERR_get_error(), + (char *)context->service_buffer)); + return 1; + } + + /* verify private key */ + if (!SSL_CTX_check_private_key( + context->ssl_client_ctx)) { + lwsl_err("Private SSL key doesn't match cert\n"); + return 1; + } + } + + context->protocols[0].callback(context, NULL, + LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS, + context->ssl_client_ctx, NULL, 0); + + return 0; +} +#endif + +LWS_VISIBLE int +lws_ssl_capable_read(struct libwebsocket *wsi, unsigned char *buf, int len) +{ + int n; + + if (!wsi->ssl) + return lws_ssl_capable_read_no_ssl(wsi, buf, len); + + n = SSL_read(wsi->ssl, buf, len); + if (n >= 0) + return n; + + n = SSL_get_error(wsi->ssl, n); + if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) + return LWS_SSL_CAPABLE_MORE_SERVICE; + + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_capable_write(struct libwebsocket *wsi, unsigned char *buf, int len) +{ + int n; + + if (!wsi->ssl) + return lws_ssl_capable_write_no_ssl(wsi, buf, len); + + n = SSL_write(wsi->ssl, buf, len); + if (n >= 0) + return n; + + n = SSL_get_error(wsi->ssl, n); + if (n == SSL_ERROR_WANT_READ || n == SSL_ERROR_WANT_WRITE) { + if (n == SSL_ERROR_WANT_WRITE) + lws_set_blocking_send(wsi); + return LWS_SSL_CAPABLE_MORE_SERVICE; + } + + return LWS_SSL_CAPABLE_ERROR; +} + +LWS_VISIBLE int +lws_ssl_pending(struct libwebsocket *wsi) +{ + if (wsi->ssl) + return SSL_pending(wsi->ssl); + return 0; +} + +LWS_VISIBLE int +lws_ssl_close(struct libwebsocket *wsi) +{ + int n; + + if (!wsi->ssl) + return 0; /* not handled */ + + n = SSL_get_fd(wsi->ssl); + SSL_shutdown(wsi->ssl); + compatible_close(n); + SSL_free(wsi->ssl); + + return 1; /* handled */ +} + +LWS_VISIBLE int +lws_server_socket_service_ssl(struct libwebsocket_context *context, + struct libwebsocket **pwsi, struct libwebsocket *new_wsi, + int accept_fd, struct libwebsocket_pollfd *pollfd) +{ + int n, m; + struct libwebsocket *wsi = *pwsi; +#ifndef USE_CYASSL + BIO *bio; +#endif + + if (!LWS_SSL_ENABLED(context)) + return 0; + + switch (wsi->mode) { + case LWS_CONNMODE_SERVER_LISTENER: + + new_wsi->ssl = SSL_new(context->ssl_ctx); + if (new_wsi->ssl == NULL) { + lwsl_err("SSL_new failed: %s\n", + ERR_error_string(SSL_get_error( + new_wsi->ssl, 0), NULL)); + libwebsockets_decode_ssl_error(); + free(new_wsi); + compatible_close(accept_fd); + break; + } + + SSL_set_ex_data(new_wsi->ssl, + openssl_websocket_private_data_index, context); + + SSL_set_fd(new_wsi->ssl, accept_fd); + +#ifdef USE_CYASSL + CyaSSL_set_using_nonblock(new_wsi->ssl, 1); +#else + SSL_set_mode(new_wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + bio = SSL_get_rbio(new_wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); + bio = SSL_get_wbio(new_wsi->ssl); + if (bio) + BIO_set_nbio(bio, 1); /* nonblocking */ + else + lwsl_notice("NULL rbio\n"); +#endif + + /* + * we are not accepted yet, but we need to enter ourselves + * as a live connection. That way we can retry when more + * pieces come if we're not sorted yet + */ + + *pwsi = new_wsi; + wsi = *pwsi; + wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; + insert_wsi_socket_into_fds(context, wsi); + + libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, + AWAITING_TIMEOUT); + + lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); + + /* fallthru */ + + case LWS_CONNMODE_SSL_ACK_PENDING: + + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) + goto fail; + + lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); + + lws_latency_pre(context, wsi); + + n = recv(wsi->sock, context->service_buffer, + sizeof(context->service_buffer), MSG_PEEK); + + /* + * optionally allow non-SSL connect on SSL listening socket + * This is disabled by default, if enabled it goes around any + * SSL-level access control (eg, client-side certs) so leave + * it disabled unless you know it's not a problem for you + */ + + if (context->allow_non_ssl_on_ssl_port && n >= 1 && + context->service_buffer[0] >= ' ') { + /* + * TLS content-type for Handshake is 0x16 + * TLS content-type for ChangeCipherSpec Record is 0x14 + * + * A non-ssl session will start with the HTTP method in + * ASCII. If we see it's not a legit SSL handshake + * kill the SSL for this connection and try to handle + * as a HTTP connection upgrade directly. + */ + wsi->use_ssl = 0; + SSL_shutdown(wsi->ssl); + SSL_free(wsi->ssl); + wsi->ssl = NULL; + goto accepted; + } + + /* normal SSL connection processing path */ + + n = SSL_accept(wsi->ssl); + lws_latency(context, wsi, + "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); + + if (n == 1) + goto accepted; + + m = SSL_get_error(wsi->ssl, n); + lwsl_debug("SSL_accept failed %d / %s\n", + m, ERR_error_string(m, NULL)); + + if (m == SSL_ERROR_WANT_READ) { + if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) + goto fail; + + lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_READ); + + lwsl_info("SSL_ERROR_WANT_READ\n"); + break; + } + if (m == SSL_ERROR_WANT_WRITE) { + if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) + goto fail; + + lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE); + break; + } + lwsl_debug("SSL_accept failed skt %u: %s\n", + pollfd->fd, ERR_error_string(m, NULL)); + libwebsocket_close_and_free_session(context, wsi, + LWS_CLOSE_STATUS_NOSTATUS); + break; + +accepted: + /* OK, we are accepted... give him some time to negotiate */ + libwebsocket_set_timeout(wsi, + PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + AWAITING_TIMEOUT); + + wsi->mode = LWS_CONNMODE_HTTP_SERVING; + + lwsl_debug("accepted new SSL conn\n"); + break; + } + + return 0; + +fail: + return 1; +} + +LWS_VISIBLE void +lws_ssl_context_destroy(struct libwebsocket_context *context) +{ + if (context->ssl_ctx) + SSL_CTX_free(context->ssl_ctx); + if (context->ssl_client_ctx) + SSL_CTX_free(context->ssl_client_ctx); + + ERR_remove_state(0); + ERR_free_strings(); + EVP_cleanup(); + CRYPTO_cleanup_all_ex_data(); +} diff --git a/test-server/attack.sh b/test-server/attack.sh index dae2c2aafe..0186f4867e 100755 --- a/test-server/attack.sh +++ b/test-server/attack.sh @@ -16,6 +16,29 @@ function check { exit 1 fi dd if=$LOG bs=1 skip=$LEN 2>/dev/null + + if [ "$1" = "default" ] ; then + diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null + if [ $? -ne 0 ] ; then + echo "FAIL: got something other than test.html back" + exit 1 + fi + fi + + if [ "$1" = "forbidden" ] ; then + if [ -z "`grep '

403 Forbidden

' /tmp/lwscap`" ] ; then + echo "FAIL: should have told forbidden (test server has no dirs)" + exit 1 + fi + fi + + if [ "$1" == "args" ] ; then + a="`dd if=$LOG bs=1 skip=$LEN 2>/dev/null |grep Uri.Args\: | tr -s ' ' | cut -d' ' -f4-`" + if [ "$a" != "$2" ] ; then + echo "Args '$a' not $2" + exit 1 + fi + fi LEN=`stat $LOG -c %s` } @@ -30,6 +53,19 @@ while [ -z "`grep Listening $LOG`" ] ; do done check +echo +echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)" +rm -f /tmp/lwscap +echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check args "arg=1" + +echo +echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)" +rm -f /tmp/lwscap +echo -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check args "arg=/../." + + echo echo "---- spam enough crap to not be GET" echo "not GET" | nc $SERVER $PORT @@ -107,7 +143,7 @@ check echo echo "---- good request but http payload coming too (should be ignored and test.html served)" -echo -e "GET blah HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \ +echo -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \ "......................................................................................................................." \ "......................................................................................................................." \ "......................................................................................................................." \ @@ -124,25 +160,58 @@ echo -e "GET blah HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD....................... "......................................................................................................................." \ "......................................................................................................................." \ | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check -diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null -if [ $? -ne 0 ] ; then - echo "FAIL: got something other than test.html back" - exit 1 -fi +check default echo -echo "---- directory attack" +echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)" rm -f /tmp/lwscap -echo -e "GET ../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check -diff /tmp/lwscap /usr/share/libwebsockets-test-server/test.html > /dev/null -if [ $? -ne 0 ] ; then - echo "FAIL: got something other than test.html back" - exit 1 -fi +echo -e "GET /../../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check forbidden + +echo +echo "---- directory attack 2 (/../ should be /)" +rm -f /tmp/lwscap +echo -e "GET /../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check default + +echo +echo "---- directory attack 3 (/./ should be /)" +rm -f /tmp/lwscap +echo -e "GET /./ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check default + +echo +echo "---- directory attack 4 (/blah/.. should be /)" +rm -f /tmp/lwscap +echo -e "GET /blah/.. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check default + +echo +echo "---- directory attack 5 (/blah/../ should be /)" +rm -f /tmp/lwscap +echo -e "GET /blah/../ HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check default + +echo +echo "---- directory attack 6 (/blah/../. should be /)" +rm -f /tmp/lwscap +echo -e "GET /blah/../. HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check default + +echo +echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)" +rm -f /tmp/lwscap +echo -e "GET /%2e%2e%2f../../../etc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check forbidden + +echo +echo "---- directory attack 7 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)" +rm -f /tmp/lwscap +echo -e "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.1\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +check forbidden + echo -echo "--- survived" +echo "--- survived OK ---" kill -2 $CPID diff --git a/test-server/test-client.c b/test-server/test-client.c index 7b32cb1f67..3f7c8b8302 100644 --- a/test-server/test-client.c +++ b/test-server/test-client.c @@ -21,11 +21,16 @@ #include #include -#include #include #include #include +#ifdef _WIN32 +#define random rand +#else +#include +#endif + #ifdef CMAKE_BUILD #include "lws_config.h" #endif @@ -38,7 +43,8 @@ static int deny_deflate; static int deny_mux; static struct libwebsocket *wsi_mirror; static int mirror_lifetime = 0; -static int force_exit = 0; +static volatile int force_exit = 0; +static int longlived = 0; /* * This demo shows how to connect multiple websockets simultaneously to a @@ -73,6 +79,15 @@ callback_dumb_increment(struct libwebsocket_context *this, { switch (reason) { + case LWS_CALLBACK_CLIENT_ESTABLISHED: + fprintf(stderr, "callback_dumb_increment: LWS_CALLBACK_CLIENT_ESTABLISHED\n"); + break; + + case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: + fprintf(stderr, "LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n"); + was_closed = 1; + break; + case LWS_CALLBACK_CLOSED: fprintf(stderr, "LWS_CALLBACK_CLOSED\n"); was_closed = 1; @@ -125,13 +140,25 @@ callback_lws_mirror(struct libwebsocket_context *context, switch (reason) { - case LWS_CALLBACK_CLOSED: - fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime); - wsi_mirror = NULL; - break; - case LWS_CALLBACK_CLIENT_ESTABLISHED: + fprintf(stderr, "callback_lws_mirror: LWS_CALLBACK_CLIENT_ESTABLISHED\n"); + + mirror_lifetime = 10 + (random() & 1023); + /* useful to test single connection stability */ + if (longlived) + mirror_lifetime += 50000; + + fprintf(stderr, "opened mirror connection with " + "%d lifetime\n", mirror_lifetime); + + /* + * mirror_lifetime is decremented each send, when it reaches + * zero the connection is closed in the send callback. + * When the close callback comes, wsi_mirror is set to NULL + * so a new connection will be opened + */ + /* * start the ball rolling, * LWS_CALLBACK_CLIENT_WRITEABLE will come next service @@ -140,6 +167,11 @@ callback_lws_mirror(struct libwebsocket_context *context, libwebsocket_callback_on_writable(context, wsi); break; + case LWS_CALLBACK_CLOSED: + fprintf(stderr, "mirror: LWS_CALLBACK_CLOSED mirror_lifetime=%d\n", mirror_lifetime); + wsi_mirror = NULL; + break; + case LWS_CALLBACK_CLIENT_RECEIVE: /* fprintf(stderr, "rx %d '%s'\n", (int)len, (char *)in); */ break; @@ -185,13 +217,13 @@ callback_lws_mirror(struct libwebsocket_context *context, static struct libwebsocket_protocols protocols[] = { { - "dumb-increment-protocol", + "dumb-increment-protocol,fake-nonexistant-protocol", callback_dumb_increment, 0, 20, }, { - "lws-mirror-protocol", + "fake-nonexistant-protocol,lws-mirror-protocol", callback_lws_mirror, 0, 128, @@ -227,7 +259,6 @@ int main(int argc, char **argv) const char *address; struct libwebsocket *wsi_dumb; int ietf_version = -1; /* latest */ - int longlived = 0; struct lws_context_creation_info info; memset(&info, 0, sizeof info); @@ -306,16 +337,18 @@ int main(int argc, char **argv) protocols[PROTOCOL_DUMB_INCREMENT].name, ietf_version); if (wsi_dumb == NULL) { - fprintf(stderr, "libwebsocket dumb connect failed\n"); + fprintf(stderr, "libwebsocket connect failed\n"); ret = 1; goto bail; } - fprintf(stderr, "Websocket connections opened\n"); + fprintf(stderr, "Waiting for connect...\n"); /* * sit there servicing the websocket context to handle incoming * packets, and drawing random circles on the mirror protocol websocket + * nothing happens until the client websocket connection is + * asynchronously established */ n = 0; @@ -337,25 +370,10 @@ int main(int argc, char **argv) if (wsi_mirror == NULL) { fprintf(stderr, "libwebsocket " - "dumb connect failed\n"); + "mirror connect failed\n"); ret = 1; goto bail; } - - mirror_lifetime = 10 + (random() & 1023); - /* useful to test single connection stability */ - if (longlived) - mirror_lifetime += 50000; - - fprintf(stderr, "opened mirror connection with " - "%d lifetime\n", mirror_lifetime); - - /* - * mirror_lifetime is decremented each send, when it reaches - * zero the connection is closed in the send callback. - * When the close callback comes, wsi_mirror is set to NULL - * so a new connection will be opened - */ } bail: diff --git a/test-server/test-echo.c b/test-server/test-echo.c index 5692182b71..3d48349cbc 100644 --- a/test-server/test-echo.c +++ b/test-server/test-echo.c @@ -24,16 +24,16 @@ #include #include -#include #include #include -#include #include -#ifdef WIN32 -#else +#include + +#ifndef _WIN32 #include +#include +#include #endif -#include #ifdef CMAKE_BUILD #include "lws_config.h" @@ -41,7 +41,7 @@ #include "../lib/libwebsockets.h" -int force_exit = 0; +static volatile int force_exit = 0; #define MAX_ECHO_PAYLOAD 1400 #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" @@ -250,17 +250,20 @@ int main(int argc, char **argv) } } -#ifndef LWS_NO_DAEMONIZE +#ifndef LWS_NO_DAEMONIZE /* * normally lock path would be /var/lock/lwsts or similar, to * simplify getting started without having to take care about * permissions or running as root, set to /tmp/.lwsts-lock */ +#if defined(WIN32) || defined(_WIN32) +#else if (!client && daemonize && lws_daemonize("/tmp/.lwstecho-lock")) { fprintf(stderr, "Failed to daemonize\n"); return 1; } #endif +#endif #ifdef WIN32 #else diff --git a/test-server/test-fraggle.c b/test-server/test-fraggle.c index 01b0819aeb..f2ea1e01a3 100644 --- a/test-server/test-fraggle.c +++ b/test-server/test-fraggle.c @@ -21,15 +21,8 @@ #include #include -#include #include #include -#include - -#ifdef CMAKE_BUILD -#include "lws_config.h" -#endif - #include "../lib/libwebsockets.h" #define LOCAL_RESOURCE_PATH INSTALL_DATADIR"/libwebsockets-test-server" diff --git a/test-server/test-ping.c b/test-server/test-ping.c index b209d059f8..fb7bff5b9d 100644 --- a/test-server/test-ping.c +++ b/test-server/test-ping.c @@ -21,26 +21,24 @@ #include #include -#include #include #include #include -#include - -#include #include -#ifndef WIN32 + +#ifndef _WIN32 +#include #include +#include #include #include +#include #endif #ifdef CMAKE_BUILD #include "lws_config.h" #endif -#include - #include "../lib/libwebsockets.h" /* @@ -338,7 +336,7 @@ int main(int argc, char **argv) char ip[30]; #ifndef WIN32 struct sigaction sa; - //struct winsize w; + struct winsize w; #endif struct timeval tv; unsigned long oldus = 0; @@ -420,10 +418,10 @@ int main(int argc, char **argv) } #ifndef WIN32 - //if (isatty(STDOUT_FILENO)) - // if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) - // if (w.ws_col > 0) - // screen_width = w.ws_col; + if (isatty(STDOUT_FILENO)) + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) != -1) + if (w.ws_col > 0) + screen_width = w.ws_col; #endif info.port = CONTEXT_PORT_NO_LISTEN; diff --git a/test-server/test-server.c b/test-server/test-server.c index b4352f78db..682bb5d32f 100644 --- a/test-server/test-server.c +++ b/test-server/test-server.c @@ -24,32 +24,24 @@ #include #include -#include #include +#include #include -#include #include #include #include -#ifdef WIN32 +#ifdef _WIN32 +#include #ifdef EXTERNAL_POLL - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #endif - #include - #include - #include - - #include "websock-w32.h" +#define poll WSAPoll #endif - -#else // NOT WIN32 +#else #include +#include +#include #endif -#include - #include "../lib/libwebsockets.h" static int close_testing; @@ -58,7 +50,8 @@ int max_poll_elements; struct pollfd *pollfds; int *fd_lookup; int count_pollfds; -int force_exit = 0; +static volatile int force_exit = 0; +static struct libwebsocket_context *context; /* * This demo server shows how to use libwebsockets for one or more @@ -99,18 +92,94 @@ struct serveable { const char *mimetype; }; -static const struct serveable whitelist[] = { - { "/favicon.ico", "image/x-icon" }, - { "/libwebsockets.org-logo.png", "image/png" }, - - /* last one is the default served if no match */ - { "/test.html", "text/html" }, -}; - struct per_session_data__http { int fd; }; +/* + * this is just an example of parsing handshake headers, you don't need this + * in your code unless you will filter allowing connections by the header + * content + */ + +static void +dump_handshake_info(struct libwebsocket *wsi) +{ + int n; + static const char *token_names[] = { + /*[WSI_TOKEN_GET_URI] =*/ "GET URI", + /*[WSI_TOKEN_POST_URI] =*/ "POST URI", + /*[WSI_TOKEN_HOST] =*/ "Host", + /*[WSI_TOKEN_CONNECTION] =*/ "Connection", + /*[WSI_TOKEN_KEY1] =*/ "key 1", + /*[WSI_TOKEN_KEY2] =*/ "key 2", + /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol", + /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade", + /*[WSI_TOKEN_ORIGIN] =*/ "Origin", + /*[WSI_TOKEN_DRAFT] =*/ "Draft", + /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge", + + /* new for 04 */ + /*[WSI_TOKEN_KEY] =*/ "Key", + /*[WSI_TOKEN_VERSION] =*/ "Version", + /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin", + + /* new for 05 */ + /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions", + + /* client receives these */ + /*[WSI_TOKEN_ACCEPT] =*/ "Accept", + /*[WSI_TOKEN_NONCE] =*/ "Nonce", + /*[WSI_TOKEN_HTTP] =*/ "Http", + + "Accept:", + "If-Modified-Since:", + "Accept-Encoding:", + "Accept-Language:", + "Pragma:", + "Cache-Control:", + "Authorization:", + "Cookie:", + "Content-Length:", + "Content-Type:", + "Date:", + "Range:", + "Referer:", + "Uri-Args:", + + /*[WSI_TOKEN_MUXURL] =*/ "MuxURL", + }; + char buf[256]; + + for (n = 0; n < sizeof(token_names) / sizeof(token_names[0]); n++) { + if (!lws_hdr_total_length(wsi, n)) + continue; + + lws_hdr_copy(wsi, buf, sizeof buf, n); + + fprintf(stderr, " %s = %s\n", token_names[n], buf); + } +} + +const char * get_mimetype(const char *file) +{ + int n = strlen(file); + + if (n < 5) + return NULL; + + if (!strcmp(&file[n - 4], ".ico")) + return "image/x-icon"; + + if (!strcmp(&file[n - 4], ".png")) + return "image/png"; + + if (!strcmp(&file[n - 5], ".html")) + return "text/html"; + + return NULL; +} + /* this protocol server (always the first one) just knows how to do HTTP */ static int callback_http(struct libwebsocket_context *context, @@ -124,19 +193,42 @@ static int callback_http(struct libwebsocket_context *context, #endif char buf[256]; char leaf_path[1024]; + char b64[64]; + struct timeval tv; int n, m; unsigned char *p; + char *other_headers; static unsigned char buffer[4096]; struct stat stat_buf; struct per_session_data__http *pss = (struct per_session_data__http *)user; + const char *mimetype; #ifdef EXTERNAL_POLL - int fd = (int)(long)in; + struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in; #endif switch (reason) { case LWS_CALLBACK_HTTP: + dump_handshake_info(wsi); + + if (len < 1) { + libwebsockets_return_http_status(context, wsi, + HTTP_STATUS_BAD_REQUEST, NULL); + return -1; + } + + /* this server has no concept of directories */ + if (strchr((const char *)in + 1, '/')) { + libwebsockets_return_http_status(context, wsi, + HTTP_STATUS_FORBIDDEN, NULL); + return -1; + } + + /* if a legal POST URL, let it continue and accept data */ + if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) + return 0; + /* check for the "send a big file by hand" example case */ if (!strcmp((const char *)in, "/leaf.jpg")) { @@ -194,14 +286,44 @@ static int callback_http(struct libwebsocket_context *context, } /* if not, send a file the easy way */ + strcpy(buf, resource_path); + if (strcmp(in, "/")) { + if (*((const char *)in) != '/') + strcat(buf, "/"); + strncat(buf, in, sizeof(buf) - strlen(resource_path)); + } else /* default file to serve */ + strcat(buf, "/test.html"); + buf[sizeof(buf) - 1] = '\0'; + + /* refuse to serve files we don't understand */ + mimetype = get_mimetype(buf); + if (!mimetype) { + lwsl_err("Unknown mimetype for %s\n", buf); + libwebsockets_return_http_status(context, wsi, + HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); + return -1; + } - for (n = 0; n < (sizeof(whitelist) / sizeof(whitelist[0]) - 1); n++) - if (in && strcmp((const char *)in, whitelist[n].urlpath) == 0) - break; - - sprintf(buf, "%s%s", resource_path, whitelist[n].urlpath); + /* demostrates how to set a cookie on / */ + + other_headers = NULL; + if (!strcmp((const char *)in, "/") && + !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { + /* this isn't very unguessable but it'll do for us */ + gettimeofday(&tv, NULL); + sprintf(b64, "LWS_%u_%u_COOKIE", + (unsigned int)tv.tv_sec, + (unsigned int)tv.tv_usec); + + sprintf(leaf_path, + "Set-Cookie: test=LWS_%u_%u_COOKIE;Max-Age=360000\x0d\x0a", + (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec); + other_headers = leaf_path; + lwsl_err(other_headers); + } - if (libwebsockets_serve_http_file(context, wsi, buf, whitelist[n].mimetype)) + if (libwebsockets_serve_http_file(context, wsi, buf, + mimetype, other_headers)) return -1; /* through completion or error, close the socket */ /* @@ -212,6 +334,25 @@ static int callback_http(struct libwebsocket_context *context, break; + case LWS_CALLBACK_HTTP_BODY: + strncpy(buf, in, 20); + buf[20] = '\0'; + if (len < 20) + buf[len] = '\0'; + + lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", + (const char *)buf, (int)len); + + break; + + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + /* the whole of the sent body arried, close the connection */ + libwebsockets_return_http_status(context, wsi, + HTTP_STATUS_OK, NULL); + + return -1; + case LWS_CALLBACK_HTTP_FILE_COMPLETION: // lwsl_info("LWS_CALLBACK_HTTP_FILE_COMPLETION seen\n"); /* kill the connection after we sent one file */ @@ -229,7 +370,7 @@ static int callback_http(struct libwebsocket_context *context, goto bail; /* sent it all, close conn */ if (n == 0) - goto bail; + goto flush_bail; /* * because it's HTTP and not websocket, don't need to take * care about pre and postamble @@ -242,9 +383,19 @@ static int callback_http(struct libwebsocket_context *context, /* partial write, adjust */ lseek(pss->fd, m - n, SEEK_CUR); + if (m) /* while still active, extend timeout */ + libwebsocket_set_timeout(wsi, + PENDING_TIMEOUT_HTTP_CONTENT, 5); + } while (!lws_send_pipe_choked(wsi)); libwebsocket_callback_on_writable(context, wsi); break; +flush_bail: + /* true if still partial pending */ + if (lws_send_pipe_choked(wsi)) { + libwebsocket_callback_on_writable(context, wsi); + break; + } bail: close(pss->fd); @@ -275,6 +426,20 @@ static int callback_http(struct libwebsocket_context *context, * protocol 0 callback */ + case LWS_CALLBACK_LOCK_POLL: + /* + * lock mutex to protect pollfd state + * called before any other POLL related callback + */ + break; + + case LWS_CALLBACK_UNLOCK_POLL: + /* + * unlock mutex to protect pollfd state when + * called after any other POLL related callback + */ + break; + case LWS_CALLBACK_ADD_POLL_FD: if (count_pollfds >= max_poll_elements) { @@ -282,30 +447,39 @@ static int callback_http(struct libwebsocket_context *context, return 1; } - fd_lookup[fd] = count_pollfds; - pollfds[count_pollfds].fd = fd; - pollfds[count_pollfds].events = (int)(long)len; + fd_lookup[pa->fd] = count_pollfds; + pollfds[count_pollfds].fd = pa->fd; + pollfds[count_pollfds].events = pa->events; pollfds[count_pollfds++].revents = 0; break; case LWS_CALLBACK_DEL_POLL_FD: if (!--count_pollfds) break; - m = fd_lookup[fd]; + m = fd_lookup[pa->fd]; /* have the last guy take up the vacant slot */ pollfds[m] = pollfds[count_pollfds]; fd_lookup[pollfds[count_pollfds].fd] = m; break; - case LWS_CALLBACK_SET_MODE_POLL_FD: - pollfds[fd_lookup[fd]].events |= (int)(long)len; + case LWS_CALLBACK_CHANGE_MODE_POLL_FD: + pollfds[fd_lookup[pa->fd]].events = pa->events; break; - case LWS_CALLBACK_CLEAR_MODE_POLL_FD: - pollfds[fd_lookup[fd]].events &= ~(int)(long)len; - break; #endif + case LWS_CALLBACK_GET_THREAD_ID: + /* + * if you will call "libwebsocket_callback_on_writable" + * from a different thread, return the caller thread ID + * here so lws can use this information to work out if it + * should signal the poll() loop to exit and restart early + */ + + /* return pthread_getthreadid_np(); */ + + break; + default: break; } @@ -313,53 +487,6 @@ static int callback_http(struct libwebsocket_context *context, return 0; } -/* - * this is just an example of parsing handshake headers, you don't need this - * in your code unless you will filter allowing connections by the header - * content - */ - -static void -dump_handshake_info(struct libwebsocket *wsi) -{ - int n; - static const char *token_names[WSI_TOKEN_COUNT] = { - /*[WSI_TOKEN_GET_URI] =*/ "GET URI", - /*[WSI_TOKEN_HOST] =*/ "Host", - /*[WSI_TOKEN_CONNECTION] =*/ "Connection", - /*[WSI_TOKEN_KEY1] =*/ "key 1", - /*[WSI_TOKEN_KEY2] =*/ "key 2", - /*[WSI_TOKEN_PROTOCOL] =*/ "Protocol", - /*[WSI_TOKEN_UPGRADE] =*/ "Upgrade", - /*[WSI_TOKEN_ORIGIN] =*/ "Origin", - /*[WSI_TOKEN_DRAFT] =*/ "Draft", - /*[WSI_TOKEN_CHALLENGE] =*/ "Challenge", - - /* new for 04 */ - /*[WSI_TOKEN_KEY] =*/ "Key", - /*[WSI_TOKEN_VERSION] =*/ "Version", - /*[WSI_TOKEN_SWORIGIN] =*/ "Sworigin", - - /* new for 05 */ - /*[WSI_TOKEN_EXTENSIONS] =*/ "Extensions", - - /* client receives these */ - /*[WSI_TOKEN_ACCEPT] =*/ "Accept", - /*[WSI_TOKEN_NONCE] =*/ "Nonce", - /*[WSI_TOKEN_HTTP] =*/ "Http", - /*[WSI_TOKEN_MUXURL] =*/ "MuxURL", - }; - char buf[256]; - - for (n = 0; n < WSI_TOKEN_COUNT; n++) { - if (!lws_hdr_total_length(wsi, n)) - continue; - - lws_hdr_copy(wsi, buf, sizeof buf, n); - - fprintf(stderr, " %s = %s\n", token_names[n], buf); - } -} /* dumb_increment protocol */ @@ -485,7 +612,7 @@ callback_lws_mirror(struct libwebsocket_context *context, LWS_SEND_BUFFER_PRE_PADDING, ringbuffer[pss->ringbuffer_tail].len, LWS_WRITE_TEXT); - if (n < ringbuffer[pss->ringbuffer_tail].len) { + if (n < 0) { lwsl_err("ERROR %d writing to mirror socket\n", n); return -1; } @@ -513,7 +640,11 @@ callback_lws_mirror(struct libwebsocket_context *context, * for tests with chrome on same machine as client and * server, this is needed to stop chrome choking */ +#ifdef _WIN32 + Sleep(1); +#else usleep(1); +#endif } break; @@ -601,6 +732,7 @@ static struct libwebsocket_protocols protocols[] = { void sighandler(int sig) { force_exit = 1; + libwebsocket_cancel_service(context); } static struct option options[] = { @@ -608,9 +740,11 @@ static struct option options[] = { { "debug", required_argument, NULL, 'd' }, { "port", required_argument, NULL, 'p' }, { "ssl", no_argument, NULL, 's' }, + { "allow-non-ssl", no_argument, NULL, 'a' }, { "interface", required_argument, NULL, 'i' }, { "closetest", no_argument, NULL, 'c' }, -#ifndef LWS_NO_DAEMONIZE + { "libev", no_argument, NULL, 'e' }, + #ifndef LWS_NO_DAEMONIZE { "daemonize", no_argument, NULL, 'D' }, #endif { "resource_path", required_argument, NULL, 'r' }, @@ -623,14 +757,13 @@ int main(int argc, char **argv) char key_path[1024]; int n = 0; int use_ssl = 0; - struct libwebsocket_context *context; int opts = 0; char interface_name[128] = ""; const char *iface = NULL; #ifndef WIN32 int syslog_options = LOG_PID | LOG_PERROR; #endif - unsigned int oldus = 0; + unsigned int ms, oldms = 0; struct lws_context_creation_info info; int debug_level = 7; @@ -642,10 +775,13 @@ int main(int argc, char **argv) info.port = 7681; while (n >= 0) { - n = getopt_long(argc, argv, "ci:hsp:d:Dr:", options, NULL); + n = getopt_long(argc, argv, "eci:hsap:d:Dr:", options, NULL); if (n < 0) continue; switch (n) { + case 'e': + opts |= LWS_SERVER_OPTION_LIBEV; + break; #ifndef LWS_NO_DAEMONIZE case 'D': daemonize = 1; @@ -660,6 +796,9 @@ int main(int argc, char **argv) case 's': use_ssl = 1; break; + case 'a': + opts |= LWS_SERVER_OPTION_ALLOW_NON_SSL_ON_SSL_PORT; + break; case 'p': info.port = atoi(optarg); break; @@ -770,9 +909,10 @@ int main(int argc, char **argv) * as soon as it can take more packets (usually immediately) */ - if (((unsigned int)tv.tv_usec - oldus) > 50000) { + ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); + if ((ms - oldms) > 50) { libwebsocket_callback_on_writable_all_protocol(&protocols[PROTOCOL_DUMB_INCREMENT]); - oldus = tv.tv_usec; + oldms = ms; } #ifdef EXTERNAL_POLL diff --git a/test-server/test.html b/test-server/test.html index 919f46a25a..49558841d6 100644 --- a/test-server/test.html +++ b/test-server/test.html @@ -233,7 +233,9 @@ u = u.split('/'); - return pcol + u[0]; + /* + "/xxx" bit is for IE10 workaround */ + + return pcol + u[0] + "/xxx"; } diff --git a/win32port/win32helpers/gettimeofday.h b/win32port/win32helpers/gettimeofday.h index 223ceeed8b..2a4e4a2a27 100644 --- a/win32port/win32helpers/gettimeofday.h +++ b/win32port/win32helpers/gettimeofday.h @@ -15,14 +15,17 @@ #else #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL #endif - + +#ifndef _TIMEZONE_DEFINED struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ }; - + int gettimeofday(struct timeval *tv, struct timezone *tz); +#endif + #endif diff --git a/win32port/zlib/gzio.c b/win32port/zlib/gzio.c index 26f2da1e97..a57352e1e0 100644 --- a/win32port/zlib/gzio.c +++ b/win32port/zlib/gzio.c @@ -10,6 +10,7 @@ #include #include "zutil.h" +#include "gzguts.h" #ifdef NO_DEFLATE /* for compatiblity with old definition */ # define NO_GZCOMPRESS @@ -977,7 +978,7 @@ const char * ZEXPORT gzerror (file, errnum) *errnum = s->z_err; if (*errnum == Z_OK) return (const char*)""; - m = (char*)(*errnum == Z_ERRNO ? zstrerror(errno) : s->stream.msg); + m = (char*)(*errnum == Z_ERRNO ? zstrerror() : s->stream.msg); if (m == NULL || *m == '\0') m = (char*)ERR_MSG(s->z_err); From 39f651ee23b1a7954f2b75ab925f535d5cea495b Mon Sep 17 00:00:00 2001 From: u0u0 Date: Wed, 8 Apr 2015 15:11:31 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E8=AF=B4=E6=98=8E?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README b/README index 33a75e4490..e294a2dbbd 100644 --- a/README +++ b/README @@ -8,10 +8,4 @@ $./build_framework.py buildname Android: $export ANDROID_NDK=/absolute/path/to/the/android-ndk $cd libwebsockets/android -$./scripts/cmake_android_armeabi.sh - -If you Android NDK is x86_64, you may need put below code to android.toolchain.cmake - -set( ANDROID_NDK_HOST_X64 1 CACHE BOOL "Try to use 64-bit compiler toolchain" ) - -如何Android编译找不到NDK,可能需要手动打开上面的条件。 \ No newline at end of file +$./scripts/cmake_android_armeabi.sh \ No newline at end of file