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);